I was thinking about a pragmatic and 'law-abiding' way to prevent Google Tag Manager to be enabled without the user's consent as demanded by the gdpr. Turned out to be quite simple. Here's my take on it.

My solution is based on the open source version of Osano's Cookie Consent script spiced up with some typoscript. The script can be configured to always show up unless it set a cookie that would store the current user's choice regarding privacy protection. Depending on the existence or content of that cookie we can then use typoscript to render or skip the GTM code.

Typically one would include the GTM snippet into the page header like so:

page.headerData.999 = TEXT
page.headerData.999.value (
    <!-- Google Tag Manager -->
    new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
    <!-- End Google Tag Manager -->

Depending on the mentioned cookie we can remove the snippet right away and be sure no tracking will happen on a first visit:

[request.getCookieParams()['cookieconsent_status'] != 'allow']
page.headerData.999 >

In addition we can add a typoscript setting to use in Fluid templates to control the rendering of the required GTM noscript tag fx:

plugin.tx_myext.settings.cookiesAllowed = 0
[request.getCookieParams()['cookieconsent_status'] == 'allow']
plugin.tx_myext.settings.cookiesAllowed = 1
<f:if condition="{settings.cookiesAllowed}">
<!-- Google Tag Manager (noscript) -->
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-XXXXXXX"
                  height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<!-- End Google Tag Manager (noscript) -->

Finally the cookie-consent script can be configured to point to the site's data protection page (I always add a typoscript constant for the page uid) and to force reloading the current page after the user made a decision to enable GTM in case of acceptance:

page.jsFooterInline {
    10 = TEXT
    10.typolink.parameter = {$plugin.tx_myext.settings.dataProtectionPageUid}
    10.typolink.returnLast = url
    10.typolink.forceAbsoluteUrl = 1
    10.wrap (
        window.addEventListener("load", function () {
                content: {
                    "message": "This website uses cookies to blah...",
                    "dismiss": "Decline cookies",
                    "allow": "Accept cookies",
                    "link": "Privacy policy",
                    "policy": "Cookies and privacy settings",
                    "href": "|"
                position: "bottom-left",
                revokable: true,
                type: "opt-in",
                compliance: {"opt-in": '<div class="cc-compliance cc-highlight">{{allow}}{{dismiss}}</div>'},
                window: '<div role="dialog" aria-live="polite" aria-label="cookieconsent" aria-describedby="cookieconsent:desc" class="cc-window cc-overlay"><div class="cc-overlay__inner"><div class="{{classes}}"><!--googleoff: all-->{{children}}<!--googleon: all--></div></div></div>',
                onStatusChange: function () {window.location.reload()}