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 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">
<select id="cookieconsent" class="form-select" @bind="@_cookieconsent">
<option value="True">@SharedLocalizer["Yes"]</option>
<option value="False">@SharedLocalizer["No"]</option>
<option value="">@SharedLocalizer["Disabled"]</option>
<option value="optin">@Localizer["OptIn"]</option>
<option value="optout">@Localizer["OptOut"]</option>
</select>
</div>
</div>
@ -424,7 +425,7 @@
private string _themetype = "";
private string _containertype = "";
private string _admincontainertype = "";
private string _cookieconsent = "False";
private string _cookieconsent = "";
private Dictionary<string, string> _textEditors = new Dictionary<string, string>();
private string _textEditor = "";
@ -515,7 +516,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", "False");
_cookieconsent = SettingService.GetSetting(settings, "CookieConsent", string.Empty);
// functionality
var textEditors = ServiceProvider.GetServices<ITextEditor>();

View File

@ -427,9 +427,15 @@
<value>System</value>
</data>
<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 name="CookieConsent.Text" xml:space="preserve">
<value>Cookie Consent:</value>
</data>
<data name="OptIn" xml:space="preserve">
<value>Opt-In</value>
</data>
<data name="OptOut" xml:space="preserve">
<value>Opt-Out</value>
</data>
</root>

View File

@ -117,11 +117,11 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Apply" xml:space="preserve">
<value>Apply</value>
<data name="Confirm" xml:space="preserve">
<value>Confirm</value>
</data>
<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 name="Privacy" xml:space="preserve">
<value>Privacy</value>

View File

@ -16,10 +16,20 @@ namespace Oqtane.Services
private string ApiUrl => CreateApiUrl("CookieConsent");
/// <inheritdoc />
public async Task<bool> CanTrackAsync()
public async Task<bool> IsActionedAsync()
{
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()
@ -27,5 +37,11 @@ namespace Oqtane.Services
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;
}
}
}

View File

@ -9,16 +9,34 @@ namespace Oqtane.Services
/// </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();
Task<bool> CanTrackAsync(bool optOut);
/// <summary>
/// Grant cookie consent
/// 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();
}
}

View File

@ -5,30 +5,64 @@
@inject IJSRuntime JSRuntime
@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 {
private bool showBanner;
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; }
@ -36,21 +70,87 @@
[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()
{
showBanner = !(await CookieConsentService.CanTrackAsync());
_enabled = bool.Parse(SettingService.GetSetting(PageState.Site.Settings, "CookieConsent", "False"));
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 = 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))
{
var interop = new Interop(JSRuntime);
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 Guid RenderId { get; set; }
public bool Refresh { get; set; }
public bool AllowCookies { get; set; }
public List<Page> Pages
{