update the cookie consent control.

This commit is contained in:
Ben 2025-02-28 23:32:17 +08:00
parent 6dddd8eff8
commit 1ced5c0425
16 changed files with 322 additions and 134 deletions

View File

@ -127,11 +127,12 @@
</div> </div>
</div> </div>
<div class="row mb-1 align-items-center"> <div class="row mb-1 align-items-center">
<Label Class="col-sm-3" For="cookieconsent" HelpText="Specify if cookie consent is enabled on this site" ResourceKey="CookieConsent">Cookie Consent: </Label> <Label Class="col-sm-3" For="cookieconsent" HelpText="Specify if cookie consent is enabled on this site. Please make sure your using theme supports Cookie Consent when enable this option." ResourceKey="CookieConsent">Cookie Consent: </Label>
<div class="col-sm-9"> <div class="col-sm-9">
<select id="cookieconsent" class="form-select" @bind="@_cookieconsent"> <select id="cookieconsent" class="form-select" @bind="@_cookieconsent">
<option value="True">@SharedLocalizer["Yes"]</option> <option value="">@SharedLocalizer["Disabled"]</option>
<option value="False">@SharedLocalizer["No"]</option> <option value="optin">@Localizer["OptIn"]</option>
<option value="optout">@Localizer["OptOut"]</option>
</select> </select>
</div> </div>
</div> </div>
@ -424,7 +425,7 @@
private string _themetype = ""; private string _themetype = "";
private string _containertype = ""; private string _containertype = "";
private string _admincontainertype = ""; private string _admincontainertype = "";
private string _cookieconsent = "False"; private string _cookieconsent = "";
private Dictionary<string, string> _textEditors = new Dictionary<string, string>(); private Dictionary<string, string> _textEditors = new Dictionary<string, string>();
private string _textEditor = ""; private string _textEditor = "";
@ -515,7 +516,7 @@
_containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype); _containers = ThemeService.GetContainerControls(PageState.Site.Themes, _themetype);
_containertype = (!string.IsNullOrEmpty(site.DefaultContainerType)) ? site.DefaultContainerType : Constants.DefaultContainer; _containertype = (!string.IsNullOrEmpty(site.DefaultContainerType)) ? site.DefaultContainerType : Constants.DefaultContainer;
_admincontainertype = (!string.IsNullOrEmpty(site.AdminContainerType)) ? site.AdminContainerType : Constants.DefaultAdminContainer; _admincontainertype = (!string.IsNullOrEmpty(site.AdminContainerType)) ? site.AdminContainerType : Constants.DefaultAdminContainer;
_cookieconsent = SettingService.GetSetting(settings, "CookieConsent", "False"); _cookieconsent = SettingService.GetSetting(settings, "CookieConsent", string.Empty);
// functionality // functionality
var textEditors = ServiceProvider.GetServices<ITextEditor>(); var textEditors = ServiceProvider.GetServices<ITextEditor>();

View File

@ -427,9 +427,15 @@
<value>System</value> <value>System</value>
</data> </data>
<data name="CookieConsent.HelpText" xml:space="preserve"> <data name="CookieConsent.HelpText" xml:space="preserve">
<value>Specify if cookie consent is enabled on this site</value> <value>Specify if cookie consent is enabled on this site. Please make sure your using theme supports Cookie Consent when enable this option.</value>
</data> </data>
<data name="CookieConsent.Text" xml:space="preserve"> <data name="CookieConsent.Text" xml:space="preserve">
<value>Cookie Consent:</value> <value>Cookie Consent:</value>
</data> </data>
<data name="OptIn" xml:space="preserve">
<value>Opt-In</value>
</data>
<data name="OptOut" xml:space="preserve">
<value>Opt-Out</value>
</data>
</root> </root>

View File

@ -117,11 +117,11 @@
<resheader name="writer"> <resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<data name="Apply" xml:space="preserve"> <data name="Confirm" xml:space="preserve">
<value>Apply</value> <value>Confirm</value>
</data> </data>
<data name="ConsentDescription" xml:space="preserve"> <data name="ConsentDescription" xml:space="preserve">
<value>By clicking "Accept", you agree us to use cookies to ensure you get the best experience on our website.</value> <value>I agree to using cookies to provide the best user experience for this site.</value>
</data> </data>
<data name="Privacy" xml:space="preserve"> <data name="Privacy" xml:space="preserve">
<value>Privacy</value> <value>Privacy</value>

