sandstorm/cookiepunch
composer require sandstorm/cookiepunch
Block elements like iframes and scripts before the markup reaches the browser and provide a dialog in the browser to enable them again.
v4.4.2
Sandstorm.CookiePunch
This Neos package provides a general approach for blocking elements like script tags and iframes before the markup reaches the browser and therefore provides a general approach for blocking cookies or other concepts of tracking user behaviour. It integrates Klaro as UI for displaying a cookie-consent and unblocking groups of elements after the user consented.
Features
- eel helpers to block elements (scripts and iframes) before the markup is sent to the client
- eel helper to place contextual consents for any part of the markup
- a easy way to configure blocking via yaml config supporting patterns to target elements in markup
- localization support via Yaml and/or Fusion
- data source providing all services e.g. as a dropdown in the inspector
- an awesome cookie-consent provided by Klaro
❤️ directly bundled with this package- supports unblocking of elements
- supports contextual consents to temporarily/permanently unblock content by consenting directly on the element without the need to open the consent modal
- You definitely need to check out their project on GitHub ;)
Installation
composer require sandstorm/cookiepunch
Basic Configuration and Usages
Step 1: Adding the consent-modal
Create a new fusion file CookiePunch.fusion
in .../Resources/Private/Fusion
with the following content:
prototype(Neos.Neos:Page) {
head.javascripts.cookiepunchConsent = Sandstorm.CookiePunch:Consent
# Block Global
@process.blockIframes = ${CookiePunch.blockIframes(value, !node.context.inBackend)}
@process.blockScripts = ${CookiePunch.blockScripts(value, !node.context.inBackend)}
}
This will add the needed js and css to your page. If you reload the page you should see the consent-modal. Now all <iframe>
and <script>
tags will be
blocked. !node.context.inBackend
disables blocking in the Neos backend.
Open or reload your page. If blocking works your page should look broken, sorry for that ;)
You can open the console of your browser inspector and type klaro.show()
to verify that klaro is working. In the next steps we will configure purposes
and services
that will
show up in the consent modal.
Let's start unbreaking your page;)
Step 2: Never block your own javascript
You might have some scripts that you never want to be blocked because your page would not be usable at all. They are often called main.js
, app.js
, index.js
, ...
renderer = Neos.Fusion:Tag {
tagName = "script"
attrbutes.src = "resource://Vendor.Example/Public/JavaScript/index.js"
@process.neverBlock = ${CookiePunch.neverBlockScripts(value)}
}
renderer = afx`
<script src={props.src} type="application/javascript" @process.neverBlock={CookiePunch.neverBlockScripts(value)}></script>
`
You could also use the technique described in the next step to never block script however for scripts that are technically needed by the site the eel helper can be used in a more flexible way and adds more semantics to your code. "Hey, I checked this part of the code and it should not be blocked!".
Step 3: Blocking via yaml config
In Configuration/
create a Settings.CookiePunch.yaml
.
HINT: Add the schema.json
file from this package to your IDE and select it for Settings.CookiePunch.yaml
. This will make it easier and give you auto-completion when configuring CookiePunch.
Sandstorm:
CookiePunch:
consent:
purposes:
mediaembeds: Media Embeds
services:
anchor:
title: Anchor FM
description: Podcast Player
purposes:
- mediaembeds
blocking:
tagPatterns:
script:
"Packages/Neos.Neos":
block: false
"Packages/Unikka.Slick":
block: false
iframe:
"https://anchor.fm":
service: anchor
This config is split in two parts.
consent
will directly be used to configure what is shown in the klaro consent. Klaro differentiates between purposes
and services
. A purpose
is a group for
multiple services
. Check the consent to see how changes to the config are reflected in the browser.
blocking
is used to find tags by a pattern, e.g. "Packages/Unikka.Slick"
and then block them in the backend by "breaking" the tag. Instead of block: ...
you can define a
service service: myservice
. This way klaro can unblock the content once the user gives his consent.
Matching tags with patterns
Blocking patterns are configured for a tag. We currently support bocking <script>
and <iframe>
tags.
Let's look at the Packages/Neos.Neos
pattern from the example above. This pattern will match all the following tags:
<script src="https://raw.githubusercontent.com/sandstorm/Sandstorm.CookiePunch/master//foo/bar/Packages/Neos.Neos/baz/index.js"/>
<script data-foo="Neos.Neos"/>
<script Neos.Neosisawesome src="https://raw.githubusercontent.com/sandstorm/Sandstorm.CookiePunch/master//some/source/main.js"/>
The tag is a string that matches if it contains the given pattern. You could also match for foo
or anything else in this string. Internally strpos()
is used.
This pattern will never be blocked:
"Packages/Neos.Neos":
block: false
This pattern will always be blocked and cannot be unblocked by the consent:
Might be useful if the editor can place any html content and you want to always block certain tags.
"https://really-stuff.bad":
block: true
This pattern will be blocked and can be unblocked by the user using the consent:
"https://anchor.fm":
service: anchor
Step 4: Providing a link to your privacy statement
The default url is /privacy
. This can be changed.
via the config as a string
Sandstorm:
CookiePunch:
consent:
privacyPolicyUrl: /different-privacy
Via the config using an XLF file
Sandstorm:
CookiePunch:
consent:
privacyPolicyUrl: Vendor.Site:Main:privacyPolicyUrl
Via fusion overriding the config prototype
prototype(Sandstorm.CookiePunch:Config) {
consent.privacyPolicyUrl = Neos.Neos:NodeUri {
node = ${q(site).find('[instanceof Vendor.Site:PrivacyPage]').get(0)}
}
}
prototype(Sandstorm.CookiePunch:Config) {
consent.privacyPolicyUrl = ${q(site).find('[instanceof Vendor.Site:Homepage]').property("privacyPolicyUrl")}
consent.privacyPolicyUrl.@process.convert = Neos.Neos:ConvertUris
}
Check the consent modal in the browser if the link is present!
Step 5: Styling
Via yaml config (klaro css variables)
A full yaml config can be found in Examples/Settings.CookiePunch.Styling.yaml
Sandstorm:
CookiePunch:
consent:
styling:
font-family: "Work Sans,Helvetica Neue,Helvetica,Arial,sans-serif"
green1: "red"
green2: "green"
green3: "blue"
border-radius: "0"
Manual styling
Keep in mind that this might break when updating CookiePunch (which will also update klaro.js).
First we have to disable css completely noCSS = true
prototype(Neos.Neos:Page) {
head.javascripts.cookiepunchConsent = Sandstorm.CookiePunch:Consent {
noCSS = true
}
# Block Global
@process.blockIframes = ${CookiePunch.blockIframes(value, !node.context.inBackend)}
@process.blockScripts = ${CookiePunch.blockScripts(value, !node.context.inBackend)}
}
Now no styling is applied to the consent UI. All styling has to be done by you. To make this easier you can find the original klaro styles
here: Resources/Private/KlaroCss/klaro.css
.
Step 6: Let the user open the consent modal later
You simply need to call klaro.show()
. Below is an example of how to create a link that will open the modal.
<a
href=""
onclick="(function(e){e.preventDefault();klaro.show()})(arguments[0])"
>Cookie Settings</a
>
For Neos you might want to create a NodeType that can be placed e.g. between the sections of your data security statement. If you are using Neos:NodeTypes
you can place a HTMl element with the
corresponding markup.
Using the link editor in Neos will not work as it does not support setting the onclick
attribute.
Advanced Usages
Full list of consent options
see annotated yaml of consent options
Most of the inline comments are directly copied from the annotated config.js of klaro for your convenience ;)
Full list of service options
see annotated yaml of service options
Most of the inline comments are directly copied from the annotated config.js of klaro for your convenience ;)
Blocking a rendered fusion subtree
An already blocked markup will not be blocked again when running the eel-helpers for Neos.Neos:Page
.
This means we can hook into e.g. plugins to block them and attach them to a service in the consent modal.
This technique especially comes in handy for blocking inline <script>...</script>
tags that cannot be matched by a pattern.
// Plugin Implementation Example
prototype(Vendor.Plugin.FooTube:Embed) < prototype(Neos.Fusion:Component) {
renderer = afx`
<div>
<iframe src="https://raw.githubusercontent.com/sandstorm/Sandstorm.CookiePunch/master/..."></iframe>
<script type="text/javascript">...</script>
</div>
`
}
// CookiePunch.fusion
prototype(Vendor.Plugin.FooTube:Embed) {
// tags in this part of the tree will be blocked first
@process.blockIframes = ${CookiePunch.blockIframes(value, !node.context.inBackend, "footube")}
@process.blockScripts = ${CookiePunch.blockScripts(value, !node.context.inBackend, "footube")}
}
prototype(Neos.Neos:Page) {
head.javascripts.cookiepunchConsent = Sandstorm.CookiePunch:Consent
// at last all remaining tags will be blocked according to the config
@process.blockIframes = ${CookiePunch.blockIframes(value, !node.context.inBackend)}
@process.blockScripts = ${CookiePunch.blockScripts(value, !node.context.inBackend)}
}
Adding a contextual consent for non-iframe element
When blocking a <script>
tag you might end up with a broken UI as some styles might not be applied and some markup might not be created.
You can use the following eel-helper to wrap parts of the rendered fusion tree so klaro can "replace" the broken content and add
a contextual consent.
// CookiePunch.fusion
prototype(Vendor.Plugin.FooTube:Embed) {
@process.addContextualConsent = ${CookiePunch.addContextualConsent("footube", value, !node.context.inBackend)}
}
Let the editor choose a service from the inspector
If you allow your editors to place HTML (e.g. if you are using Neos.NodeTypes.Html:Html
node type) the editor
can place markup that potentially sets cookies. With the default configuration CookiePunch will block this content.
This content cannot be unblocked if the markup is not matched by any pattern in the yaml config.
You can add Sandstorm.CookiePunch:Mixin.ConsentServices
to your NodeTypes.yaml to get a dropdown in the inspector.
"Neos.NodeTypes.Html:Html":
superTypes:
"Sandstorm.CookiePunch:Mixin.ConsentServices": true
You also need to add this for the actual blocking.
prototype(Neos.NodeTypes.Html:Html) {
@process.blockIframes = ${CookiePunch.blockIframes(value, !node.context.inBackend, q(node).property("consentServices"))}
@process.blockScripts = ${CookiePunch.blockScripts(value, !node.context.inBackend, q(node).property("consentServices"))}
}
Let the editor change the text of the consent
You can override the corresponding path in the fusion prototype Sandstorm.CookiePunch:Config.Translations
with
the text property of a content node. If the property contains markup you need to change the config of the consent.
Sandstorm:
CookiePunch:
consent:
# Setting this to true will render the descriptions of the consent
# modal and consent notice are HTML. Use with care.
htmlTexts: true
Translations
Klaro already provides translations for many languages. These are made available as XLIFF files in Resources/Private/Translations
.
You can override translations
- by creating you own XLIFF files overriding the default ones
- in the yaml config by providing/overriding a translation key instead of the actual text ( e.g. for the title of service )
- by overriding the corresponding path in the fusion prototypes
Sandstorm.CookiePunch:Config.Translations
orSandstorm.CookiePunch:Config
Troubleshooting
iframes work after unblocking but are the wrong size or in the wrong place
Please check
- Do you have an iframe that you blocked because it adds cookies?
- Do you have a Js that manipulates this iframe?
- Is this Js not blocked while the iframe is?
- Does a reload of the page fix the problem after you consented?
This could be the problem
- The Js runs once when the page loads, but the iframe is still "broken" (maybe having the wrong size).
- The Js does some styling magic to extend the iframe to the available width of the page.
- The Js needs to run when the iframe is in an unblocked state otherwise the calculation fails.
How to fix
- Also block the Js, although it does not add any cookies.
- Attach it to the same service as the iframe.
- This way the Js will run after the iframe was unblocked.
Migrating from version 1 to 2
HINT: Add the schema.json
file from this package to your IDE and select it for your yaml files. This will make it easier and give you auto-completion and validation when migrating.
Everything concerning the actual blocking of tags by changing the markup on the server
is moved to Sandstorm/CookiePunch/blocking/...
in the config
Everything concerning the rendering of the consent and therefore the configuration of klaro
is moved to Sandstorm/CookiePunch/consent/...
in the config
Blocking Mode
Old
Sandstorm:
CookiePunch:
mode:
blockAllScripts: true
blockAllIframes: true
New
Sandstorm:
CookiePunch:
blocking:
tagPatterns:
script:
"*":
block: true
iframe:
"*":
block: true
Blocking Patterns
Old
Sandstorm:
CookiePunch:
elements:
block: true # default blocking mode for all tags
group: default # default group for all blocked tags
patterns:
"Packages/Neos.Neos":
type: script
block: false
"https://anchor.fm":
type: iframe
block: true
New
Sandstorm:
CookiePunch:
blocking:
tagPatterns:
script:
# "*":
# service: default -> see explanation
"Packages/Neos.Neos":
block: false
iframe:
# "*":
# service: default -> see explanation
"https://anchor.fm":
service: mediaembeds
IMPORTANT: The wildcard pattern *
should only be used in rare situations. Think about if you really need
to change the default blocking behaviour and create a generic service default
as this defies the whole purpose
of documenting the services used for this page.
A pattern schould eigther have block: true|false
OR service: "nameofservice"
Groups -> Services
We changed the naming to match the api of klaro. Everything concerning the consent can be configured here:
Sandstorm/CookiePunch/consent/...
Old
Sandstorm:
CookiePunch:
groups:
anchor:
title: Anchor FM
description: Podcast Player
New
Sandstorm:
CookiePunch:
consent:
services:
anchor:
title: Anchor FM
description: Podcast Player
Consent Options for each group -> service
As we are already in Sandstorm/CookiePunch/consent/...
we drop the additional
consent
nesting level. All options of a service concern the consent.
Old
anchor:
title: Anchor FM
description: Podcast Player
purposes:
- mediaembeds
consent:
required: true
New
anchor:
title: Anchor FM
description: Podcast Player
purposes:
- mediaembeds
required: true
For more consent options of a service see the docs for advanced configuration.
Styling
If you have custom styling it will most likely break depending on what was changed in klaro.js bundled with this package. This package does not provide a SCSS file anymore. Check the section on styling for more information.