I've created 2 "submodules" (for Google Analytics module and AddToAny module) that disable those cookies as long as the agreed-cookie isn't set through the EU Cookie Compliance module.

Might be interesting to include with this module, since it offers a more complete (but optional) solution to the developer?

I've started work to disable the login form (but stopped it) since I don't think it is necessary because cookies get auto-accepted once the site visitor is browsing the site.

Any feedback is welcome!

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

achton’s picture

sneyerest: excellent idea! I think we ought to pursue this line of thought further.

I don't have time to review your modules right now, but I have implemented support for 6.x for blocking GA in #1744852: Disable Analytics tracking until user accepts policy. I had not thought to attempt in a seperate module, so that is a direct extension of the main module. I am particularly interested in the approach for GA, since there are more ways to do this (the Cookie Control module uses a very different method than mine).

We should combine forces and see if your suggestion can become the defacto way of blocking various cookies. It will allow us to keep the main module leaner, I hope.

I will review your code as soon as possible.

sneyerst’s picture

The 2 modules I posted are loosely based on the Cookie Control GA example. I do find the EU Cookie Compliance a better and cleaner implementation.

Please let me know if you are interested in the disabled login form (if not accepted). Might still be interesting to implement if #1568590: Accepting cookies by visitors who continue browsing the website should be an option rather than a feature and #1720596: Add a "No, do not accept" option will get a solution.

Miszel’s picture

I personally prefer this approach over mixing support for other contributed modules with the core EU cookie compliance module. There are however a couple of things that we will have to consider.

1) I think that the ideal place to implement support for a contributed module is in that module, not in the EU cookie compliance module. I think it may become impractical to keep up with changes to many other contributed modules in the EUCC module. We can however start off with including submodules into EUCC and see how it goes. If the idea takes off and we will have too many submodules we may try to move them to their counterparts

2) The submodules should use javascript to check if cookies have been accepted or not. If you do that in PHP, it won't work with pages that are fully cached. For example, Varnish can be configured to ignore the cookies set by EUCC and still serve pages from cache and hence your PHP code will not run. I think this is essencial for busy sites with lots of anonymous traffic. There is a javascript function that checks if cookies have been accepted: Drupal.eu_cookie_compliance.hasAgreed().

3) I would suggest leaving the dissabling form functionality out for now. You can perhaps create a feature request for that issue an leave it for discussion to see if it is a desired feature.

sneyerst’s picture

Marcin Pajdzik, I didn't know about the caching issues when handling this serverside.

Implementing this correctly on the client increases the difficulty of making GA, AddToAny, ... compatible with EU Cookie Compliance module quite a bit (certainly when handling GA, AddToAny as a black box). I will try however to come up with a better solution in the coming days.

achton’s picture

I still have not reviewed the approach in the two submodules, but if they are indeed serverside, then what Marcin mentions does apply. It's problematic at best (performance-wise).

GA happens to have a JS-based method that allows us to disable tracking and the setting of cookies on a per-request basis, and that is how I have accomplished this in #1744852: Disable Analytics tracking until user accepts policy. So as long as other cookie-setting modules have similar options, the submodule approach is still valid. We just need to make sure that each solution is cache-aware and tailored properly to the cookies in question.

This made me think, though, that we ought to discuss (or maybe just document) the proper ways to interact with the Javascript-layer of the EUCC-module, and outline a generic way to interact with the popup. We could consider building a set of API functions for this purpose, and this might even go some way to solve #1633706: Providing an default information page for admins and a hook for developers and future requests for better DX. For instance, I could envision the popup automagically informing the user of which cookies were blocked, simply because a submodule has defined support for those.

So perhaps we need to split this into three issues:

  • Define and document best practice for integrating with EUCC module
  • Add submodule for blocking Google Analytics
  • Add submodule for blocking AddToAny

And further down the line (2.x maybe?) we should add hooks and some automagic for those submodules and future ones that wish to support other types of cookies.

sneyerst’s picture

I'm reworking the integration with Google Analytics module based on the provided input. Code so far:

