woocommerce / woocommerce-google-analytics-integration

WordPress plugin: Provides the integration between WooCommerce and Google Analytics.
http://wordpress.org/plugins/woocommerce-google-analytics-integration/
172 stars 69 forks source link

Consent Mode not updated with page caching #399

Closed toktor closed 4 months ago

toktor commented 7 months ago

Describe the bug:

When using WP-Rocket and caching enabled, we can not change the consent state via woocommerce_ga_gtag_consent_modes filter. The issue lies as the consent mode is included as inline JS and WP-Rocket creates static files for each page where we can not change the inline JS.

Steps to reproduce:

  1. Have site with WP-Rocket page-cache enabled
  2. Send data to woocommerce_ga_gtag_consent_modes when user is logged out and modifying consent
  3. WP-Rocket serves previous version of JS with previous consent

Expected behavior:

  1. Possibility to change consent on the fly without clearing page cache
  2. Not serve wrong consent for customers who get served static page cache

Actual behavior:

  1. Not possible to change consent on the fly without clearing cache. This can also create issue of cross-user-cache where one consent state might end up in the wrong user view as the cache file is the same. At least thats how I understand.

Additional details:

Possible proposed solution:

  1. I think the issue lies with inline JS in here: https://github.com/woocommerce/woocommerce-google-analytics-integration/blob/76edbfd35c0a75ed096df9f57a4206cd895cc591/includes/class-wc-google-gtag-js.php#L85

Simplest solution would be to change this JS (or at least consent part) to be included JS file. In this case we can exclude it from caching separately.

  1. Alternative method would be to include Consent States with JS only and not have them come from PHP. Ex. Ajax call to wp-admin to add consent settings.

  2. Third option would be to find a filter in JS that runs the event code and attach to this. So while we do print "wrong" consent modes in the html, we can listen with JS once the code starts running and modify the consent states on the fly when they are sent - without touching the html.

Background: We have created a bridge to use Complianz plugin to manage consent itself and we are using WGAI to show consent. That means we have a bridge that sends consent from Complianz to WGAI but as of not, we cant update consent properly. We cant use Complianz either to manage consent in tags as it does not work together with WGAI. But I assume this issue would be with any of the caching and consent plugins/solutions where we need to update the consent dynamically.

Please let me know if I have understood the problem correctly. I can propose a solution/code modification, if the possible solution is something that makes sense.

Please let me know if this bug-report does not belong here or should be somewhere else.

EDIT: I missed the part where it is recommended to update on-page consent with gtag. We can do this but the default page state update would still be nice.

tomalec commented 7 months ago

Hi @toktor, thanks for early adopting the latest changes and for the detailed report :)

Supporting WP Rocket may be tricky. I haven't investigated it deeply yet, but from a high level, there may be more problems than just consent mode if the plugin turns every page into a static one.

The way the extension works is that it adds inline JS with dynamic data for each page, which may contain information about products added to the cart, purchases, etc. Before 2.0.0 (before consent mode support), it was even more dynamic, as the extension was inlining gtag calls depending on the state. So, having the pages cached could result in irrelevant things being tracked.

As per the consent mode itself: The idea behind the default state = the thing our extension set = gtag('consent', 'default', {...} ), is that it represents your site configuration, and the consent set on the page loaded, before the user takes any action and before any event is tracked (even the page_view). Then the consent could be updated in the page run-time, using gtag('consent','update',{...}) according to a user changing their consent via banner, loading their preferences, etc. (we do not implement any gtag('consnt','update'... in WCGAI).

So, speaking of potential solutions:

  1. Do I understand correctly that you suggest removing inline JS and replacing it with a blocking <script src="file.js"> where file.js would be dynamically created on the PHP side to contain consent modes?
  2. AJAX call for default setting could be trouble some due to the logic I explained above. To respect the Google API rules and users' rights, we'd have to hold all the actions and all the tags from our and other extensions until the AJAX response comes. I cannot imagine the way to do so. (but I'm always happy to be proved wrong :) )
  3. Adding a filter in JS seems more doable than above. But if you have access to JS, you can set your own gtag('consent', 'default',{}) after ours, overwriting the settings. Which is the way correct way to use Google's API. So, there is no need for the hook. You can synchronously add your own blocking script that does gtag('consent', 'default',{ the right one }), and it's done.

AFAIK The fallowing is the right way to use the Google's API and should work fine

<script>
   // WCGAI code with "wrong" defaults
   gtag('consent', 'default', {
      'analytics_storage': 'denied',
      'ad_storage': 'denied',
      'ad_user_data': 'denied',
      'ad_personalization': 'denied',
      'region': ['PL', 'ES' /*...*/]
    });
</script>
<!-- later in the document, in your blocking script -->
<script>
   // your code, with the "right" defaults
   gtag('consent', 'default', {
      'analytics_storage': 'allow',
      'ad_storage': 'allow',
      'ad_user_data': 'denied',
      'ad_personalization': 'denied',
      'region': ['PL', 'ES' /*...*/]
    });
    // ... asynchronously, after AJAX, the user click, whatever
    gtag( 'consent', 'update', {
      'analytics_storage': 'allow',
      'ad_storage': 'allow',
      'ad_user_data': 'allow',
      'ad_personalization': 'allow',
      });
</script>

Thanks for giving the context and mentioning Complianz. I still have it on my to-do list to investigate the integration with them more deeply, but we planned that for v1 or v2 of consent mode. We wanted to ship MVP and basic manual/hook-based configuration ASAP.

Could you elaborate a bit more on how you do that bridge and what and why does not work together?

toktor commented 7 months ago

@tomalec thank you for the detailed response! I think I might have gone a bit off-topic but hope it might help with brainstorming.

Adding a filter in JS seems more doable than above. But if you have access to JS, you can set your own gtag('consent', 'default',{}) after ours, overwriting the settings. Which is the way correct way to use Google's API. So, there is no need for the hook. You can synchronously add your own blocking script that does gtag('consent', 'default',{ the right one }), and it's done.

In current configuration WCGAI page_view is running straight after var wcgai is established with no room to change consent states. Tho I am not sure is consent important for that event.

So, having the pages cached could result in irrelevant things being tracked.

Yep, but I haven't seen ecommerce site that can afford to not cache pages. Cart is updated dynamically via ajax. Even with page cache, each page still has its product parameters correct. With consent now we have first time information that is per-user-basis.

Do I understand correctly that you suggest removing inline JS and replacing it with a blocking Githubissues.

  • Githubissues is a development platform for aggregating issues.