View File

@ -16,10 +16,20 @@ namespace Oqtane.Services
private string ApiUrl => CreateApiUrl("CookieConsent"); private string ApiUrl => CreateApiUrl("CookieConsent");
/// <inheritdoc /> public async Task<bool> IsActionedAsync()
public async Task<bool> CanTrackAsync()
{ {
return await GetJsonAsync<bool>($"{ApiUrl}/CanTrack"); 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() public async Task<string> CreateConsentCookieAsync()
@ -27,5 +37,11 @@ namespace Oqtane.Services
var cookie = await GetStringAsync($"{ApiUrl}/CreateConsentCookie"); var cookie = await GetStringAsync($"{ApiUrl}/CreateConsentCookie");
return cookie ?? string.Empty; return cookie ?? string.Empty;
} }
public async Task<string> WithdrawConsentCookieAsync()
{
var cookie = await GetStringAsync($"{ApiUrl}/WithdrawConsentCookie");
return cookie ?? string.Empty;
}
} }
} }

View File

@ -9,16 +9,34 @@ namespace Oqtane.Services
/// </summary> /// </summary>
public interface ICookieConsentService public interface ICookieConsentService
{ {
/// <summary>
/// Get cookie consent bar actioned status
/// </summary>
/// <returns></returns>
Task<bool> IsActionedAsync();
/// <summary> /// <summary>
/// Get cookie consent status /// Get cookie consent status
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
Task<bool> CanTrackAsync(); Task<bool> CanTrackAsync(bool optOut);
/// <summary> /// <summary>
/// Grant cookie consent /// create actioned cookie
/// </summary>
/// <returns></returns>
Task<string> CreateActionedCookieAsync();
/// <summary>
/// create consent cookie
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
Task<string> CreateConsentCookieAsync(); Task<string> CreateConsentCookieAsync();
/// <summary>
/// widhdraw consent cookie
/// </summary>
/// <returns></returns>
Task<string> WithdrawConsentCookieAsync();
} }
} }

View File