Serverside:

function eu_cookie_compliance_google_analytics_page_alter(&$page) {
  drupal_add_js(drupal_get_path('module', 'eu_cookie_compliance_google_analytics') . '/eu_cookie_compliance_google_analytics.js', array('type'=>'file', 'scope'=>'footer'));
}

Javascript:

(function ($) {

	var ua = 'UA-2928567-1';
	var disable_ua = 'ga-disable-' + ua;
	window[disable_ua] = true;
	
	Drupal.eu_cookie_compliance.pushPageView = function(ga_disabled_ua) {
		window[ga_disabled_ua] = false;
		_gaq.push(['_trackPageview']);
	}

	
	if (Drupal.eu_cookie_compliance.hasAgreed()) {
		Drupal.eu_cookie_compliance.pushPageView(disable_ua);
	}
	
})(jQuery);

I do have a question though. How do I pass the UA code to this script the right way?

Any help and any feedback is much appreciated.

achton’s picture

I believe the patch for 6.x provided here will help you out.

The GA module sets this as a variable, so we can retrieve that in our own module and send it to the JS layer for our own use. The submodule should depend on the GA module, btw.

Miszel’s picture

Server side: You just need to remember that you should not use conditional code that depends on cookie's values.

Client side: As for GA, I think that the patch linked in the comment above is the best approach. We may have to add an event to the EUCC javascript - something like onAcceptance so that we can trigger other scripts once a visitor has accepted cookies.

Miszel’s picture

After giving it some thoughts I am not sure if the submodules solution is the best one. I certainly prefer seperating the code that blocks third party cookies from the EUCC core module but creating many submodules will litter the modules admin page in Drupal unnecesarly. At the end of the day all that such a submodule would have to provide is javascript code and a piece of PHP code that would include that javascript code. In my opinion it is too little to justify creating a module.

What I would sugest we should consider as an alternative solution are plugins that could be provided but EUCC and/or other modules.

What I think we would have to do in EUCC is to provide:

1) A drupal hook that would allow modules to register a plugin. A module that implements this hook would provide a path to the plugin cookie compliance javascript code and a plugin name.
2) An admin page that would list all registered plugins and would allow to enable/disable them
3) A simple caching mechanizm that would cache all enabled plugins (using drupal cache API).

This way we will centralized the control over all third party cookie compliance javascript code. Other modules will not have to care about where and how to add their javascript code. EUCC will do that.

I think that the EUCC module will still have to provide an event that would be fired on cookie acceptance. This event and the hasAgreed() function could be used in the third party cookie compliance javascript code.

I am interested in your thoughts.

sneyerst’s picture

I need a solution on the short term, and will probably settle for one of my proposed modules for now. I don't use Varnish so it wouldn't be too troublesome.

But you do have a point there and I really like the idea of a centralized administration of EUCC "plugins". In case such a system exists, I will definitely take a shot to it and try and implement some plugins for it.

achton’s picture

Title: Include cookie prevention submodules? » Add support for cookie blocking submodules/plugins
Priority: Minor » Major

Marcin: I don't think the project would become littered with submodules as such, and any problem that may surface in the admin UI could easily be solved, e.g. by only showing submodules that relate to enabled cookie-setting modules. A huge site may have 4-5 modules enabled that set cookies, I'd think.
Also, I don't agree on the argument about code size in submodules. It is much more important that the logical distinction between the main module and submodules is clear - and I think we all agree that, in the long term, it is not benificial to include this cookie-blocking stuff directly in EUCC core (as I have done in my patch).

Creating submodules has many benefits, I think:

  • the code is simple and easily reviewed by us, meaning quick adoption for new releases of EUCC
  • no need to badger other projects about support, it all happens within the EUCC project
  • we can take advantage of existing module eco-system, theme hooks, caching, etc.
  • the admin page can be automatically extended by the submodules to include cookie-specific settings (e.g. using hook_form_alter())
  • enabling new cookie-blocking features is as simple as enabling a module in Drupal
  • other developers can quickly look over existing submodules and adapt them for their needs (major point IMO)

