Skip to content

Commit

Permalink
Merge pull request #30 from KaufmannDigital/feature/decision-ttl
Browse files Browse the repository at this point in the history
  • Loading branch information
Nikdro authored Sep 2, 2021
2 parents c9920c7 + 3a9aa72 commit a1df8e8
Show file tree
Hide file tree
Showing 13 changed files with 159 additions and 59 deletions.
27 changes: 21 additions & 6 deletions Classes/Controller/JavaScriptController.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,18 @@ public function initializeRenderJavaScriptAction()
$this->response->setComponentParameter(SetHeaderComponent::class, 'Cache-Control', 'max-age=0, private, must-revalidate');
}


public function renderJavaScriptAction(array $dimensions = [])
{

try {
$filteredDimensions =
array_filter($dimensions, function($key) {
return in_array($key, $this->consentDimensions);
},
ARRAY_FILTER_USE_KEY);
array_filter(
$dimensions,
function ($key) {
return in_array($key, $this->consentDimensions);
},
ARRAY_FILTER_USE_KEY
);

$dimensionIdentifier = implode(
'_',
Expand All @@ -78,12 +81,24 @@ public function renderJavaScriptAction(array $dimensions = [])

$cacheIdentifier = 'kd_gdpr_cc_' . sha1(json_encode($consents) . $dimensionIdentifier . $siteNode->getIdentifier());

$q = new FlowQuery([$siteNode]);

$consentDate = new \DateTime(isset($cookie['consentDates'][$dimensionIdentifier]) ? $cookie['consentDates'][$dimensionIdentifier] : $cookie['consentDate']);
$cookieSettings = $q->find('[instanceof KaufmannDigital.GDPR.CookieConsent:Content.CookieSettings]')->get(0);
$decisionTtl = $cookieSettings->getProperty('decisionTtl') ?? 0;
$expireDate = clone $consentDate;
$expireDate->add(\DateInterval::createFromDateString($decisionTtl . ' seconds'));
if ($expireDate < new \DateTime('now')) {
$this->response->setContentType('text/javascript');
$this->response->setContent('');
return;
}

if ($this->cache->has($cacheIdentifier)) {
$this->redirect('downloadGeneratedJavaScript', null, null, ['hash' => $cacheIdentifier]);
return;
}

$q = new FlowQuery([$siteNode]);
$cookieNodes = $q->find('[instanceof KaufmannDigital.GDPR.CookieConsent:Content.Cookie][javaScriptCode != ""]')->sort('priority', 'DESC')->get();

$javaScript = '';
Expand Down
19 changes: 19 additions & 0 deletions Configuration/NodeTypes.Content.CookieSettings.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,22 @@
label: i18n
inspector:
group: cookieSettings
decisionTtl:
type: integer
defaultValue: 0
ui:
label: i18n
help:
message: i18n
inspector:
group: cookieSettings
closeButtonEnabled:
type: boolean
defaultValue: false
ui:
label: i18n
reloadIfChanged: true
help:
message: i18n
inspector:
group: cookieSettings
24 changes: 24 additions & 0 deletions Configuration/Settings.KaufmannDigital.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
KaufmannDigital:
GDPR:
CookieConsent:
siteCSSFilepath: null
customCSSFilepath: null
consentLogEnabled: false
consentDimensions: []
# See README.md
# - language
# - country
cookieName: 'KD_GDPR_CC'
cookieDomainName: null
siteStyles: []
# "rootNode name 1":
# siteCSSFilepath: <link to css>
# customCSSFilepath: <link to custom css>
# "rootNode name 2":
# siteCSSFilepath: <link to css>
# customCSSFilepath: <link to custom css>
backend:
includeGeneratedJs: true
excludeDocumentNodeTypes: []
# See README.md
# - 'Vendor.Package:Document.Imprint'
21 changes: 21 additions & 0 deletions Configuration/Settings.Neos.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
Neos:
Neos:
nodeTypes:
groups:
cookies:
position: 300
label: 'Cookies'
collapsed: true
fusion:
autoInclude:
KaufmannDigital.GDPR.CookieConsent: true
userInterface:
translation:
autoInclude:
'KaufmannDigital.GDPR.CookieConsent':
- 'NodeTypes/*'
Flow:
mvc:
routes:
KaufmannDigital.GDPR.CookieConsent:
position: 'before Neos.Neos'
43 changes: 0 additions & 43 deletions Configuration/Settings.yaml

This file was deleted.

Binary file added Documentation/Images/Decision_TTL.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,11 @@ To do this, you only need to edit the version date. You can find it in the inspe
After the date has been changed, the banner will be shown again to all visitors, who have submitted the cookie banner before this date. Old settings are used as presets.


#### Invalidating user-decisions after time
Sometimes it is necessary or advantageous to remind the user of his decision and ask him to confirm it again. For this purpose, a TTL for the decision can be set in the backend:
![decision-time setting](Documentation/Images/Decision_TTL.png)
The unit is seconds. After the set time has expired, the banner appears again with the default settings of the last decision, so the user can easily accept the old decision with just one click.
The value `0` (default) disables the repeated display of the banner.
### Styling
#### Custom Banner-Styles
The banner comes with a few basic-styles for positioning, which are getting included inline. To add your custom styles, just put a CSS-Files somewhere in your Resources-Folder and include it using Settings.yaml:
Expand Down
3 changes: 2 additions & 1 deletion Resources/Private/Fusion/Content/CookieSettings.fusion
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ prototype(KaufmannDigital.GDPR.CookieConsent:Content.CookieSettings) < prototype
}

hasNecessaryGroup = ${q(node).find('[instanceof KaufmannDigital.GDPR.CookieConsent:Content.NecessarySettingGroup]').count() > 0}
closeButtonEnabled = ${q(node).property('closeButtonEnabled')}

labels = Neos.Fusion:RawArray {
acceptAll = ${q(node).property('acceptAllButtonLabel')}
Expand Down Expand Up @@ -57,7 +58,7 @@ prototype(KaufmannDigital.GDPR.CookieConsent:Content.CookieSettings) < prototype
</div>
</div>
<div class="gdpr-cookieconsent-settings__close">
<KaufmannDigital.GDPR.CookieConsent:Component.Atom.CloseButton/>
<KaufmannDigital.GDPR.CookieConsent:Component.Atom.CloseButton @if.condition={props.closeButtonEnabled} />
</div>
</div>
<KaufmannDigital.GDPR.CookieConsent:Helper.GeneratedStyles/>
Expand Down
11 changes: 9 additions & 2 deletions Resources/Private/Fusion/Helper/JavaScriptSettings.fusion
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
prototype(KaufmannDigital.GDPR.CookieConsent:Helper.JavaScriptSettings) < prototype(Neos.Fusion:Tag) {
tagName = 'script'

@context.versionDate = ${q(site).find('[instanceof KaufmannDigital.GDPR.CookieConsent:Content.CookieSettings]').first().property("versionDate")}
cookieSettingsNode = ${q(site).find('[instanceof KaufmannDigital.GDPR.CookieConsent:Content.CookieSettings]').get(0)}
@context.versionDate = ${q(this.cookieSettingsNode).property('versionDate')}
@context.decisionTtl = ${q(this.cookieSettingsNode).property('decisionTtl')}

@context.apiUrl = Neos.Fusion:UriBuilder {
package = 'KaufmannDigital.GDPR.CookieConsent'
controller = 'Api'
Expand All @@ -18,13 +21,17 @@ prototype(KaufmannDigital.GDPR.CookieConsent:Helper.JavaScriptSettings) < protot
@context.cookieName = ${Configuration.setting('KaufmannDigital.GDPR.CookieConsent.cookieName')}
@context.cookieDomainName = ${Configuration.setting('KaufmannDigital.GDPR.CookieConsent.cookieDomainName')}

@context.nodeTypeDisabled = ${Array.indexOf(Configuration.setting('KaufmannDigital.GDPR.CookieConsent.excludeDocumentNodeTypes'), documentNode.nodeType.name) >= 0}

content = ${"
var KD_GDPR_CC = {
apiUrl: '" + apiUrl + "',
cookieName: '" + cookieName +"',
cookieDomainName: '" + cookieDomainName +"',
versionTimestamp: " + versionDate.timestamp * 1000 + ",
dimensionsIdentifier: '" + (dimensionsIdentifier != '' ? dimensionsIdentifier : 'default') + "'
decisionTtl: " + (decisionTtl ? (decisionTtl * 1000) : 0) + ",
dimensionsIdentifier: '" + (dimensionsIdentifier != '' ? dimensionsIdentifier : 'default') + "',
nodeTypeDisabled: " + (nodeTypeDisabled ? 'true' : 'false') +"
};
"}
@if.condition = ${node.context.inBackend == false}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,25 @@
<source>Version date</source>
<target state="final">Versions-Datum</target>
</trans-unit>

<trans-unit id="properties.decisionTtl" xml:space="preserve">
<source>TTL (Decision time in seconds)</source>
<target state="final">TTL (Speicherdauer der Entscheidung in Sekunden)</target>
</trans-unit>

<trans-unit id="properties.decisionTtl.ui.help.message" xml:space="preserve">
<source>Number of seconds until cookie settings are displayed and confirmed again. Example values are: 86400 (1 day), 604800 (1 week), 2592000 (30 days), 31536000 (1 year)</source>
<target state="final">Anzahl der Sekunden, bis die Cookie-Einstellungen erneut angezeigt und bestätigt werden. Beispielwerte sind: 86400 (1 Tag), 604800 (1 Woche), 2592000 (30 Tage), 31536000 (1 Jahr)</target>
</trans-unit>

<trans-unit id="properties.closeButtonEnabled" xml:space="preserve">
<source>Show close-button</source>
<target state="final">Zeige X (zum schließen)</target>
</trans-unit>

<trans-unit id="properties.closeButtonEnabled.ui.help.message" xml:space="preserve">
<source>Zeigt ein "X" in der oberen rechten Ecke, falls aktiviert. Ein klick auf dieses X schließt den Banner ohne Entscheidung. Er erscheint beim nächsten Seitenaufruf erneut.</source>
</trans-unit>
</body>
</file>
</xliff>
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,22 @@
<trans-unit id="properties.versionDate" xml:space="preserve">
<source>Version date</source>
</trans-unit>

<trans-unit id="properties.decisionTtl" xml:space="preserve">
<source>TTL (Decision time in seconds)</source>
</trans-unit>

<trans-unit id="properties.decisionTtl.ui.help.message" xml:space="preserve">
<source>Number of seconds until cookie settings are displayed and confirmed again. Example values are: 86400 (1 day), 604800 (1 week), 2592000 (30 days), 31536000 (1 year)</source>
</trans-unit>

<trans-unit id="properties.closeButtonEnabled" xml:space="preserve">
<source>Show close-button</source>
</trans-unit>

<trans-unit id="properties.closeButtonEnabled.ui.help.message" xml:space="preserve">
<source>Shows an "X" on the right top of the banner, if enabled. A click on the X hides the banner, but without making a decision. It re-appears on the next pageload.</source>
</trans-unit>
</body>
</file>
</xliff>
15 changes: 11 additions & 4 deletions Resources/Public/JavaScript/Initialize.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
function loadCookiebannerHtml() {
function loadCookiebannerHtml(openSettings = false) {
if (document.body.classList.contains('neos-backend')) return;
var xhr = new XMLHttpRequest();
xhr.addEventListener('load', function() {
Expand All @@ -10,15 +10,15 @@ function loadCookiebannerHtml() {
eval(scriptTags[n].innerHTML);
}
if (typeof initializeCookieConsent === 'function') {
initializeCookieConsent();
initializeCookieConsent(openSettings);
}
});

xhr.open('GET', KD_GDPR_CC.apiUrl);
xhr.send();
}

if (document.cookie.indexOf(KD_GDPR_CC.cookieName) >= 0) {
if (KD_GDPR_CC.nodeTypeDisabled === false && document.cookie.indexOf(KD_GDPR_CC.cookieName) >= 0) {
/*Cookie set*/
window.dataLayer = window.dataLayer || [];
var cookieObject = JSON.parse(
Expand All @@ -42,13 +42,20 @@ if (document.cookie.indexOf(KD_GDPR_CC.cookieName) >= 0) {
loadCookiebannerHtml();
}

//Re-Open Cookie-Consent, if TTL is expired
var decisionExpiry = cookieConsentDate.getTime() + KD_GDPR_CC.decisionTtl;
if (KD_GDPR_CC.decisionTtl > 0 && decisionExpiry < new Date()) {
loadCookiebannerHtml(true);
}


window.dataLayer.push({
event: 'KD_GDPR_CC_consent',
KD_GDPR_CC: {
consents: cookieObject.consents,
},
});
} else if (document.getElementsByClassName('gdpr-cookieconsent-settings').length === 0 && window.neos === undefined) {
} else if (KD_GDPR_CC.nodeTypeDisabled === false && document.getElementsByClassName('gdpr-cookieconsent-settings').length === 0 && window.neos === undefined) {
/*No Cookie set, not in backend & not on cookie page*/
loadCookiebannerHtml();
}
Expand Down
15 changes: 12 additions & 3 deletions Resources/Public/JavaScript/Main.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
});
})([Element.prototype, CharacterData.prototype, DocumentType.prototype]);

function initializeCookieConsent() {
function initializeCookieConsent(openSettings = false) {

var kd_gdpr_cc_userid;
var cookieSettingsContainer = document.querySelector('.gdpr-cookieconsent-settings');
Expand Down Expand Up @@ -56,6 +56,10 @@ function initializeCookieConsent() {
btnAcceptAll.style.display = 'none';
btnSaveSettings.style.display = 'block';
});
if (openSettings) {
let clickEvent = new MouseEvent('click');
btnIndividualSettingsEnable.dispatchEvent(clickEvent);
}

btnIndividualSettingsDisable.addEventListener('click', function() {
individualSettingsContainer.style.display = 'none';
Expand Down Expand Up @@ -190,7 +194,11 @@ function dispatchEventsForCookies(inputs) {
function saveConsentToCookie(inputs, userId) {
var cookie = decodeCookie();
var currentDate = new Date();
var expireDate = new Date(currentDate.getFullYear() + 1, currentDate.getMonth(), currentDate.getDate());
if (KD_GDPR_CC.decisionTtl && KD_GDPR_CC.decisionTtl > 0) {
expireDate = new Date(currentDate.getTime() + KD_GDPR_CC.decisionTtl);
} else {
var expireDate = new Date(currentDate.getFullYear() + 1, currentDate.getMonth(), currentDate.getDate());
}

var consents = cookie && cookie.consents ? cookie.consents : {};
consents[KD_GDPR_CC.dimensionsIdentifier] = [];
Expand All @@ -201,6 +209,7 @@ function saveConsentToCookie(inputs, userId) {
var consentDates = cookie && cookie.consentDates ? cookie.consentDates : {};
consentDates[KD_GDPR_CC.dimensionsIdentifier] = currentDate.toUTCString();


var cookieData = {
userId: userId,
consents: consents,
Expand All @@ -209,7 +218,7 @@ function saveConsentToCookie(inputs, userId) {
expireDate: expireDate.toUTCString()
};

var cookieParams = encodeURI(JSON.stringify(cookieData)) + "; expires=" + expireDate.toUTCString() + "; path=/; " + (KD_GDPR_CC.cookieDomainName ? ('domain=' + KD_GDPR_CC.cookieDomainName + ';') : '') +" Secure;";
var cookieParams = encodeURI(JSON.stringify(cookieData)) + "; expires=" + new Date(currentDate.getTime() + 315360000000).toUTCString() + "; path=/; " + (KD_GDPR_CC.cookieDomainName ? ('domain=' + KD_GDPR_CC.cookieDomainName + ';') : '') +" Secure;";
document.cookie = KD_GDPR_CC.cookieName + "=" + cookieParams;

window.dataLayer = window.dataLayer || [];
Expand Down

0 comments on commit a1df8e8

Please sign in to comment.