Compare commits
113 Commits
Author | SHA1 | Date | |
---|---|---|---|
89a166ea9b | |||
391827222e | |||
c1721bd1a1 | |||
f6630ae241 | |||
424cab64a8 | |||
05d2096fb8 | |||
7683af81bc | |||
bad10b3812 | |||
9f9522c2ed | |||
62879c3e52 | |||
45610f8dd7 | |||
18102cbd78 | |||
262d6a1529 | |||
1124ddaf90 | |||
981add3872 | |||
8d4b30140e | |||
b9c59137a8 | |||
0b1c7e06ca | |||
fcaf80cba6 | |||
6358b9eabb | |||
70a3fab1ff | |||
bdf86ace86 | |||
d57132d1e4 | |||
a6e87abf99 | |||
f1771610fe | |||
a88ea9780f | |||
bca0866d72 | |||
cebed93abf | |||
ee2b2e3569 | |||
cb8e9ee244 | |||
486184b16c | |||
9f9bd1988f | |||
ba1bfd1bc0 | |||
e12926e971 | |||
5b4db0de3b | |||
70ff55faa6 | |||
f2bd47d8bc | |||
e2b9c9e98e | |||
b0791a594f | |||
ea5eaa6ed2 | |||
ec3fd1d585 | |||
d76de22977 | |||
c0e3483cc7 | |||
0994cdf3b6 | |||
a76fd82262 | |||
2f919c7d69 | |||
f12592731b | |||
4a20e1a25d | |||
cc7111c3ff | |||
81972aed62 | |||
5e2092c6d4 | |||
d136f8ac91 | |||
5b23917940 | |||
2cda0a3798 | |||
f315ad1ce9 | |||
49f1c273c2 | |||
8518476c87 | |||
a34ed756db | |||
a48232c4e3 | |||
ac65e38390 | |||
eab3a753f5 | |||
9e5922e121 | |||
bf57b23776 | |||
8f4a20fd46 | |||
b3716da5ac | |||
2c129fd800 | |||
6fb18e7a25 | |||
38d28d6944 | |||
a187e1a7a2 | |||
7d7a19c7c2 | |||
6a2ae2153a | |||
f50ba1a91e | |||
b09575dbd6 | |||
c52ee3d91d | |||
1ced5c0425 | |||
e399a5c9b1 | |||
08dff5fb67 | |||
912760f2a7 | |||
4b62fdbf93 | |||
df593d43a7 | |||
89b1fba771 | |||
5505c91ae0 | |||
cc720ff399 | |||
29f07f6c56 | |||
a69e197a1f | |||
6dddd8eff8 | |||
51aada8922 | |||
b47bf40e8f | |||
48151bf365 | |||
659950996d | |||
6e656a4d0a | |||
bf308dd13d | |||
982f3b1943 | |||
7a4ea8cf1b | |||
7c0482a87c | |||
101ededd89 | |||
a8cbc0040e | |||
ed91bb445b | |||
f158a222f4 | |||
46bcad1fca | |||
5e147afb9f | |||
b061d4593f | |||
3fa520b4ef | |||
2df05b4afd | |||
e0569a6748 | |||
2e6ab398d9 | |||
94b03d2a6b | |||
f84fe30bb6 | |||
049ddef531 | |||
a1a214c742 | |||
c40a483ffa | |||
aff99acfae | |||
628129c08d |
25
.gitea/workflows/build-container.yml
Normal file
25
.gitea/workflows/build-container.yml
Normal file
@ -0,0 +1,25 @@
|
||||
name: build-docker-imge
|
||||
on:
|
||||
- push
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build the docker container
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: "Git clone"
|
||||
run: git clone ${{ gitea.server_url }}/${{ gitea.repository }}.git .
|
||||
- name: "Git checkout"
|
||||
run: git checkout "${{ gitea.sha }}"
|
||||
- uses: aevea/action-kaniko@master
|
||||
name: Run Kaniko to build our api docker container.
|
||||
with:
|
||||
image: kocoded/oqtane.framework
|
||||
tag: ${{ git.workflow_sha }}
|
||||
tag_with_latest: github.ref == 'refs/heads/master'
|
||||
registry: git.kocoder.xyz
|
||||
username: ${{ secrets.CI_RUNNER_USER }}
|
||||
password: ${{ secrets.CI_RUNNER_TOKEN }}
|
||||
build_file: Dockerfile
|
||||
target: deploy
|
||||
|
24
Dockerfile
Normal file
24
Dockerfile
Normal file
@ -0,0 +1,24 @@
|
||||
# Build
|
||||
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
|
||||
|
||||
WORKDIR /source
|
||||
|
||||
COPY --link . .
|
||||
|
||||
RUN dotnet restore /source/Oqtane.sln
|
||||
|
||||
RUN dotnet build "/source/Oqtane.sln" -c Release -o /source/build/
|
||||
|
||||
# Publish
|
||||
FROM build AS publish
|
||||
|
||||
RUN dotnet publish "Oqtane.Server/Oqtane.Server.csproj" -c Release -o /source/publish/
|
||||
|
||||
# Deploy
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS deploy
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
|
||||
COPY --from=publish /source/publish/ /app/
|
||||
ENTRYPOINT ["dotnet", "Oqtane.Server.dll"]
|
@ -52,6 +52,8 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||
services.AddScoped<IVisitorService, VisitorService>();
|
||||
services.AddScoped<ISyncService, SyncService>();
|
||||
services.AddScoped<ILocalizationCookieService, LocalizationCookieService>();
|
||||
services.AddScoped<ICookieConsentService, CookieConsentService>();
|
||||
services.AddScoped<IOutputCacheService, OutputCacheService>();
|
||||
|
||||
// providers
|
||||
services.AddScoped<ITextEditor, Oqtane.Modules.Controls.QuillJSTextEditor>();
|
||||
|
@ -55,10 +55,6 @@ else
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await GetJobs();
|
||||
if (_jobs.Count == 0)
|
||||
{
|
||||
AddModuleMessage(string.Format(Localizer["Message.NoJobs"], NavigateUrl("admin/system")), MessageType.Warning);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task GetJobs()
|
||||
|
@ -29,12 +29,12 @@ else
|
||||
{
|
||||
<div class="form-group">
|
||||
<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 class="form-group mt-2">
|
||||
<Label Class="control-label" For="password" HelpText="Please enter your Password" ResourceKey="Password">Password:</Label>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -63,24 +63,7 @@
|
||||
<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>
|
||||
<div class="col-sm-9">
|
||||
@if (!string.IsNullOrEmpty(_packagename))
|
||||
{
|
||||
<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 class="row mb-1 align-items-center">
|
||||
@ -244,7 +227,6 @@
|
||||
private string _moduledefinitionname = "";
|
||||
private string _version;
|
||||
private string _packagename = "";
|
||||
private string _packageurl = "";
|
||||
private string _owner = "";
|
||||
private string _url = "";
|
||||
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;
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="name" HelpText="Enter the page name" ResourceKey="Name">Name: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="name" class="form-control" @bind="@_name" required />
|
||||
<input id="name" class="form-control" @bind="@_name" maxlength="50" required />
|
||||
</div>
|
||||
</div>
|
||||
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Admin))
|
||||
@ -101,13 +101,13 @@
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="path" HelpText="Optionally enter a url path for this page (ie. home ). If you do not provide a url path, the page name will be used. If the page is intended to be the root path specify '/'." ResourceKey="UrlPath">Url Path: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="path" class="form-control" @bind="@_path" />
|
||||
<input id="path" class="form-control" @bind="@_path" maxlength="256" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="url" HelpText="Optionally enter a url which this page should redirect to when a user navigates to it" ResourceKey="Redirect">Redirect: </Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="url" class="form-control" @bind="@_url" />
|
||||
<input id="url" class="form-control" @bind="@_url" maxlength="500" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
@ -147,7 +147,7 @@
|
||||
<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>
|
||||
<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 class="row mb-1 align-items-center">
|
||||
@ -186,13 +186,13 @@
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="headcontent" HelpText="Optionally enter content to be included in the page head (ie. meta, link, or script tags)" ResourceKey="HeadContent">Head Content: </Label>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="headcontent" class="form-control" @bind="@_headcontent" rows="3"></textarea>
|
||||
<textarea id="headcontent" class="form-control" @bind="@_headcontent" rows="3" maxlength="4000"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="bodycontent" HelpText="Optionally enter content to be included in the page body (ie. script tags)" ResourceKey="BodyContent">Body Content: </Label>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="bodycontent" class="form-control" @bind="@_bodycontent" rows="3"></textarea>
|
||||
<textarea id="bodycontent" class="form-control" @bind="@_bodycontent" rows="3" maxlength="4000"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -205,13 +205,13 @@
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="headcontent" HelpText="Optionally enter content to be included in the page head (ie. meta, link, or script tags)" ResourceKey="HeadContent">Head Content: </Label>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="headcontent" class="form-control" @bind="@_headcontent" rows="3"></textarea>
|
||||
<textarea id="headcontent" class="form-control" @bind="@_headcontent" rows="3" maxlength="4000"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="bodycontent" HelpText="Optionally enter content to be included in the page body (ie. script tags)" ResourceKey="BodyContent">Body Content: </Label>
|
||||
<div class="col-sm-9">
|
||||
<textarea id="bodycontent" class="form-control" @bind="@_bodycontent" rows="3"></textarea>
|
||||
<textarea id="bodycontent" class="form-control" @bind="@_bodycontent" rows="3" maxlength="4000"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -56,6 +56,19 @@
|
||||
<input id="displayname" class="form-control" @bind="@_displayname" maxlength="50" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="subject" HelpText="Deine Studien- oder Ausbildungsrichtung" ResourceKey="Subject">Fachrichtung:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="subject" class="form-control" @bind="@_subject" maxlength="100" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<Label Class="col-sm-3" For="graduationYear" HelpText="Dein voraussichtlicher Abschlussjahrgang" ResourceKey="GraduationYear">Abschlussjahrgang:</Label>
|
||||
<div class="col-sm-9">
|
||||
<input id="graduationYear" type="number" class="form-control" @bind="@_graduationYear" min="1950" max="2100" step="1" required />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<br />
|
||||
<button type="button" class="btn btn-primary" @onclick="Register">@Localizer["Register"]</button>
|
||||
@ -89,6 +102,9 @@ else
|
||||
private string _displayname = string.Empty;
|
||||
private bool _userCreated = false;
|
||||
private bool _allowsitelogin = true;
|
||||
private string _subject = string.Empty;
|
||||
private int _graduationYear;
|
||||
|
||||
|
||||
public override SecurityAccessLevel SecurityAccessLevel => SecurityAccessLevel.Anonymous;
|
||||
|
||||
@ -124,7 +140,10 @@ else
|
||||
Password = _password,
|
||||
Email = _email,
|
||||
DisplayName = (_displayname == string.Empty ? _username : _displayname),
|
||||
PhotoFileId = null
|
||||
PhotoFileId = null,
|
||||
Subject = _subject,
|
||||
GraduationYear = _graduationYear
|
||||
|
||||
};
|
||||
user = await UserService.AddUserAsync(user);
|
||||
|
||||
|
@ -14,6 +14,7 @@
|
||||
@inject IStringLocalizer<Index> Localizer
|
||||
@inject INotificationService NotificationService
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
@inject IOutputCacheService CacheService
|
||||
|
||||
@if (_initialized)
|
||||
{
|
||||
@ -50,11 +51,12 @@
|
||||
</div>
|
||||
</div>
|
||||
<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="input-group">
|
||||
<input id="sitemap" class="form-control" @bind="@_sitemap" disabled />
|
||||
<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>
|
||||
@ -72,20 +74,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<Section Name="Appearance" Heading="Appearance" ResourceKey="Appearance">
|
||||
<Section Name="Theme" Heading="Theme" ResourceKey="Theme">
|
||||
<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">
|
||||
<Label Class="col-sm-3" For="defaultTheme" HelpText="Select the sites default theme" ResourceKey="DefaultTheme">Default Theme: </Label>
|
||||
<div class="col-sm-9">
|
||||
@ -126,6 +116,32 @@
|
||||
</select>
|
||||
</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>
|
||||
</Section>
|
||||
<Section Name="Functionality" Heading="Functionality" ResourceKey="Functionality">
|
||||
@ -415,6 +431,7 @@
|
||||
private string _themetype = "";
|
||||
private string _containertype = "";
|
||||
private string _admincontainertype = "";
|
||||
private string _cookieconsent = "";
|
||||
|
||||
private Dictionary<string, string> _textEditors = new Dictionary<string, string>();
|
||||
private string _textEditor = "";
|
||||
@ -505,6 +522,7 @@
|
||||
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
|
||||
_containertype = (!string.IsNullOrEmpty(site.DefaultContainerType)) ? site.DefaultContainerType : Constants.DefaultContainer;
|
||||
_admincontainertype = (!string.IsNullOrEmpty(site.AdminContainerType)) ? site.AdminContainerType : Constants.DefaultAdminContainer;
|
||||
_cookieconsent = SettingService.GetSetting(settings, "CookieConsent", string.Empty);
|
||||
|
||||
// functionality
|
||||
var textEditors = ServiceProvider.GetServices<ITextEditor>();
|
||||
@ -717,6 +735,9 @@
|
||||
settings = SettingService.SetSetting(settings, "SiteGuid", _siteguid, true);
|
||||
settings = SettingService.SetSetting(settings, "NotificationRetention", _retention.ToString(), true);
|
||||
|
||||
//cookie consent
|
||||
settings = SettingService.SetSetting(settings, "CookieConsent", _cookieconsent);
|
||||
|
||||
// functionality
|
||||
settings = SettingService.SetSetting(settings, "TextEditor", _textEditor);
|
||||
|
||||
@ -913,4 +934,9 @@
|
||||
_aliasname = "";
|
||||
StateHasChanged();
|
||||
}
|
||||
|
||||
private async Task EvictSitemapOutputCache() {
|
||||
await CacheService.EvictByTag(Constants.SitemapOutputCacheTag);
|
||||
AddModuleMessage(Localizer["Success.SiteMap.CacheEvicted"], MessageType.Success);
|
||||
}
|
||||
}
|
||||
|
@ -153,8 +153,10 @@
|
||||
</div>
|
||||
<br /><br />
|
||||
<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" />
|
||||
<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 Name="Log" Heading="Log" ResourceKey="Log">
|
||||
<div class="container">
|
||||
|
@ -45,24 +45,7 @@
|
||||
<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>
|
||||
<div class="col-sm-9">
|
||||
@if (!string.IsNullOrEmpty(_packagename))
|
||||
{
|
||||
<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 class="row mb-1 align-items-center">
|
||||
@ -116,7 +99,6 @@
|
||||
private string _name;
|
||||
private string _version;
|
||||
private string _packagename = "";
|
||||
private string _packageurl = "";
|
||||
private string _owner = "";
|
||||
private string _url = "";
|
||||
private string _contact = "";
|
||||
@ -185,27 +167,4 @@
|
||||
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 IThemeService ThemeService
|
||||
@inject IPackageService PackageService
|
||||
@inject ISiteService SiteService
|
||||
@inject IStringLocalizer<Index> Localizer
|
||||
@inject IStringLocalizer<SharedResources> SharedLocalizer
|
||||
|
||||
@ -19,6 +20,7 @@ else
|
||||
|
||||
<Pager Items="@_themes">
|
||||
<Header>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th style="width: 1px;"> </th>
|
||||
<th>@SharedLocalizer["Name"]</th>
|
||||
@ -36,6 +38,7 @@ else
|
||||
<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><NavLink class="btn btn-secondary" href="@NavigateUrl("admin/site")">@Localizer["Assign"]</NavLink></td>
|
||||
<td>@context.Name</td>
|
||||
<td>@context.Version</td>
|
||||
<td>
|
||||
|
@ -54,11 +54,13 @@ else
|
||||
<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">
|
||||
<select id="allowregistration" class="form-select" @bind="@_allowregistration">
|
||||
<option value="True">@SharedLocalizer["Yes"]</option>
|
||||
<option value="False">@SharedLocalizer["No"]</option>
|
||||
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||
<option value="false">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
@if (_providertype != "")
|
||||
{
|
||||
<div class="row mb-1 align-items-center">
|
||||
@ -80,8 +82,6 @@ else
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
{
|
||||
<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>
|
||||
<div class="col-sm-9">
|
||||
@ -113,6 +113,15 @@ else
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-1 align-items-center">
|
||||
<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>
|
||||
<div class="col-sm-9">
|
||||
<select id="logouteverywhere" class="form-select" @bind="@_logouteverywhere">
|
||||
<option value="true">@SharedLocalizer["Yes"]</option>
|
||||
<option value="false">@SharedLocalizer["No"]</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</Section>
|
||||
@if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
@ -469,6 +478,7 @@ else
|
||||
private string _cookiename;
|
||||
private string _cookieexpiration;
|
||||
private string _alwaysremember;
|
||||
private string _logouteverywhere;
|
||||
|
||||
private string _minimumlength;
|
||||
private string _uniquecharacters;
|
||||
@ -529,7 +539,7 @@ else
|
||||
await LoadUsersAsync(true);
|
||||
|
||||
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");
|
||||
|
||||
if (UserSecurity.IsAuthorized(PageState.User, RoleNames.Host))
|
||||
@ -538,6 +548,7 @@ else
|
||||
_cookiename = SettingService.GetSetting(settings, "LoginOptions:CookieName", ".AspNetCore.Identity.Application");
|
||||
_cookieexpiration = SettingService.GetSetting(settings, "LoginOptions:CookieExpiration", "");
|
||||
_alwaysremember = SettingService.GetSetting(settings, "LoginOptions:AlwaysRemember", "false");
|
||||
_logouteverywhere = SettingService.GetSetting(settings, "LoginOptions:LogoutEverywhere", "false");
|
||||
|
||||
_minimumlength = SettingService.GetSetting(settings, "IdentityOptions:Password:RequiredLength", "6");
|
||||
_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:CookieExpiration", _cookieexpiration, true);
|
||||
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:RequiredUniqueChars", _uniquecharacters, true);
|
||||
|
@ -2,6 +2,7 @@
|
||||
@namespace Oqtane.Modules.HtmlText
|
||||
@inherits ModuleBase
|
||||
@inject IHtmlTextService HtmlTextService
|
||||
@inject ISettingService SettingService
|
||||
@inject IStringLocalizer<Index> Localizer
|
||||
|
||||
@if (PageState.EditMode)
|
||||
@ -36,6 +37,10 @@
|
||||
{
|
||||
content = htmltext.Content;
|
||||
content = Utilities.FormatContent(content, PageState.Alias, "render");
|
||||
if (bool.Parse(SettingService.GetSetting(ModuleState.Settings, "DynamicTokens", "false")))
|
||||
{
|
||||
content = ReplaceTokens(content);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -15,7 +15,7 @@ namespace Oqtane.Modules.HtmlText
|
||||
Version = "1.0.1",
|
||||
ServerManagerType = "Oqtane.Modules.HtmlText.Manager.HtmlTextManager, Oqtane.Server",
|
||||
ReleaseVersions = "1.0.0,1.0.1",
|
||||
SettingsType = string.Empty,
|
||||
SettingsType = "Oqtane.Modules.HtmlText.Settings, Oqtane.Client",
|
||||
Resources = new List<Resource>()
|
||||
{
|
||||
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 System.Linq;
|
||||
using System.Dynamic;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Oqtane.Modules
|
||||
{
|
||||
@ -35,7 +36,7 @@ namespace Oqtane.Modules
|
||||
protected PageState PageState { get; set; }
|
||||
|
||||
[CascadingParameter]
|
||||
protected Module ModuleState { get; set; }
|
||||
protected Models.Module ModuleState { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public RenderModeBoundary RenderModeBoundary { get; set; }
|
||||
@ -413,6 +414,79 @@ namespace Oqtane.Modules
|
||||
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
|
||||
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>
|
||||
<OutputType>Exe</OutputType>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Version>6.1.0</Version>
|
||||
<Version>6.1.1</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -12,7 +12,7 @@
|
||||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/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>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<RootNamespace>Oqtane</RootNamespace>
|
||||
@ -22,10 +22,10 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -180,9 +180,6 @@
|
||||
<data name="Once" xml:space="preserve">
|
||||
<value>Execute Once</value>
|
||||
</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">
|
||||
<value>Refresh</value>
|
||||
</data>
|
||||
|
@ -228,18 +228,6 @@
|
||||
<data name="View License" xml:space="preserve">
|
||||
<value>View License</value>
|
||||
</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">
|
||||
<value>Browse</value>
|
||||
</data>
|
||||
|
@ -118,7 +118,7 @@
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="NoCriteria" xml:space="preserve">
|
||||
<value>You Must Provide Some Search Criteria</value>
|
||||
<value>Please Enter Some Search Criteria</value>
|
||||
</data>
|
||||
<data name="NoResult" xml:space="preserve">
|
||||
<value>No Content Matches The Criteria Provided</value>
|
||||
|
@ -349,7 +349,7 @@
|
||||
<value>Relay Configured?</value>
|
||||
</data>
|
||||
<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 name="SiteMap.Text" xml:space="preserve">
|
||||
<value>Site Map:</value>
|
||||
@ -426,4 +426,25 @@
|
||||
<data name="System" xml:space="preserve">
|
||||
<value>System</value>
|
||||
</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>
|
@ -118,7 +118,7 @@
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<data name="Swagger" xml:space="preserve">
|
||||
<value>Access Swagger UI</value>
|
||||
<value>Swagger UI</value>
|
||||
</data>
|
||||
<data name="FrameworkVersion.HelpText" xml:space="preserve">
|
||||
<value>Framework Version</value>
|
||||
@ -306,4 +306,7 @@
|
||||
<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>
|
||||
</data>
|
||||
<data name="Endpoints" xml:space="preserve">
|
||||
<value>API Endpoints</value>
|
||||
</data>
|
||||
</root>
|
@ -180,16 +180,4 @@
|
||||
<data name="View License" xml:space="preserve">
|
||||
<value>View License</value>
|
||||
</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>
|
@ -156,4 +156,7 @@
|
||||
<data name="Enabled" xml:space="preserve">
|
||||
<value>Enabled?</value>
|
||||
</data>
|
||||
<data name="Assign" xml:space="preserve">
|
||||
<value>Assign</value>
|
||||
</data>
|
||||
</root>
|
@ -507,4 +507,10 @@
|
||||
<data name="Error.DeleteUser" xml:space="preserve">
|
||||
<value>Error Deleting User</value>
|
||||
</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>
|
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>
|
||||
<!--
|
||||
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
|
||||
@inherits ThemeBase
|
||||
|
||||
|
||||
<div class="breadcrumbs">
|
||||
<Breadcrumbs />
|
||||
</div>
|
||||
|
||||
<div class="row flex-xl-nowrap gx-0">
|
||||
<div class="sidebar">
|
||||
<nav class="navbar">
|
||||
@ -22,13 +17,18 @@
|
||||
<Login />
|
||||
<ControlPanel LanguageDropdownAlignment="right" />
|
||||
</div>
|
||||
<div class="breadcrumbs">
|
||||
<Breadcrumbs />
|
||||
</div>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="row px-4">
|
||||
<Pane Name="@PaneNames.Admin" />
|
||||
<CookieConsent />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@code {
|
||||
|
@ -573,7 +573,7 @@
|
||||
else
|
||||
{
|
||||
// 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);
|
||||
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">
|
||||
<input type="hidden" name="@Constants.RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
|
||||
<input type="hidden" name="returnurl" value="@returnurl" />
|
||||
<input type="hidden" name="everywhere" value="@everywhere" />
|
||||
<button type="submit" class="@CssClass">@Localizer["Logout"]</button>
|
||||
</form>
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.JSInterop;
|
||||
using Oqtane.Enums;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Providers;
|
||||
using Oqtane.Security;
|
||||
using Oqtane.Services;
|
||||
@ -26,6 +25,7 @@ namespace Oqtane.Themes.Controls
|
||||
protected string loginurl;
|
||||
protected string logouturl;
|
||||
protected string returnurl;
|
||||
protected string everywhere;
|
||||
|
||||
protected override void OnParametersSet()
|
||||
{
|
||||
@ -57,6 +57,7 @@ namespace Oqtane.Themes.Controls
|
||||
|
||||
// set logout url
|
||||
logouturl = Utilities.TenantUrl(PageState.Alias, "/pages/logout/");
|
||||
everywhere = SettingService.GetSetting(PageState.Site.Settings, "LoginOptions:LogoutEverywhere", "false");
|
||||
|
||||
// verify anonymous users can access current page
|
||||
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
|
||||
{
|
||||
// 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);
|
||||
await interop.SubmitForm(logouturl, fields);
|
||||
}
|
||||
|
@ -107,6 +107,7 @@
|
||||
{
|
||||
<Pane Name="Footer" />
|
||||
}
|
||||
<CookieConsent />
|
||||
</div>
|
||||
</main>
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
try
|
||||
|
@ -27,6 +27,7 @@ namespace Oqtane.UI
|
||||
public bool IsInternalNavigation { get; set; }
|
||||
public Guid RenderId { get; set; }
|
||||
public bool Refresh { get; set; }
|
||||
public bool AllowCookies { get; set; }
|
||||
|
||||
public List<Page> Pages
|
||||
{
|
||||
|
@ -14,6 +14,7 @@
|
||||
@inject IUrlMappingService UrlMappingService
|
||||
@inject ILogService LogService
|
||||
@inject ISettingService SettingService
|
||||
@inject ICookieConsentService CookieConsentService
|
||||
@inject IJSRuntime JSRuntime
|
||||
@implements IHandleAfterRender
|
||||
@implements IDisposable
|
||||
@ -293,6 +294,14 @@
|
||||
// load additional metadata for modules
|
||||
(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)
|
||||
_pagestate = new PageState
|
||||
{
|
||||
@ -316,7 +325,8 @@
|
||||
ReturnUrl = returnurl,
|
||||
IsInternalNavigation = _isInternalNavigation,
|
||||
RenderId = Guid.NewGuid(),
|
||||
Refresh = false
|
||||
Refresh = false,
|
||||
AllowCookies = _allowCookies.GetValueOrDefault(true)
|
||||
};
|
||||
OnStateChange?.Invoke(_pagestate);
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Version>6.1.0</Version>
|
||||
<Version>6.1.1</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -10,7 +10,7 @@
|
||||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/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>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
@ -34,7 +34,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<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>
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Version>6.1.0</Version>
|
||||
<Version>6.1.1</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -10,7 +10,7 @@
|
||||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/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>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
@ -34,8 +34,8 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="EFCore.NamingConventions" Version="9.0.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.1" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.3" />
|
||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Version>6.1.0</Version>
|
||||
<Version>6.1.1</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -10,7 +10,7 @@
|
||||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/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>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
@ -33,7 +33,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Version>6.1.0</Version>
|
||||
<Version>6.1.1</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -10,7 +10,7 @@
|
||||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/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>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
@ -33,7 +33,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="9.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -6,7 +6,7 @@
|
||||
<!-- <TargetFrameworks>net9.0-android;net9.0-ios;net9.0-maccatalyst</TargetFrameworks> -->
|
||||
<!-- <TargetFrameworks>$(TargetFrameworks);net9.0-tizen</TargetFrameworks> -->
|
||||
<OutputType>Exe</OutputType>
|
||||
<Version>6.1.0</Version>
|
||||
<Version>6.1.1</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -14,7 +14,7 @@
|
||||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/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>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<RootNamespace>Oqtane.Maui</RootNamespace>
|
||||
@ -30,7 +30,7 @@
|
||||
<ApplicationId>com.oqtane.maui</ApplicationId>
|
||||
|
||||
<!-- Versions -->
|
||||
<ApplicationDisplayVersion>6.1.0</ApplicationDisplayVersion>
|
||||
<ApplicationDisplayVersion>6.1.1</ApplicationDisplayVersion>
|
||||
<ApplicationVersion>1</ApplicationVersion>
|
||||
|
||||
<!-- To develop, package, and publish an app to the Microsoft Store, see: https://aka.ms/MauiTemplateUnpackaged -->
|
||||
@ -67,14 +67,14 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.1" />
|
||||
<PackageReference Include="System.Net.Http.Json" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.Maui.Controls" Version="9.0.30" />
|
||||
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="9.0.30" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="9.0.30" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" Version="9.0.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.3" />
|
||||
<PackageReference Include="System.Net.Http.Json" Version="9.0.3" />
|
||||
<PackageReference Include="Microsoft.Maui.Controls" Version="9.0.40" />
|
||||
<PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="9.0.40" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebView.Maui" Version="9.0.40" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -14,6 +14,9 @@ Oqtane.Interop = {
|
||||
}
|
||||
document.cookie = cookieString;
|
||||
},
|
||||
setCookieString: function (cookieString) {
|
||||
document.cookie = cookieString;
|
||||
},
|
||||
getCookie: function (name) {
|
||||
name = name + "=";
|
||||
var decodedCookie = decodeURIComponent(document.cookie);
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package>
|
||||
<metadata>
|
||||
<id>Oqtane.Client</id>
|
||||
<version>6.1.0</version>
|
||||
<version>6.1.1</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.0</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.1</releaseNotes>
|
||||
<readme>readme.md</readme>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package>
|
||||
<metadata>
|
||||
<id>Oqtane.Framework</id>
|
||||
<version>6.1.0</version>
|
||||
<version>6.1.1</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
@ -11,8 +11,8 @@
|
||||
<copyright>.NET Foundation</copyright>
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework/releases/download/v6.1.0/Oqtane.Framework.6.1.0.Upgrade.zip</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.0</releaseNotes>
|
||||
<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.1</releaseNotes>
|
||||
<readme>readme.md</readme>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane framework</tags>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package>
|
||||
<metadata>
|
||||
<id>Oqtane.Server</id>
|
||||
<version>6.1.0</version>
|
||||
<version>6.1.1</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.0</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.1</releaseNotes>
|
||||
<readme>readme.md</readme>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package>
|
||||
<metadata>
|
||||
<id>Oqtane.Shared</id>
|
||||
<version>6.1.0</version>
|
||||
<version>6.1.1</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.0</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.1</releaseNotes>
|
||||
<readme>readme.md</readme>
|
||||
<icon>icon.png</icon>
|
||||
<tags>oqtane</tags>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<package>
|
||||
<metadata>
|
||||
<id>Oqtane.Updater</id>
|
||||
<version>6.1.0</version>
|
||||
<version>6.1.1</version>
|
||||
<authors>Shaun Walker</authors>
|
||||
<owners>.NET Foundation</owners>
|
||||
<title>Oqtane Framework</title>
|
||||
@ -12,7 +12,7 @@
|
||||
<requireLicenseAcceptance>false</requireLicenseAcceptance>
|
||||
<license type="expression">MIT</license>
|
||||
<projectUrl>https://github.com/oqtane/oqtane.framework</projectUrl>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.0</releaseNotes>
|
||||
<releaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.1</releaseNotes>
|
||||
<readme>readme.md</readme>
|
||||
<icon>icon.png</icon>
|
||||
<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 IVisitorRepository VisitorRepository
|
||||
@inject IJwtManager JwtManager
|
||||
@inject ICookieConsentService CookieConsentService
|
||||
@inject ISettingService SettingService
|
||||
|
||||
@if (_initialized)
|
||||
{
|
||||
@ -107,6 +109,7 @@
|
||||
private string _styleSheets = "";
|
||||
private string _scripts = "";
|
||||
private string _message = "";
|
||||
private bool _allowCookies;
|
||||
private PageState _pageState;
|
||||
|
||||
// CascadingParameter is required to access HttpContext
|
||||
@ -140,6 +143,9 @@
|
||||
_prerender = site.Prerender;
|
||||
_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>();
|
||||
|
||||
Route route = new Route(url, alias.Path);
|
||||
@ -170,7 +176,7 @@
|
||||
modules = await SiteService.GetModulesAsync(site.SiteId, page.PageId);
|
||||
}
|
||||
|
||||
if (site.VisitorTracking)
|
||||
if (site.VisitorTracking && _allowCookies)
|
||||
{
|
||||
TrackVisitor(site.SiteId);
|
||||
}
|
||||
@ -245,7 +251,8 @@
|
||||
ReturnUrl = "",
|
||||
IsInternalNavigation = false,
|
||||
RenderId = Guid.NewGuid(),
|
||||
Refresh = true
|
||||
Refresh = true,
|
||||
AllowCookies = _allowCookies
|
||||
};
|
||||
}
|
||||
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;
|
||||
});
|
||||
}).ToList();
|
||||
|
||||
}
|
||||
|
||||
// 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;
|
||||
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
|
||||
{
|
||||
|
@ -34,6 +34,14 @@ namespace Oqtane.Extensions
|
||||
options.SetDefaultCulture(defaultCulture)
|
||||
.AddSupportedCultures(supportedCultures)
|
||||
.AddSupportedUICultures(supportedCultures);
|
||||
|
||||
foreach(var culture in options.SupportedCultures)
|
||||
{
|
||||
if (culture.TextInfo.IsRightToLeft)
|
||||
{
|
||||
RightToLeftCulture.ResolveFormat(culture);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return app;
|
||||
@ -47,6 +55,5 @@ namespace Oqtane.Extensions
|
||||
|
||||
public static IApplicationBuilder UseExceptionMiddleWare(this IApplicationBuilder builder)
|
||||
=> builder.UseMiddleware<ExceptionMiddleware>();
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -103,6 +103,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||
services.AddScoped<ISearchService, SearchService>();
|
||||
services.AddScoped<ISearchProvider, DatabaseSearchProvider>();
|
||||
services.AddScoped<IImageService, ImageService>();
|
||||
services.AddScoped<ICookieConsentService, ServerCookieConsentService>();
|
||||
|
||||
// providers
|
||||
services.AddScoped<ITextEditor, Oqtane.Modules.Controls.QuillJSTextEditor>();
|
||||
@ -116,6 +117,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||
// services
|
||||
services.AddTransient<ISiteService, ServerSiteService>();
|
||||
services.AddTransient<ILocalizationCookieService, ServerLocalizationCookieService>();
|
||||
services.AddTransient<IOutputCacheService, ServerOutputCacheService>();
|
||||
|
||||
// repositories
|
||||
services.AddTransient<IModuleDefinitionRepository, ModuleDefinitionRepository>();
|
||||
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
@ -45,28 +46,61 @@ namespace Oqtane.Infrastructure
|
||||
|
||||
protected async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
await Task.Yield(); // required so that this method does not block startup
|
||||
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
using (var scope = _serviceScopeFactory.CreateScope())
|
||||
{
|
||||
IConfigurationRoot _config = scope.ServiceProvider.GetRequiredService<IConfigurationRoot>();
|
||||
ILogger<HostedServiceBase> _filelogger = scope.ServiceProvider.GetRequiredService<ILogger<HostedServiceBase>>();
|
||||
|
||||
// if framework is installed
|
||||
if (IsInstalled(_config))
|
||||
{
|
||||
try
|
||||
{
|
||||
var jobs = scope.ServiceProvider.GetRequiredService<IJobRepository>();
|
||||
|
||||
// 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)
|
||||
{
|
||||
// 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 != "")
|
||||
{
|
||||
job.Name = jobObject.Name;
|
||||
}
|
||||
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;
|
||||
|
||||
job = jobs.AddJob(job);
|
||||
}
|
||||
|
||||
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 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
|
||||
DateTime NextExecution;
|
||||
if (job.NextExecution == null)
|
||||
@ -146,9 +180,6 @@ namespace Oqtane.Infrastructure
|
||||
}
|
||||
}
|
||||
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}"));
|
||||
}
|
||||
@ -208,9 +239,12 @@ namespace Oqtane.Infrastructure
|
||||
{
|
||||
using (var scope = _serviceScopeFactory.CreateScope())
|
||||
{
|
||||
IConfigurationRoot _config = scope.ServiceProvider.GetRequiredService<IConfigurationRoot>();
|
||||
ILogger<HostedServiceBase> _filelogger = scope.ServiceProvider.GetRequiredService<ILogger<HostedServiceBase>>();
|
||||
|
||||
try
|
||||
{
|
||||
if (IsInstalled(_config))
|
||||
{
|
||||
string jobTypeName = Utilities.GetFullTypeName(GetType().AssemblyQualifiedName);
|
||||
IJobRepository jobs = scope.ServiceProvider.GetRequiredService<IJobRepository>();
|
||||
@ -222,43 +256,13 @@ namespace Oqtane.Infrastructure
|
||||
job.IsExecuting = false;
|
||||
jobs.UpdateJob(job);
|
||||
}
|
||||
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;
|
||||
}
|
||||
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)
|
||||
{
|
||||
// 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 Starting Scheduled Job: {Name} - {ex}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_executingTask = ExecuteAsync(_cancellationTokenSource.Token);
|
||||
|
||||
@ -314,6 +318,11 @@ namespace Oqtane.Infrastructure
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsInstalled(IConfigurationRoot config)
|
||||
{
|
||||
return !string.IsNullOrEmpty(config.GetConnectionString(SettingKeys.ConnectionStringKey));
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_cancellationTokenSource.Cancel();
|
||||
|
@ -39,7 +39,7 @@ namespace Oqtane.Infrastructure
|
||||
List<Site> sites = siteRepository.GetSites().ToList();
|
||||
foreach (Site site in sites)
|
||||
{
|
||||
log += "Processing Site: " + site.Name + "<br />";
|
||||
log += "<br />Processing Site: " + site.Name + "<br />";
|
||||
int retention;
|
||||
int count;
|
||||
|
||||
@ -118,11 +118,11 @@ namespace Oqtane.Infrastructure
|
||||
try
|
||||
{
|
||||
var assemblies = installationManager.RegisterAssemblies();
|
||||
log += assemblies.ToString() + " Assemblies Registered<br />";
|
||||
log += "<br />" + assemblies.ToString() + " Assemblies Registered<br />";
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
log += $"Error Registering Assemblies - {ex.Message}<br />";
|
||||
log += $"<br />Error Registering Assemblies - {ex.Message}<br />";
|
||||
}
|
||||
|
||||
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 Microsoft.Extensions.Localization;
|
||||
using Oqtane.Documentation;
|
||||
using Oqtane.Infrastructure;
|
||||
using Oqtane.Models;
|
||||
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")]
|
||||
public class AdminSiteTemplate : ISiteTemplate
|
||||
{
|
||||
private readonly IStringLocalizer<AdminSiteTemplate> _localizer;
|
||||
|
||||
public AdminSiteTemplate(IStringLocalizer<AdminSiteTemplate> localizer)
|
||||
{
|
||||
_localizer = localizer;
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
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
|
||||
{
|
||||
Name = "Not Found",
|
||||
|
@ -7,7 +7,7 @@ using Oqtane.Models;
|
||||
using Oqtane.Repository;
|
||||
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")]
|
||||
public class DefaultSiteTemplate : ISiteTemplate
|
||||
|
@ -4,7 +4,7 @@ using Oqtane.Infrastructure;
|
||||
using Oqtane.Models;
|
||||
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")]
|
||||
public class EmptySiteTemplate : ISiteTemplate
|
||||
|
@ -1,7 +1,9 @@
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Oqtane.Infrastructure.SiteTemplates;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Repository;
|
||||
using Oqtane.Shared;
|
||||
@ -75,6 +77,9 @@ namespace Oqtane.Infrastructure
|
||||
case "6.1.0":
|
||||
Upgrade_6_1_0(tenant, scope);
|
||||
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");
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
var tenants = scope.ServiceProvider.GetRequiredService<ITenantManager>();
|
||||
|
@ -3,7 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Version>6.1.0</Version>
|
||||
<Version>6.1.1</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -11,7 +11,7 @@
|
||||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/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>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<RootNamespace>Oqtane</RootNamespace>
|
||||
@ -34,21 +34,21 @@
|
||||
<EmbeddedResource Include="Scripts\MigrateTenant.sql" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.3" />
|
||||
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.0.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.1">
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="9.0.3">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="9.0.1" />
|
||||
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.11-pre20241216174303" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.6" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.72" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.2.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="9.0.3" />
|
||||
<PackageReference Include="Microsoft.Data.Sqlite.Core" Version="9.0.3" />
|
||||
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.11" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.7" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.12.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="7.3.1" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Oqtane.Client\Oqtane.Client.csproj" />
|
||||
|
@ -50,7 +50,6 @@ namespace Oqtane.Pages
|
||||
{
|
||||
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;
|
||||
return BrokenFile();
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ using System.Xml;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.RazorPages;
|
||||
using Microsoft.AspNetCore.OutputCaching;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Oqtane.Enums;
|
||||
using Oqtane.Infrastructure;
|
||||
@ -19,6 +20,7 @@ using Oqtane.Shared;
|
||||
namespace Oqtane.Pages
|
||||
{
|
||||
[AllowAnonymous]
|
||||
[OutputCache(Duration = 300, Tags = [Constants.SitemapOutputCacheTag])]
|
||||
public class SitemapModel : PageModel
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
|
@ -22,6 +22,14 @@ namespace Oqtane.Repository
|
||||
{
|
||||
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);
|
||||
return _db.Job.ToList();
|
||||
});
|
||||
|
@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Oqtane.Infrastructure;
|
||||
@ -27,7 +28,7 @@ namespace Oqtane.Repository
|
||||
{
|
||||
if (IsMaster(entityName))
|
||||
{
|
||||
return _master.Setting.Where(item => item.EntityName == entityName);
|
||||
return _master.Setting.Where(item => item.EntityName == entityName).ToList();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -38,13 +39,28 @@ namespace Oqtane.Repository
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public Setting AddSetting(Setting setting)
|
||||
{
|
||||
using var tenant = _tenantContextFactory.CreateDbContext();
|
||||
if (IsMaster(setting.EntityName))
|
||||
{
|
||||
_master.Setting.Add(setting);
|
||||
@ -52,6 +68,7 @@ namespace Oqtane.Repository
|
||||
}
|
||||
else
|
||||
{
|
||||
using var tenant = _tenantContextFactory.CreateDbContext();
|
||||
tenant.Setting.Add(setting);
|
||||
tenant.SaveChanges();
|
||||
}
|
||||
@ -61,7 +78,6 @@ namespace Oqtane.Repository
|
||||
|
||||
public Setting UpdateSetting(Setting setting)
|
||||
{
|
||||
using var tenant = _tenantContextFactory.CreateDbContext();
|
||||
if (IsMaster(setting.EntityName))
|
||||
{
|
||||
_master.Entry(setting).State = EntityState.Modified;
|
||||
@ -69,6 +85,7 @@ namespace Oqtane.Repository
|
||||
}
|
||||
else
|
||||
{
|
||||
using var tenant = _tenantContextFactory.CreateDbContext();
|
||||
tenant.Entry(setting).State = EntityState.Modified;
|
||||
tenant.SaveChanges();
|
||||
}
|
||||
@ -78,33 +95,32 @@ namespace Oqtane.Repository
|
||||
|
||||
public Setting GetSetting(string entityName, int settingId)
|
||||
{
|
||||
using var tenant = _tenantContextFactory.CreateDbContext();
|
||||
if (IsMaster(entityName))
|
||||
{
|
||||
return _master.Setting.Find(settingId);
|
||||
}
|
||||
else
|
||||
{
|
||||
using var tenant = _tenantContextFactory.CreateDbContext();
|
||||
return tenant.Setting.Find(settingId);
|
||||
}
|
||||
}
|
||||
|
||||
public Setting GetSetting(string entityName, int entityId, string settingName)
|
||||
{
|
||||
using var tenant = _tenantContextFactory.CreateDbContext();
|
||||
if (IsMaster(entityName))
|
||||
{
|
||||
return _master.Setting.Where(item => item.EntityName == entityName && item.EntityId == entityId && item.SettingName == settingName).FirstOrDefault();
|
||||
}
|
||||
else
|
||||
{
|
||||
using var tenant = _tenantContextFactory.CreateDbContext();
|
||||
return tenant.Setting.Where(item => item.EntityName == entityName && item.EntityId == entityId && item.SettingName == settingName).FirstOrDefault();
|
||||
}
|
||||
}
|
||||
|
||||
public void DeleteSetting(string entityName, int settingId)
|
||||
{
|
||||
using var tenant = _tenantContextFactory.CreateDbContext();
|
||||
if (IsMaster(entityName))
|
||||
{
|
||||
Setting setting = _master.Setting.Find(settingId);
|
||||
@ -113,6 +129,7 @@ namespace Oqtane.Repository
|
||||
}
|
||||
else
|
||||
{
|
||||
using var tenant = _tenantContextFactory.CreateDbContext();
|
||||
Setting setting = tenant.Setting.Find(settingId);
|
||||
tenant.Setting.Remove(setting);
|
||||
tenant.SaveChanges();
|
||||
@ -122,7 +139,6 @@ namespace Oqtane.Repository
|
||||
|
||||
public void DeleteSettings(string entityName, int entityId)
|
||||
{
|
||||
using var tenant = _tenantContextFactory.CreateDbContext();
|
||||
if (IsMaster(entityName))
|
||||
{
|
||||
IEnumerable<Setting> settings = _master.Setting
|
||||
@ -136,6 +152,7 @@ namespace Oqtane.Repository
|
||||
}
|
||||
else
|
||||
{
|
||||
using var tenant = _tenantContextFactory.CreateDbContext();
|
||||
IEnumerable<Setting> settings = tenant.Setting
|
||||
.Where(item => item.EntityName == entityName)
|
||||
.Where(item => item.EntityId == entityId);
|
||||
|
@ -9,6 +9,7 @@ using Oqtane.Enums;
|
||||
using Oqtane.Infrastructure;
|
||||
using Oqtane.Models;
|
||||
using Oqtane.Modules;
|
||||
using Oqtane.Modules.Admin.Modules;
|
||||
using Oqtane.Shared;
|
||||
using Module = Oqtane.Models.Module;
|
||||
|
||||
@ -25,6 +26,7 @@ namespace Oqtane.Repository
|
||||
private readonly IPageModuleRepository _pageModuleRepository;
|
||||
private readonly IModuleDefinitionRepository _moduleDefinitionRepository;
|
||||
private readonly IThemeRepository _themeRepository;
|
||||
private readonly ISettingRepository _settingRepository;
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly IConfigurationRoot _config;
|
||||
private readonly IServerStateManager _serverState;
|
||||
@ -32,8 +34,8 @@ namespace Oqtane.Repository
|
||||
private static readonly object _lock = new object();
|
||||
|
||||
public SiteRepository(IDbContextFactory<TenantDBContext> factory, IRoleRepository roleRepository, IProfileRepository profileRepository, IFolderRepository folderRepository, IPageRepository pageRepository,
|
||||
IModuleRepository moduleRepository, IPageModuleRepository pageModuleRepository, IModuleDefinitionRepository moduleDefinitionRepository, IThemeRepository themeRepository, IServiceProvider serviceProvider,
|
||||
IConfigurationRoot config, IServerStateManager serverState, ILogManager logger)
|
||||
IModuleRepository moduleRepository, IPageModuleRepository pageModuleRepository, IModuleDefinitionRepository moduleDefinitionRepository, IThemeRepository themeRepository, ISettingRepository settingRepository,
|
||||
IServiceProvider serviceProvider, IConfigurationRoot config, IServerStateManager serverState, ILogManager logger)
|
||||
{
|
||||
_factory = factory;
|
||||
_roleRepository = roleRepository;
|
||||
@ -44,6 +46,7 @@ namespace Oqtane.Repository
|
||||
_pageModuleRepository = pageModuleRepository;
|
||||
_moduleDefinitionRepository = moduleDefinitionRepository;
|
||||
_themeRepository = themeRepository;
|
||||
_settingRepository = settingRepository;
|
||||
_serviceProvider = serviceProvider;
|
||||
_config = config;
|
||||
_serverState = serverState;
|
||||
@ -391,6 +394,7 @@ namespace Oqtane.Repository
|
||||
{
|
||||
_logger.Log(LogLevel.Information, "Site Template", LogFunction.Update, "Page Updated {Page}", page);
|
||||
}
|
||||
UpdateSettings(EntityNames.Page, page.PageId, pageTemplate.Settings);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -401,6 +405,7 @@ namespace Oqtane.Repository
|
||||
{
|
||||
_logger.Log(LogLevel.Information, "Site Template", LogFunction.Create, "Page Added {Page}", page);
|
||||
}
|
||||
UpdateSettings(EntityNames.Page, page.PageId, pageTemplate.Settings);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -457,6 +462,7 @@ namespace Oqtane.Repository
|
||||
{
|
||||
_logger.Log(LogLevel.Information, "Site Template", LogFunction.Update, "Page Module Updated {PageModule}", pageModule);
|
||||
}
|
||||
UpdateSettings(EntityNames.Module, pageModule.Module.ModuleId, pageTemplateModule.Settings);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -475,6 +481,7 @@ namespace Oqtane.Repository
|
||||
{
|
||||
_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
|
||||
var count = 0;
|
||||
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();
|
||||
while (visitors.Count > 0)
|
||||
{
|
||||
count += visitors.Count;
|
||||
db.Visitor.RemoveRange(visitors);
|
||||
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();
|
||||
}
|
||||
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 =>
|
||||
{
|
||||
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
|
||||
@ -222,6 +224,7 @@ namespace Oqtane
|
||||
app.UseJwtAuthorization();
|
||||
app.UseRouting();
|
||||
app.UseCors();
|
||||
app.UseOutputCache();
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
app.UseAntiforgery();
|
||||
|
@ -2,10 +2,10 @@
|
||||
"RenderMode": "Interactive",
|
||||
"Runtime": "Server",
|
||||
"Database": {
|
||||
"DefaultDBType": ""
|
||||
"DefaultDBType": "Oqtane.Database.Sqlite.SqliteDatabase, Oqtane.Database.Sqlite"
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"DefaultConnection": ""
|
||||
"DefaultConnection": "Data Source=Oqtane-202507152139.db;"
|
||||
},
|
||||
"Installation": {
|
||||
"DefaultAlias": "",
|
||||
@ -54,5 +54,9 @@
|
||||
"LogLevel": {
|
||||
"Default": "Information"
|
||||
}
|
||||
}
|
||||
},
|
||||
"InstallationId": "e0b3aaed-a33d-4756-bd08-a83403d0c437",
|
||||
"InstallationVersion": "6.1.1",
|
||||
"InstallationDate": "202507152140",
|
||||
"PackageRegistryEmail": "ismail.hasimoglu@edu.szu.at"
|
||||
}
|
@ -13,11 +13,11 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.1" />
|
||||
<PackageReference Include="System.Net.Http.Json" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.Http" Version="9.0.3" />
|
||||
<PackageReference Include="System.Net.Http.Json" Version="9.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -19,10 +19,10 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="9.0.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="9.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -30,7 +30,6 @@
|
||||
|
||||
.breadcrumbs {
|
||||
background-color: #e6e6e6;
|
||||
border-bottom: 1px solid #d6d5d5;
|
||||
}
|
||||
|
||||
.top-row {
|
||||
@ -120,12 +119,7 @@
|
||||
.app-logo .navbar-brand {
|
||||
color: white;
|
||||
}
|
||||
|
||||
@media (max-width: 767.98px) {
|
||||
.main .top-row {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (max-width: 991.98px) {
|
||||
.app-search {
|
||||
border-radius: 6px;
|
||||
}
|
||||
@ -141,7 +135,7 @@
|
||||
|
||||
.app-search:active, .app-search:hover {
|
||||
display: block;
|
||||
position: fixed;
|
||||
position: absolute;
|
||||
color: #fff;
|
||||
top: 0;
|
||||
min-height: 60px;
|
||||
@ -149,6 +143,7 @@
|
||||
left: 0;
|
||||
z-index: 999;
|
||||
border-radius: 0;
|
||||
background-color: #e6e6e6;
|
||||
}
|
||||
|
||||
.app-search:active .app-form-inline, .app-search:hover .app-form-inline {
|
||||
@ -169,6 +164,11 @@
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 767.98px) {
|
||||
.main .top-row {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 768px) {
|
||||
app {
|
||||
@ -257,6 +257,7 @@
|
||||
width: 100%;
|
||||
left: 0;
|
||||
z-index: 4;
|
||||
border-bottom: 1px solid #d6d5d5;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
|
@ -99,7 +99,49 @@ div.app-moduleactions a.dropdown-toggle, div.app-moduleactions div.dropdown-menu
|
||||
color: #ffffff;
|
||||
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) {
|
||||
|
||||
.app-menu {
|
||||
@ -130,45 +172,7 @@ div.app-moduleactions a.dropdown-toggle, div.app-moduleactions div.dropdown-menu
|
||||
position: relative;
|
||||
top: 60px;
|
||||
}
|
||||
.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;
|
||||
.app-search:active, .app-search:hover{
|
||||
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>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.3" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="9.0.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.Localization" Version="9.0.3" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -272,3 +272,13 @@ app {
|
||||
.app-logo .navbar-brand {
|
||||
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;
|
||||
},
|
||||
setCookieString: function (cookieString) {
|
||||
document.cookie = cookieString;
|
||||
},
|
||||
getCookie: function (name) {
|
||||
name = name + "=";
|
||||
var decodedCookie = decodeURIComponent(document.cookie);
|
||||
|
@ -1,8 +1,8 @@
|
||||
const scriptKeys = new Set();
|
||||
|
||||
export function onUpdate() {
|
||||
// determine if this is an enhanced navigation
|
||||
let enhancedNavigation = scriptKeys.size !== 0;
|
||||
// determine if this is an initial request
|
||||
let initialRequest = scriptKeys.size === 0;
|
||||
|
||||
// iterate over all script elements in document
|
||||
const scripts = document.getElementsByTagName('script');
|
||||
@ -11,7 +11,7 @@ export function onUpdate() {
|
||||
if (script.hasAttribute('data-reload')) {
|
||||
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"
|
||||
let dataReload = script.getAttribute('data-reload');
|
||||
if ((dataReload === 'always' || dataReload === 'true') || (!scriptKeys.has(key) && dataReload == 'once')) {
|
||||
@ -40,7 +40,7 @@ function getKey(script) {
|
||||
function reloadScript(script) {
|
||||
try {
|
||||
if (isValid(script)) {
|
||||
replaceScript(script);
|
||||
injectScript(script);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Blazor Script Reload failed to load script: ${getKey(script)}`, error);
|
||||
@ -55,16 +55,18 @@ function isValid(script) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function replaceScript(script) {
|
||||
function injectScript(script) {
|
||||
return new Promise((resolve, reject) => {
|
||||
var newScript = document.createElement('script');
|
||||
|
||||
// replicate attributes and content
|
||||
for (let i = 0; i < script.attributes.length; i++) {
|
||||
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.removeAttribute('data-reload');
|
||||
|
||||
// dynamically injected scripts cannot be async or deferred
|
||||
newScript.async = false;
|
||||
@ -73,10 +75,10 @@ function replaceScript(script) {
|
||||
newScript.onload = () => resolve();
|
||||
newScript.onerror = (error) => reject(error);
|
||||
|
||||
// remove existing script element
|
||||
script.remove();
|
||||
|
||||
// replace with new script element to force reload in Blazor
|
||||
// inject script element in head to force execution in Blazor
|
||||
document.head.appendChild(newScript);
|
||||
|
||||
// remove data-reload attribute
|
||||
script.removeAttribute('data-reload');
|
||||
});
|
||||
}
|
@ -32,6 +32,15 @@ namespace Oqtane.Models
|
||||
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)
|
||||
{
|
||||
SetDefaults();
|
||||
|
@ -35,6 +35,7 @@ namespace Oqtane.Models
|
||||
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
||||
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
|
||||
};
|
||||
Settings = new List<Setting>();
|
||||
PageTemplateModules = new List<PageTemplateModule>();
|
||||
|
||||
// properties used by IModule
|
||||
@ -60,6 +61,7 @@ namespace Oqtane.Models
|
||||
public bool IsPersonalizable { get; set; }
|
||||
public bool IsDeleted { get; set; }
|
||||
public List<Permission> PermissionList { get; set; }
|
||||
public List<Setting> Settings { get; set; }
|
||||
public List<PageTemplateModule> PageTemplateModules { get; set; }
|
||||
|
||||
// properties used by IModule
|
||||
@ -99,6 +101,7 @@ namespace Oqtane.Models
|
||||
new Permission(PermissionNames.View, RoleNames.Admin, true),
|
||||
new Permission(PermissionNames.Edit, RoleNames.Admin, true)
|
||||
};
|
||||
Settings = new List<Setting>();
|
||||
Content = "";
|
||||
}
|
||||
|
||||
@ -109,6 +112,7 @@ namespace Oqtane.Models
|
||||
public string ContainerType { get; set; }
|
||||
public bool IsDeleted { get; set; }
|
||||
public List<Permission> PermissionList { get; set; }
|
||||
public List<Setting> Settings { get; set; }
|
||||
public string Content { get; set; }
|
||||
|
||||
[Obsolete("The ModulePermissions property is deprecated. Use PermissionList instead", false)]
|
||||
|
@ -32,6 +32,10 @@ namespace Oqtane.Models
|
||||
/// <summary>
|
||||
/// Reference to a <see cref="File"/> containing the users photo.
|
||||
/// </summary>
|
||||
|
||||
public string Subject { get; set; }
|
||||
public int GraduationYear { get; set; }
|
||||
|
||||
public int? PhotoFileId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
|
@ -3,7 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<Configurations>Debug;Release</Configurations>
|
||||
<Version>6.1.0</Version>
|
||||
<Version>6.1.1</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -11,7 +11,7 @@
|
||||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/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>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<RootNamespace>Oqtane</RootNamespace>
|
||||
@ -19,11 +19,11 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.1" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.3" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="9.0.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.3" />
|
||||
<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>
|
||||
|
||||
</Project>
|
||||
|
@ -4,8 +4,8 @@ namespace Oqtane.Shared
|
||||
{
|
||||
public class Constants
|
||||
{
|
||||
public static readonly string Version = "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";
|
||||
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,6.1.1";
|
||||
public const string PackageId = "Oqtane.Framework";
|
||||
public const string ClientId = "Oqtane.Client";
|
||||
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 ErrorModule = "Oqtane.Modules.Admin.Error.{Action}, Oqtane.Client";
|
||||
|
||||
public const string AdminSiteTemplate = "Oqtane.SiteTemplates.AdminSiteTemplate, Oqtane.Server";
|
||||
public const string DefaultSiteTemplate = "Oqtane.SiteTemplates.DefaultSiteTemplate, Oqtane.Server";
|
||||
public const string AdminSiteTemplate = "Oqtane.Infrastructure.SiteTemplates.AdminSiteTemplate, 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" };
|
||||
|
||||
@ -46,7 +46,7 @@ namespace Oqtane.Shared
|
||||
public const string DefaultSite = "Default Site";
|
||||
|
||||
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 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 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
|
||||
|
||||
const string RoleObsoleteMessage = "Use the corresponding member from Oqtane.Shared.RoleNames";
|
||||
|
@ -3,7 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<OutputType>Exe</OutputType>
|
||||
<Version>6.1.0</Version>
|
||||
<Version>6.1.1</Version>
|
||||
<Product>Oqtane</Product>
|
||||
<Authors>Shaun Walker</Authors>
|
||||
<Company>.NET Foundation</Company>
|
||||
@ -11,7 +11,7 @@
|
||||
<Copyright>.NET Foundation</Copyright>
|
||||
<PackageProjectUrl>https://www.oqtane.org</PackageProjectUrl>
|
||||
<PackageLicenseUrl>https://github.com/oqtane/oqtane.framework/blob/dev/LICENSE</PackageLicenseUrl>
|
||||
<PackageReleaseNotes>https://github.com/oqtane/oqtane.framework/releases/tag/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>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<RootNamespace>Oqtane</RootNamespace>
|
||||
|
20
README.md
20
README.md
@ -1,4 +1,4 @@
|
||||
# Oqtane Framework
|
||||
# Oqtane Framewo
|
||||
|
||||

|
||||
|
||||
@ -12,15 +12,13 @@ Oqtane is being developed based on some fundamental principles which are outline
|
||||
|
||||
# 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.1](https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.1) was released on March 12, 2025 and is a maintenance release including 46 pull requests by 4 different contributors, pushing the total number of project commits all-time to over 6400. 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)
|
||||
|
||||
# Getting Started (Version 6.x)
|
||||
# Getting Started (Version 6.1.1)
|
||||
|
||||
**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.3 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**.
|
||||
|
||||
@ -86,9 +84,17 @@ Connect with other developers, get support, and share ideas by joining the Oqtan
|
||||
# Roadmap
|
||||
This project is open source, and therefore is a work in progress...
|
||||
|
||||
[6.1.1](https://github.com/oqtane/oqtane.framework/releases/tag/v6.1.1) (Mar 12, 2025)
|
||||
- [x] Stabilization improvements
|
||||
- [x] Cookie Consent Banner & Privacy/Terms
|
||||
|
||||
[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)
|
||||
- [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)
|
||||
- [x] Migration to .NET 9
|
||||
|
@ -1,5 +1,9 @@
|
||||
# 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
|
||||
|
||||
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.
|
||||
|
||||
* **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.
|
||||
|
||||
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.
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user