So as you can see, I still favor the submodule approach, since I think it will allow for the long-term extendability that we need. That is also the case with the plugin-approach that you outline, but I feel like it is overkill, since we would be reinventing a lot of stuff that is already available using modules.

But let's review our options in earnest. We have several possibilites for adding support for blocking arbitrary cookies here:

  1. As core features of the EUCC module
  2. As seperate modules within the EUCC project (submodules)
  3. As a single, seperate module within the EUCC project
  4. As seperate modules outside the EUCC project
  5. As "plugins" within EUCC module
  6. As patches to each module, using EUCC hooks (e.g. GA, AddtoAny)
  7. Other options?

Whichever solution we go by, what is most important (and we totally agree here) is that it should not be necessary to submit cookie-blocking patches to other projects (they'll never get accepted anyway). The features should be developed within the EUCC project.

I feel though, that this feature can be executed much quicker with the submodule approach. I will try to do a proof-of-concept for the GA blocking to further validate my point.

Miszel’s picture

Regardless if we go with the submodules or plugins I am afraid that support for many other contributed modules within the EECC module will become very impractical. We will have to keep up with changes to all other modules that we decide to support. That will become time consuming and unmanagable.

Therefore, I do not agree that “no need to badger other projects about support, it all happens within
the EUCC project” is an advantage. I reckon that we should actually do the opposite. We should encourage other modules to provide support by making it as easy as possible for their maintaners to include support as a part of their module. We can do that by creating a very simple pluging system.

For example the views module or migrate module are not trying to provide support for all other modules. They encorage other modules to provide plugins. Fortunately our plugin system would not have to be that complicated as their. It would be just one hook.

Creating plugins has the following benefits:

* The admin page will be automatically created based on the plugins registered with the hook_cookie_compliance. No need for hook_form_alter in plugins.
* Enabling a new plugin is as simple as ticking a box on the automatically created admin page.
each plugin will provide only a simple drupal hook (ie hook_cookie_compliance that only returns an array) and javascript code in a seperate file which means that it is very simple to either include it in a contributed module or alternatively in EUCC if, for any reason, the contributed module does not want to support it and we want to.
* Other developers can quickly look into the javascript code and focus only on the cookie-blocking code. The development of a plugin is simpler as it will only requre javascript code to be developed. The developers do need to worry about how, where and when to include their code onto pages.
* Including javascript for all plugins (with drupal_add_js) will be handled in EUCC core module in one hook_page_alter function. With submodules you will have to do a hook_page_alter for each submodule. Clearly, you will get better performance with plugins and less hussle for the developers.
* Plugin system is commonnly used in drupal in the most regarded modules (look at such modules as context or views for example).
* Logical distinction between the main module code and plugins remains very clear and cookie blocking code is kept in separate files.
* We have to create only one extra file (javascript) for the plugin versus three new files for a submodule (again, performance)
* We can still take advantage of existing module eco-system, theme hooks, caching, etc. Standard drupal cache will be used for caching all js paths so that we don't have to run the cookie complaince hooks all the time.
* Perheps the most important thing – by encoraging collaboration from other module maintainers by making it as simple as possible for them to provide support, you increase chances for your module to become succesfull.

Regardless of what route we will take I think it is a good idea to do the GA submodule as a proof of concept. Even if we decide to go with plugins, it will be extremaly easy to convert:
- the js will be the same,
- we won't need the .module and .info files,
- we will only have to write four lines of code for the hook_cookie_compliance.

achton’s picture

I really don't think other projects are likely to include support for EUCC in the way you describe. Even if a cookie-blocking patch for, say, the Google Analytics module is accepted and committed, the feature would not be generally available until a stable release of GA comes out (unless one is willing to use the -dev version). Currently, GA has not released a new version for almost 18 months ..!
AddToAny 6.x has not been released for 1 year, and 7.x for 6 months. Which is another problem - there might be varying cookie-blocking support in our 6.x and 7.x branches because other maintainers roll their releases at their own discretion.

If the code is at least kept within this project, we maintain control of how often releases are made. If some new cookie-spewing module suddenly becomes very popular, we could quickly roll a new release of EUCC with support for blocking those cookies - thus satisfying those that want to (continue to) implement the policy and use that new module both. We would not be dependant on other project maintainers in such a situation.

I think that is our main disagreement.

Apart from that, I do see your point about a JS-based plugin system, although I am not convinced that performance is a real issue with submodules, since a lot of caching is leveraged (theme registry in particular) in modules. But you do seem to have a pretty solid idea of the implementation, so that goes a long way too :)

