update the cookie consent control.
This commit is contained in:
parent
6dddd8eff8
commit
1ced5c0425
|
@ -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>();
|
||||||
|
|
|
@ -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>
|
|
@ -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>
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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";
|
||||||
|
|
Loading…
Reference in New Issue
Block a user