@ -5,30 +5,64 @@
@inject IJSRuntime JSRuntime @inject IJSRuntime JSRuntime
@inject IStringLocalizer<CookieConsent> Localizer @inject IStringLocalizer<CookieConsent> Localizer
@if (_enabled && !Hidden && showBanner) @if (_enabled && !Hidden)
{ {
<form method="post" @formname="CookieConsentForm" @onsubmit="async () => await AcceptPolicy()" data-enhance>
<input type="hidden" name="@Constants.RequestVerificationToken" value="@SiteState.AntiForgeryToken" />
<div class="gdpr-consent-bar bg-light text-dark p-3 fixed-bottom">
<div class="container-fluid d-flex justify-content-between align-items-center">
<div>
@((MarkupString)Convert.ToString(Localizer["ConsentDescription"]))
</div>
<div>
<button class="btn btn-primary" type="submit">@((MarkupString)Convert.ToString(Localizer["Apply"]))</button>
@if (ShowPrivacyLink)
{
<a class="btn btn-secondary ms-2" href="/privacy" target="_blank">@((MarkupString)Convert.ToString(Localizer["Privacy"]))</a>
}
</div>
</div>
</div>
</form> <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 d-flex justify-content-between align-items-center">
<div>
@((MarkupString)Convert.ToString(Localizer["ConsentDescription"]))
@if (PageState.RenderMode == RenderModes.Static)
{
<input type="checkbox" name="cantrack" checked="@_canTrack" value="1" class="form-check-input ms-2" />
}
else
{
<input type="checkbox" name="cantrack" @bind="@_canTrack" value="1" class="form-check-input ms-2" />
}
</div>
<div>
<button class="btn btn-primary" type="submit">@((MarkupString)Convert.ToString(Localizer["Confirm"]))</button>
@if (ShowPrivacyLink)
{
<a class="btn btn-secondary ms-2" href="/privacy" target="_blank">@((MarkupString)Convert.ToString(Localizer["Privacy"]))</a>
}
</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 { @code {
private bool showBanner; private bool _showBanner;
private bool _enabled; private bool _enabled;
private bool _optout;
private bool _actioned;
private bool _canTrack;
private bool _consentPostback;
private bool _togglePostback;
[Parameter] [Parameter]
public bool Hidden { get; set; } public bool Hidden { get; set; }
@ -36,21 +70,87 @@
[Parameter] [Parameter]
public bool ShowPrivacyLink { get; set; } = true; 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() protected override async Task OnInitializedAsync()
{ {
showBanner = !(await CookieConsentService.CanTrackAsync()); var cookieConsentSetting = SettingService.GetSetting(PageState.Site.Settings, "CookieConsent", string.Empty);
_enabled = bool.Parse(SettingService.GetSetting(PageState.Site.Settings, "CookieConsent", "False")); _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() private async Task AcceptPolicy()
{ {
var cookieString = await CookieConsentService.CreateConsentCookieAsync(); var cookieString = string.Empty;
if(_optout)
{
cookieString = _canTrack ? await CookieConsentService.WithdrawConsentCookieAsync() : await CookieConsentService.CreateConsentCookieAsync();
}
else
{
cookieString = _canTrack ? await CookieConsentService.CreateConsentCookieAsync() : await CookieConsentService.WithdrawConsentCookieAsync();
}
if (!string.IsNullOrEmpty(cookieString)) if (!string.IsNullOrEmpty(cookieString))
{ {
var interop = new Interop(JSRuntime); var interop = new Interop(JSRuntime);
await interop.SetCookieString(cookieString); await interop.SetCookieString(cookieString);
showBanner = false; _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();
} }
} }
} }

View File

@ -27,6 +27,7 @@ namespace Oqtane.UI
public bool IsInternalNavigation { get; set; } public bool IsInternalNavigation { get; set; }
public Guid RenderId { get; set; } public Guid RenderId { get; set; }
public bool Refresh { get; set; } public bool Refresh { get; set; }
public bool AllowCookies { get; set; }
public List<Page> Pages public List<Page> Pages
{ {

View File

@ -31,6 +31,8 @@
@inject IUrlMappingRepository UrlMappingRepository @inject IUrlMappingRepository UrlMappingRepository
@inject IVisitorRepository VisitorRepository @inject IVisitorRepository VisitorRepository
@inject IJwtManager JwtManager @inject IJwtManager JwtManager
@inject ICookieConsentService CookieConsentService
@inject ISettingService SettingService
@if (_initialized) @if (_initialized)
{ {
@ -107,6 +109,7 @@
private string _styleSheets = ""; private string _styleSheets = "";
private string _scripts = ""; private string _scripts = "";
private string _message = ""; private string _message = "";
private bool _allowCookies;
private PageState _pageState; private PageState _pageState;
// CascadingParameter is required to access HttpContext // CascadingParameter is required to access HttpContext
@ -140,6 +143,9 @@
_prerender = site.Prerender; _prerender = site.Prerender;
_fingerprint = site.Fingerprint; _fingerprint = site.Fingerprint;
var cookieConsentSettings = SettingService.GetSetting(site.Settings, "CookieConsent", string.Empty);
_allowCookies = string.IsNullOrEmpty(cookieConsentSettings) || await CookieConsentService.CanTrackAsync(cookieConsentSettings == "optout");
var modules = new List<Module>(); var modules = new List<Module>();
Route route = new Route(url, alias.Path); Route route = new Route(url, alias.Path);
@ -170,7 +176,7 @@
modules = await SiteService.GetModulesAsync(site.SiteId, page.PageId); modules = await SiteService.GetModulesAsync(site.SiteId, page.PageId);
} }
if (site.VisitorTracking) if (site.VisitorTracking && _allowCookies)
{ {
TrackVisitor(site.SiteId); TrackVisitor(site.SiteId);
} }
@ -245,7 +251,8 @@
ReturnUrl = "", ReturnUrl = "",
IsInternalNavigation = false, IsInternalNavigation = false,
RenderId = Guid.NewGuid(), RenderId = Guid.NewGuid(),
Refresh = true Refresh = true,
AllowCookies = _allowCookies
}; };
} }
else else

View File

@ -19,10 +19,22 @@ namespace Oqtane.Controllers
_cookieConsentService = cookieConsentService; _cookieConsentService = cookieConsentService;
} }
[HttpGet("CanTrack")] [HttpGet("IsActioned")]
public async Task<bool> CanTrack() public async Task<bool> IsActioned()
{ {
return await _cookieConsentService.CanTrackAsync(); 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")] [HttpGet("CreateConsentCookie")]
@ -30,5 +42,11 @@ namespace Oqtane.Controllers
{ {
return await _cookieConsentService.CreateConsentCookieAsync(); return await _cookieConsentService.CreateConsentCookieAsync();
} }
[HttpGet("WithdrawConsentCookie")]
public async Task<string> WithdrawConsentCookie()
{
return await _cookieConsentService.WithdrawConsentCookieAsync();
}
} }
} }