I'll still do the submodule, though.

Miszel’s picture

Ok, I understand that you may doubt that the module will be used that way I described but the point is that the plugin solution give you the best of both worlds. Nothings stops you from:

1) Including a plugin as a part of the EUCC module (ie. for the modules that you described in your example).
2) Including a plugin as a part of a contributed module if the maintainer is willing to support it.
3) Creating a separate (sub)module that will only contain plugin(s) for another contributed module(s).

The diffrence lies in that the plugins will be easier to develop for other developers because for the php part they will only need to know what array they need to return to register their plugin. They will not need to know how the EUCC works internally. In addition it may be easier to maintain because if we need to change the way the js is included onto pages, we can do that in one place rather than in each of the submodules.

We can still keep control whenever we want or we can easily delegate support. I think it is important because I don't believe that you, me or anybody else will want to spend too much time on supporting EUCC and all the submodules.

I espacially like the way the migrate module does it. Please, have a look at migrate_extras (http://drupal.org/project/migrate_extras) that extends migrate.

In any case I think that creating a submodule for GA should be the first step.

achton’s picture

OK, I can see that your point of view stems from how Migrate has done it, i.e. the extensions are mainly in Migrate Extras, but some (e.g. Date support) exist in the modules themselves. Support for Migrate outside the Migrate Extras module seems to either go in a submodule (eg. date_migrate) or in a modulename.migrate.inc-file that is included in the modulename.info using files[]. I suppose we might require a modulename.cookie.inc and js/cookie.js or something to that effect which are then automagically included. I can see how that would be quite simple to implement, like you describe.

I do think the amount of flexibility you mention is cool, but I also think most developers will opt for supplying the patch to our issue queues, instead of other modules' (e.g. GA), since we will probably be able to release the feature faster, being a fairly simple project. So how about we ourselves have a single submodule - similar to Migrate Extras - which exists to support those cookie blocking plugins that are not in the modules themselves? An incubator, if you will, until those plugins can go live with the cookie-setting modules themselves. That way, we a) get up and running with cookie-blocking support quickly, b) take advantage of the plugin mechanism that external modules would use (instead of having an internal way of doing things and an external), and c) we also encapsulate cookie blocking from the main module.

How's that for a plan? :-)

sneyerst’s picture

Quite an interesting read over here. Since you guys know this modules internals far better than me, I'll leave the discussion about the direction to go to you two. I do want to help out where I can concerning plugin writing, etc.

achton’s picture

Bonus info: The maintainer for GA is not likely to support any code that blocks cookies in the GA module, judging from his take on things in #1410890: Feature for EU operators to explicitly request permission from the user to store cookies. So that is one case, where we would have to implement the plugin ourselves in order to support GA.

Miszel’s picture

I like the idea in #15. I am traveling right now but once I am back, I will be able to help out.

achton’s picture

Version: 7.x-1.x-dev » 6.x-1.x-dev
Status: Active » Needs work
FileSize
1.91 KB

Here is a very early start for the submodule approach. This patch sets up a hook_cookie_compliance() that is implemented by a new submodule, eu_cookie_compliance_blocker.

The information from that module's hook invocation is then available in hook_footer() in the form of an array containing (c/p from the patch):

+  //   - module implementing hook
+  //     - module that sets the cookie being blocked
+  //     - js file to be included
+  //     - description of the plugin

And this information can be extended, obviously. I was thinking that putting this in the footer for now would quickly let us drupal_add_js() the Javascript to get a sense of whether this will work. After that, the module_invoke() stuff should be moved somewhere for caching, probably hook_init(), and the admin interface built to mirror the plugins found.
Note that none of the actual cookie-blocking code is included in this patch yet. This is just to share a very early idea that I had and am considering as a solution.

And pardons for doing this in D6, I am most comfortable there :-/

hass’s picture

What is Google doing itself?

achton’s picture

What Google does is irrelevant. CEOs and stakeholders and site owners out there are requesting this feature, and thus developers are being made to do something about it. That's where we come in. I have all this functionality (and plenty more) running live, and there is no reason not to share it. The module is highly configurable, including the cookie blocking stuff (when it's done), so anyone can implement their own version of the required cookie information popup.
It won't have any effect on the GA module itself, so no worries :-)

