On June 5 2012, the new Dutch Telecommunications Act came in force. This act is a direct result of a recent EU ‘Cookie’ Directive regarding websites storing information on your computer. For the most part the directive is about the use of cookies, but other examples are “HTML5 local storage” and “local shared objects“. This EU Directive (officially known as the EU Directive 2002/58 on Privacy and Electronic Communications, otherwise known as the E-Privacy Directive) states in Article 5(3) the following:
Member States shall ensure that the storing of information, or the gaining of access to information already stored, in the terminal equipment of a subscriber or user is only allowed on condition that the subscriber or user concerned has given his or her consent, having been provided with clear and comprehensive information… about the purposes of the processing.
There are already a lot of sites discussing and explaining the European directive like Information Commissioner’s Office (ICO) and All About Cookies. You can find tons of information on them like the difference between session- and persistent cookies. Apart from the distinction between session and persistent cookies, cookies can also be separated into functional and non-functional cookies. Functional cookies are cookies that are required for the basic operation of your application – most of the time these are also session cookies. Non-functional cookies however are not required for your application to work properly, they are additional and most of the time they only provide an added value for the applications owner. It’s on these last kind of cookies the directive applies. The EU Member States all have their own legislations derived from the EU Directive, they all are slightly divergent amongst themselves.
The Dutch Telecommunications Acts deviates from the EU Cookie Directive by being stricter than the EU Directive. The Dutch legislator obliges visitors to opt in at the very moment they enter your website for the first time. The result is that the site is not allowed to store non functional cookies before you received your visitors consent to do so. Since the amendments to the Dutch Telecommunication Act is in force as of June, 2012 a lot of web developers in The Netherlands are currently busy adjusting their websites so they abide the law. Sure, we have some time left, because the Act will only be actively enforced as of January 2013. A lot of sites (including those from our government) are not yet compliant. However, some are, and some even deal with the new law in a very humoristic manner, like: Bolletje a website from a very renowned Dutch cookie bakery (the consumable kind of cookies).
For now, I don’t want to dig into the legislation in detail. On the contrary, I like to share some problems I ran into as well as put forward some ideas I had to cope with while dealing with the new regulations as well as using WordPress.
Acting as application developer for a large Dutch internet company, I was confronted with a challenge, considering the following paradigm:
- A lot of sites, realized using WordPress, which already had a lot of user generated content,
- Virtually all of those sites are cached using W3 Total Cache,
- Some sites where also virtually hidden behind a Squid Server or a Content Delivery Network (CDN),
- No non functional cookies are to be stored on the visitor’s computer before the visitor has given his or her consent to do so.
I already noticed that there are some plugins readily available, which could assist me coping the “cookie law”, like: Cookie confirm, Cookie control, Cookie law info, Cookie warning and Cookie opt-in. The last mentioned plugin almost suits my needs. This plugin was construed by Dutch developers… meaning, they had to deal with identical issues I have to. In most cases their solution might work in the end. Unfortunately, the plugin handles some elements at the server side… which is a pity as this does not function quite good with my fully page cached websites.
This situation left me no other choice then getting busy with code myself. So I started to categorize the cookies my sites deploy, being:
- Cookies set by Google Analytics,
- Cookies set by other external Javascripts (like: AddThis / Facebook),
- Cookies set by User Generated Content (oEmbed iframes, like: YouTube).
Google Analytics (GA)
The tracking cookie issued by Google Analytics can be anonymized. I learned from several websites that Joost de Valk is to be acknowledged for this useful suggestion. He proposes to obfuscate the last octet of a visitors IP address and by doing so disabling Google Analytics to track users on an individual level.
The following Javascript code represents an example of GA-code where the IP address is anonymized:
var _gaq = _gaq || []; | |
_gaq.push (['_setAccount', 'UA-XXXXXXX-YY']); | |
_gaq.push (['_gat._anonymizeIp']); | |
_gaq.push (['_trackPageview']); |
External Javascripts and i-Frames
Some of the plugins I used were using (external) Javascripts which set cookies. And I discovered too that some site administrators add Youtube videos using the WYSIWYG editor, usually by applying WP’s internal oEmbed. This left me with a DOM document filled with asynchronous loading external assets. Due to the asynchronous process, I was unable to determine when they start using cookies. But I felt pretty sure that cookies were being stored before my “document.ready state” fired.
To remain at an utterly safe side, I decided it would be best to prevent the external scripts to load, before I wanted them to do so. My solution was to keep the SRC attribute empty until further notice… So instead of issuing a Javascript tag and an iframe tag (like we usually do), like this:
…I wanted WordPress to output a slightly different HTML code, in which the SRC attribute is empty and the new data attribute is used to store the source. This prevents the tags from loading their sources, since none are defined in the correct manner. The new lines of code read as follows:
I used the following WP filter and function to achieve my goals:
<?php | |
add_filter( 'script_loader_src', 'my_script_loader_filter' ); | |
/** | |
* function for filter 'script_loader_src' it echo's a script-tag with it's src empty. the src is kept in data-src so javascript can put in the src-attr on a later moment. (e.g. after a cookie-check) | |
* | |
* @author Floris P. Lof | |
* @params String $src the current source of the scriptfile to be included | |
* @return Boolean false | |
*/ | |
function my_script_loader_filter( $src ) { | |
echo '<script type="text/javascript" src="" data-src="' . esc_url( $src ) . '"></script>' . PHP_EOL; | |
return false; //unfortunately this also leaves you with an extra blank script-tag in your DOM 🙁 | |
} |
Note that the above mentioned code filters all Javascripts, that were enqueued in the correct way in WordPress. You may want to edit this filter/function as to filter solely your malignant scripts.
Moreover I wrote the following filter and function for iframes, which were created by external services and WP’s oEmbed:
<?php | |
add_filter( 'embed_oembed_html', 'my_embed_filter', 10, 3 ); | |
/** | |
* function for filter 'embed_oembed_html' it echo's a iframe-tag with it's src empty. the src is kept in data-src so javascript can put in the src-attr on a later moment. (e.g. after a cookie-check) | |
* | |
* @author Floris P. Lof | |
* @params String $html the ready made html received from an external API (like Twitter, Youtube, Vimeo) | |
* @params String $url the original URI with WP's oEmbed called the external API | |
* @params Array $attr extra attributes (width height) | |
* @return String $html the rendered html-code | |
*/ | |
function my_embed_filter( $html, $url, $attr ) { | |
//load the html created by the oEmbed-service | |
$document = new DOMDocument(); | |
//error suppression needed here | |
@$document->loadHTML( $html ); | |
//list the iframes and parse them | |
$lst = $document->getElementsByTagName( 'iframe' ); | |
for ( $i = 0; $i < $lst->length; $i++ ) { | |
$iframe= $lst->item( $i ); | |
$attr_src_value = $iframe->attributes->getNamedItem( 'src' )->value; | |
//empty the 'src'-attribute | |
$iframe->attributes->getNamedItem( 'src' )->value = ''; | |
//create a new 'data-src'-attribute | |
$data_src_attr = $document->createAttribute( 'data-src' ); | |
//copy the 'src'-value to our new 'data-src'-attribute | |
@$data_src_attr->value = $attr_src_value; | |
//append it as a child to our iframe | |
$iframe->appendChild( $data_src_attr ); | |
} | |
//save our domdoc back to html | |
$html = $document->saveHTML(); | |
return $html; | |
} |
Note –
The code illustrated above filters oEmbed, but it does not filter iframes which were added in the WYSIWYG-editor manually (or in the pre-oEmbed-era). You also could use another WordPress hook to filter the content off course. This function has been tested with Vimeo and Youtube.
Enabling the external sources
Now you need to activate your tags at the client side. So I created a Javascript/jQuery module for my theme. This module invites the visitor to approve the use of cookies. When the user approves, the server needs to activate both script tags and iframe tags, e.g. by copying the value of “data-src” to “src”. The result is that external sources are loaded automatically, when and if your visitor wants to.
The code at the client side to activate these tags could read as follows:
Note that this solution could also be applied to the problem, posed by Google Analytics. Since this is also an external script. Then there would be no need to ‘anonymize’ Google Analytics.
The code snippets in this article could help you coping with the cookie law in your country. Yet, as you may have noticed, this is not an “put-this-in-your-functions.php-and-you-are-done”-article. You should be aware of your country’s telecommunication laws and your site specific adjustment needs if you want a good law-abiding implementation.
A fully working example of the implementations in this article can be found here.
What about you?
How have you dealt or how are you dealing with the cookie legislation in your country?
Photo by Richard Bussink