View File

@ -55,8 +55,5 @@ namespace Oqtane.Extensions
public static IApplicationBuilder UseExceptionMiddleWare(this IApplicationBuilder builder) public static IApplicationBuilder UseExceptionMiddleWare(this IApplicationBuilder builder)
=> builder.UseMiddleware<ExceptionMiddleware>(); => builder.UseMiddleware<ExceptionMiddleware>();
public static IApplicationBuilder UseCookieConsent(this IApplicationBuilder builder)
=> builder.UseMiddleware<CookieConsentMiddleware>();
} }
} }

View File

@ -1,71 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Oqtane.Services;
using Oqtane.Shared;
namespace Oqtane.Infrastructure
{
internal class CookieConsentMiddleware
{
private readonly IList<string> _defaultEssentialCookies = new List<string>
{
".AspNetCore.Culture",
"X-XSRF-TOKEN-COOKIE",
".AspNetCore.Identity.Application"
};
private readonly RequestDelegate _next;
public CookieConsentMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
// check if framework is installed
var config = context.RequestServices.GetService(typeof(IConfigManager)) as IConfigManager;
var settingService = context.RequestServices.GetService(typeof(ISettingService)) as ISettingService;
var cookieConsentService = context.RequestServices.GetService(typeof(ICookieConsentService)) as ICookieConsentService;
string path = context.Request.Path.ToString();
if (config.IsInstalled())
{
try
{
var settings = (Dictionary<string, string>)context.Items[Constants.HttpContextSiteSettingsKey];
if (settings != null)
{
var cookieConsentEnabled = bool.Parse(settingService.GetSetting(settings, "CookieConsent", "False"));
if (cookieConsentEnabled && !await cookieConsentService.CanTrackAsync())
{
//only allow essential cookies when consent is not granted
var loginCookieName = settingService.GetSetting(settings, "LoginOptions:CookieName", ".AspNetCore.Identity.Application");
var cookiesSetting = settingService.GetSetting(settings, "EssentialCookies", string.Empty);
var essentialCookies = !string.IsNullOrEmpty(cookiesSetting) ? cookiesSetting.Split(",").ToList() : _defaultEssentialCookies;
foreach (var cookie in context.Request.Cookies)
{
if (cookie.Key != loginCookieName && !essentialCookies.Contains(cookie.Key))
{
context.Response.Cookies.Delete(cookie.Key);
}
}
}
}
}
catch(Exception ex)
{
}
}
// continue processing
if (_next != null) await _next(context);
}
}
}

View File