hass’s picture

My point is that Google may need to do something as they cannot leave their users alone with this compliance issues (if they are really real). If they are not real - Google will not change anything, but than nobody need to do anything. That's why I must expected that the GA API may get an extension that - allows tracking without Cookies - just as one example or at least allow to add a custom function/JS snippet that prevents GA from tracking until a button is pressed or a cookie has been set.

Something that works with GA out of the box without doing something on your own.

achton’s picture

hass: We use this feature (a window property) which I think has been around for some time. It prevents cookies from being set on a per-request basis. I doubt if they will do anything else.

hass’s picture

I was not aware of this feature, but this is 100% what I talked about in #22 :-). THX for the link.

achton’s picture

So, it would be a good idea to use straight jQuery for event handling on the JS-side.

To support vanilla Drupal 6 we need to do that in jQuery 1.2 (!) .. meaning I have to make sense of the original Event implementation from September 2007 ... http://docs.jquery.com/Release:jQuery_1.2/Events

I'm already working on this, but any input or ideas or good examples (for 1.2) are welcome.

achton’s picture

Here is an updated patch against 6.x-dev.

What actually works:

  • There is now basic support for hooking into EUCC from another module, using hook_cookie_compliance()
  • From the hook implementation, you supply a path to a JS file, variables to include in the JS layer, and a title + description for the plugin
  • The JS file can access the hasAgreed() function of EUCC to determine whether the user has agreed yet, and act upon this information
  • A module can provide multiple cookie blocking plugins in this manner
  • The EUCC admin interface allows for toggling each plugin on/off
  • The included submodule (eu_cookie_compliance_blocker) has support for blocking GA until accept (yay!) - however: the GA cookies wont be set until the next request, so you lose the initial page hit, which is bad (see missing feature below)

It does NOT have these notable features (yet):

  • JS trigger for binding to the event that occurs when the acceptance status changes (only the agree button does this for now)
  • GA blocker: pushing the initial pageview to Google, once the agree button has been clicked
  • Undo button that clears accept status and plugin cookies and which can also be placed on specific pages/DOM elements (see #1577970: Provide a means to Un-set and #1760338: Add an "Undo consent"-link to popup)
  • Caching the available plugins to avoid iterating over module_implements() several times in the module

So .. this progress is not too bad, and almost constitutes our proof-of-concept as we wanted.

However, I still need to figure out how to properly get events to work. Apparently, Drupal.behaviors is supposed to be an improvement over vanilla jQuery events, since they are run continually and not only on $(document).ready, which is good if you modify the DOM after the page has finished loading. So it would be good if we could somehow do our stuff with Drupal.behaviors, but it is a rather ... "exotic" feature that is poorly documented.
I will go hunting in other modules for similarities, though.

achton’s picture

Status: Needs work » Needs review
FileSize
6.46 KB

Sorry, please disregard the previous patch. This one is correctly rebased against 6.x-1.x.

Also, I'd welcome some feedback on this approach.

svenryen’s picture

Issue summary: View changes
Status: Needs review » Closed (won't fix)
svenryen’s picture

I have created a new issue for 7.x/8.x and referenced this one, so that we can leverage on the code.
https://www.drupal.org/project/eu_cookie_compliance/issues/2947553