@ -1,10 +1,13 @@
using System; using System;
using System.Diagnostics.Contracts; using System.Diagnostics.Contracts;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Localization; using Microsoft.AspNetCore.Localization;
using Microsoft.Extensions.Options;
using Oqtane.Documentation; using Oqtane.Documentation;
using Oqtane.Shared;
namespace Oqtane.Services namespace Oqtane.Services
{ {
@ -12,27 +15,106 @@ namespace Oqtane.Services
public class ServerCookieConsentService : ICookieConsentService public class ServerCookieConsentService : ICookieConsentService
{ {
private readonly IHttpContextAccessor _accessor; private readonly IHttpContextAccessor _accessor;
private readonly CookiePolicyOptions _cookiePolicyOptions;
public ServerCookieConsentService(IHttpContextAccessor accessor) public ServerCookieConsentService(IHttpContextAccessor accessor, IOptions<CookiePolicyOptions> cookiePolicyOptions)
{ {
_accessor = accessor; _accessor = accessor;
_cookiePolicyOptions = cookiePolicyOptions.Value;
} }
public Task<bool> CanTrackAsync() public Task<bool> IsActionedAsync()
{ {
var consentFeature = _accessor.HttpContext?.Features.Get<ITrackingConsentFeature>(); var actioned = false;
var canTrack = consentFeature?.CanTrack ?? true; 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); return Task.FromResult(canTrack);
} }
public Task<string> CreateActionedCookieAsync()
{
var cookieString = CreateCookieString(false, string.Empty);
return Task.FromResult(cookieString);
}
public Task<string> CreateConsentCookieAsync() public Task<string> CreateConsentCookieAsync()
{ {
var consentFeature = _accessor.HttpContext?.Features.Get<ITrackingConsentFeature>(); var cookieString = CreateCookieString(true, Constants.CookieConsentCookieValue);
consentFeature?.GrantConsent(); return Task.FromResult(cookieString);
var cookie = consentFeature?.CreateConsentCookie() ?? string.Empty; }
return Task.FromResult(cookie); 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;
} }
} }
} }

View File

@ -169,13 +169,6 @@ namespace Oqtane
options.CustomSchemaIds(type => type.ToString()); // Handle SchemaId already used for different type options.CustomSchemaIds(type => type.ToString()); // Handle SchemaId already used for different type
}); });
services.TryAddSwagger(_useSwagger); services.TryAddSwagger(_useSwagger);
//add cookie consent policy
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.ConsentCookieValue = Constants.CookieConsentCookieValue;
});
} }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
@ -232,8 +225,6 @@ namespace Oqtane
app.UseAuthentication(); app.UseAuthentication();
app.UseAuthorization(); app.UseAuthorization();
app.UseAntiforgery(); app.UseAntiforgery();
app.UseCookiePolicy();
app.UseCookieConsent();
if (_useSwagger) if (_useSwagger)
{ {

View File

@ -121,6 +121,16 @@
color: white; color: white;
} }
/* cookie consent */
.gdpr-consent-bar .btn-show{
bottom: 0;
right: 5px;
}
.gdpr-consent-bar .btn-hide{
top: 0;
right: 5px;
}
@media (max-width: 767.98px) { @media (max-width: 767.98px) {
.main .top-row { .main .top-row {
display: none; display: none;

View File

@ -100,6 +100,16 @@ div.app-moduleactions a.dropdown-toggle, div.app-moduleactions div.dropdown-menu
z-index: 1000; z-index: 1000;
} }
/* cookie consent */
.gdpr-consent-bar .btn-show{
bottom: 0;
right: 5px;
}
.gdpr-consent-bar .btn-hide{
top: 0;
right: 5px;
}
@media (max-width: 767.98px) { @media (max-width: 767.98px) {
.app-menu { .app-menu {

View File

@ -91,7 +91,9 @@ namespace Oqtane.Shared
public const string BootstrapStylesheetUrl = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css"; public const string BootstrapStylesheetUrl = "https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.3.3/css/bootstrap.min.css";
public const string BootstrapStylesheetIntegrity = "sha512-jnSuA4Ss2PkkikSOLtYs8BlYIeeIK1h99ty4YfvRPAlzr377vr3CXDb7sb7eEEBYjDtcYj+AjBH3FLv5uSJuXg=="; public const string BootstrapStylesheetIntegrity = "sha512-jnSuA4Ss2PkkikSOLtYs8BlYIeeIK1h99ty4YfvRPAlzr377vr3CXDb7sb7eEEBYjDtcYj+AjBH3FLv5uSJuXg==";
public const string CookieConsentCookieValue = "true"; public const string CookieConsentCookieName = "Oqtane.CookieConsent";
public const string CookieConsentCookieValue = "yes";
public const string CookieConsentActionCookieValue = "yes";
// Obsolete constants // Obsolete constants
const string RoleObsoleteMessage = "Use the corresponding member from Oqtane.Shared.RoleNames"; const string RoleObsoleteMessage = "Use the corresponding member from Oqtane.Shared.RoleNames";