diff --git a/assets/js/ga.js b/assets/js/ga.js
deleted file mode 100644
index 6375501..0000000
--- a/assets/js/ga.js
+++ /dev/null
@@ -1,139 +0,0 @@
-import {analyticsCode} from '@params';
-
-let dataLayer;
-
-function isDoNotTrackEnabled() {
- if (typeof window === 'undefined') {
- return false;
- }
- const {doNotTrack, navigator} = window;
-
- // Do Not Track Settings across browsers
- const dnt = (doNotTrack || navigator.doNotTrack || navigator.msDoNotTrack);
-
- if (!dnt) {
- return false;
- }
-
- return dnt === true ||
- dnt === 1 ||
- dnt === 'yes' ||
- (typeof dnt === 'string' && dnt.charAt(0) === '1');
-}
-
-function gtag() {
- dataLayer.push(arguments);
-}
-
-/**
- * Function that tracks a click on an outbound link in Analytics.
- *
- * This function takes a valid URL string as an argument, optional
- * 'isBlank' argument, and uses that URL string as the event label.
- *
- * Setting the transport method to 'beacon' lets the hit be sent using
- * navigator.sendBeacon() in browser that support it. The navigator.sendBeacon()
- * method asynchronously sends an HTTP POST request containing a small amount of
- * data to a web server.
- */
-function trackOutboundLink(url, isBlank = false) {
- gtag('event', 'click', {
- 'event_label': url,
- 'event_category': 'outbound',
- 'transport_type': 'beacon',
- 'event_callback': () => {
- if (!isBlank) {
- document.location = url;
- }
- }
- });
-}
-
-function trackInternalEvent(label, category) {
- gtag('event', 'click', {
- 'event_label': label,
- 'event_category': category
- });
-}
-
-function onClickCallback(event) {
- const element = event.target;
- const className = element.getAttribute('class');
-
- // Track 'Back to top' click
- if (className === 'top-of-site-link') {
- trackInternalEvent('Back to top', 'navigation');
- return;
- }
-
- // Track menu show
- if (className === 'sidebar-toggle') {
- trackInternalEvent('Sidebar Toggle', 'navigation');
- return;
- }
-
- // Track annotation usage
- if (className === 'hypothesis-container') {
- trackInternalEvent('Annotation open', 'navigation');
- return;
- }
-
- // Track feeds click
- if (className === 'menu-feeds-item') {
- const feedType = element.dataset.feedType;
- trackInternalEvent(`Get ${feedType}`, 'feed');
- return;
- }
-
- // Track only external URLs.
- if ((element.tagName !== 'A') || (element.host === window.location.host)) {
- return;
- }
-
- // Track outbound link click
- trackOutboundLink(
- event.target,
- event.target.getAttribute('target') === '_blank'
- );
-}
-
-if (isDoNotTrackEnabled()) {
- // Skip analytics for users with Do Not Track enabled
- console.info('[TRACKING]: Respecting DNT with respect to analytics...'); // eslint-disable-line no-console
-} else {
- // Known DNT values not set, so we will assume it's off.
- if (analyticsCode) {
- (function () {
- // New Google Site Tag (gtag.js) tagging/analytics framework
- // See: https://developers.google.com/gtagjs
- const baseUrl = 'https://www.googletagmanager.com';
- const params = new URLSearchParams({
- id: analyticsCode
- });
-
- let script = document.createElement('script');
- script.src = baseUrl + '/gtag/js?' + params.toString();
- script.type = 'text/javascript';
- script.async = true;
-
- document.getElementsByTagName('head')[0].appendChild(script);
- }());
-
- dataLayer = window.dataLayer = window.dataLayer || [];
- gtag('js', new Date());
-
- const month = 30 * 24 * 60 * 60; // 30 days, in seconds
-
- // Setup the project analytics code and send a pageview
- gtag('config', analyticsCode, {
- 'cookie_expires': month
- });
-
- gtag('set', {
- 'cookie_flags': 'SameSite=None;Secure'
- });
-
- // Outbound link tracking.
- document.addEventListener('click', onClickCallback, false);
- }
-}
diff --git a/assets/js/gtm.js b/assets/js/gtm.js
new file mode 100644
index 0000000..4381ca9
--- /dev/null
+++ b/assets/js/gtm.js
@@ -0,0 +1,54 @@
+import {gtmCode, respectDoNotTrack} from '@params';
+
+/**
+ * Check if the Do Not Track setting is enabled.
+ *
+ * @returns {boolean}
+ */
+function isDoNotTrackEnabled() {
+ if (typeof window === 'undefined') {
+ return false;
+ }
+ const {doNotTrack, navigator} = window;
+
+ // Do Not Track Settings across browsers
+ const dnt = (doNotTrack || navigator.doNotTrack || navigator.msDoNotTrack);
+
+ if (!dnt) {
+ return false;
+ }
+
+ return dnt === true ||
+ dnt === 1 ||
+ dnt === 'yes' ||
+ (typeof dnt === 'string' && dnt.charAt(0) === '1');
+}
+
+if (respectDoNotTrack && isDoNotTrackEnabled()) {
+ // Skip analytics for users with Do Not Track enabled
+ console.info('[TRACKING]: Respecting DNT with respect to analytics...'); // eslint-disable-line no-console
+} else {
+ // Known DNT values not set, so we will assume it's off.
+ if (gtmCode) {
+ (function () {
+ const baseUrl = 'https://www.googletagmanager.com';
+ const params = new URLSearchParams({
+ id: gtmCode,
+ l: 'dataLayer'
+ });
+
+ const script = document.createElement('script');
+ script.src = `${baseUrl}/gtm.js?${params.toString()}`;
+ script.type = 'text/javascript';
+ script.async = true;
+
+ document.getElementsByTagName('head')[0].appendChild(script);
+
+ window.dataLayer = window.dataLayer || [];
+ window.dataLayer.push({
+ 'gtm.start': new Date().getTime(),
+ event: 'gtm.js'
+ });
+ }());
+ }
+}
diff --git a/exampleSite/config/_default/config.yaml b/exampleSite/config/_default/config.yaml
index 719eebb..dca03da 100644
--- a/exampleSite/config/_default/config.yaml
+++ b/exampleSite/config/_default/config.yaml
@@ -24,15 +24,6 @@ disableKinds:
taxonomies:
tag: tags
-# Google Analytics Tracking ID.
-#
-# For more info, read the article
-# https://support.google.com/analytics/answer/10089681
-#
-# Set `HUGO_ENV` environment variable or `site.Params.env` configuration
-# parameter to "production" to use Google Analytics.
-googleAnalytics: ''
-
minify:
# Do not minify XML files to avoid CDATA escape issues
disableXML: true
diff --git a/exampleSite/config/_default/params.yaml b/exampleSite/config/_default/params.yaml
index 71eca84..0eda434 100644
--- a/exampleSite/config/_default/params.yaml
+++ b/exampleSite/config/_default/params.yaml
@@ -141,3 +141,12 @@ comments:
emitMetadata: 0
inputPosition: bottom
lang: en
+
+privacy:
+ googleTagManager:
+ disable: true
+ respectDoNotTrack: true
+
+services:
+ googleTagManager:
+ id: ''
diff --git a/layouts/partials/head.html b/layouts/partials/head.html
index 036b26d..5bf6e09 100644
--- a/layouts/partials/head.html
+++ b/layouts/partials/head.html
@@ -37,5 +37,5 @@
{{- /* Scripts */}}
{{- partial "partials/scripts.html" . }}
- {{- partial "partials/seo/ga.html" . }}
+ {{- partial "partials/head/analytics.html" . }}
diff --git a/layouts/partials/head/analytics.html b/layouts/partials/head/analytics.html
new file mode 100644
index 0000000..74d045d
--- /dev/null
+++ b/layouts/partials/head/analytics.html
@@ -0,0 +1,22 @@
+{{- if eq hugo.Environment "production" }}
+ {{- if not site.Params.Privacy.GoogleTagManager.Disable }}{{/* First check GTM */}}
+ {{- with site.Params.Services.GoogleTagManager }}
+ {{- $gtmParams := dict "gtmCode" (.ID) -}}
+ {{- $gtmParams = merge $gtmParams (dict "respectDoNotTrack" site.Params.Privacy.GoogleTagManager.RespectDoNotTrack | default true) }}
+
+ {{- $gtmScript := slice -}}
+ {{- $gtmScript = $gtmScript | append (resources.Get "js/gtm.js") -}}
+ {{- $gtmScript = $gtmScript | resources.Concat "js/analytics.js" -}}
+ {{- $gtmScript = $gtmScript | js.Build (dict "format" "iife" "target" "es2015" "minify" true "params" $gtmParams) -}}
+
+ {{- if site.Params.assets.disable_fingerprinting }}
+
+ {{- else -}}
+ {{- $gtmScript = $gtmScript | fingerprint -}}
+
+ {{- end -}}
+ {{- end -}}
+ {{ else }}{{/* If GTM is disabled delegate the rest to Hugo */}}
+ {{ template "_internal/google_analytics.html" . }}
+ {{ end }}
+{{ end }}
diff --git a/layouts/partials/seo/ga.html b/layouts/partials/seo/ga.html
deleted file mode 100644
index 40758d8..0000000
--- a/layouts/partials/seo/ga.html
+++ /dev/null
@@ -1,18 +0,0 @@
-{{- if (or (eq (getenv "HUGO_ENV") "production") (eq site.Params.env "production")) -}}
- {{- with site.GoogleAnalytics -}}
- {{- $gaParams := dict "analyticsCode" (site.GoogleAnalytics | default "") -}}
-
- {{- $gaScript := slice -}}
- {{- $gaScript = $gaScript | append (resources.Get "js/ga.js") -}}
- {{- $gaScript = $gaScript | resources.Concat "js/analytics-bundle.js" -}}
-
- {{- $gaScript = $gaScript | js.Build (dict "format" "iife" "target" "es2015" "minify" true "params" $gaParams) -}}
-
- {{- if site.Params.assets.disable_fingerprinting }}
-
- {{- else -}}
- {{- $gaScript = $gaScript | fingerprint }}
-
- {{- end -}}
- {{- end -}}
-{{- end -}}
diff --git a/netlify-production.js b/netlify-production.js
new file mode 100644
index 0000000..18ae1d2
--- /dev/null
+++ b/netlify-production.js
@@ -0,0 +1,15 @@
+const fs = require('fs');
+const yaml = require('yaml');
+
+const filePath = 'exampleSite/config/_default/params.yaml';
+const file = fs.readFileSync(filePath, 'utf8');
+const config = yaml.parse(file);
+
+config.privacy.googleTagManager.disable = false;
+config.services.googleTagManager.id = 'GTM-W8D5W642';
+
+const newYaml = yaml.stringify(config);
+
+fs.writeFileSync(filePath, newYaml, 'utf8');
+
+console.log('Updated config.yaml successfully!');
diff --git a/netlify.toml b/netlify.toml
index b7de027..bd57110 100644
--- a/netlify.toml
+++ b/netlify.toml
@@ -7,6 +7,7 @@
# a base has not been set. This sample publishes the directory
# located at the absolute path "root/project/build-output"
publish = 'public'
+ command = 'npm install'
[build.environment]
HUGO_VERSION = '0.121.0'
@@ -15,22 +16,24 @@
# Production context: all deploys from the Production branch
# set in your site’s Branches settings in the UI will inherit
# these settings. You can define environment variables
-# here but we recommend using the Netlify UI for sensitive
+# here, but we recommend using the Netlify UI for sensitive
# values to keep them out of your source repository.
[context.production]
command = 'hugo --source=exampleSite --baseURL ${URL} --destination ../public --minify'
[context.production.environment]
HUGO_ENV = 'production'
- HUGO_GOOGLEANALYTICS = 'G-DP9Q137C3X'
+ HUGO_ENABLEGITINFO = 'true'
# Deploy Preview context: all deploys generated from
# a pull/merge request will inherit these settings.
[context.deploy-preview]
- command = 'npm run netlify-preview; hugo --source=exampleSite --buildDrafts --buildFuture --baseURL ${DEPLOY_PRIME_URL} --destination ../public --minify'
+ # command = 'npm run netlify-preview; hugo --source=exampleSite --buildDrafts --buildFuture --baseURL ${DEPLOY_PRIME_URL} --destination ../public --minify'
+ command = 'npm run netlify-production; npm run netlify-preview; hugo --source=exampleSite --buildDrafts --buildFuture --baseURL ${DEPLOY_PRIME_URL} --destination ../public; cat ./public/index.html'
[context.deploy-preview.environment]
- HUGO_ENV = 'development'
+ # HUGO_ENV = 'development'
+ HUGO_ENV = 'production'
# Branch Deploy context: all deploys that are not from
# a pull/merge request or from the Production branch
diff --git a/package-lock.json b/package-lock.json
index 9a46a94..df6cf5c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,7 +12,9 @@
"@playwright/test": "^1.47.2",
"@types/node": "^22.7.3",
"eslint": "^9.11.1",
- "jsdom": "^25.0.1"
+ "fs": "^0.0.1-security",
+ "jsdom": "^25.0.1",
+ "yaml": "^2.5.1"
}
},
"node_modules/@aashutoshrathi/word-wrap": {
@@ -774,6 +776,13 @@
"node": ">= 6"
}
},
+ "node_modules/fs": {
+ "version": "0.0.1-security",
+ "resolved": "https://registry.npmjs.org/fs/-/fs-0.0.1-security.tgz",
+ "integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==",
+ "dev": true,
+ "license": "ISC"
+ },
"node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
@@ -1617,6 +1626,19 @@
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
"dev": true
},
+ "node_modules/yaml": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz",
+ "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "yaml": "bin.mjs"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
"node_modules/yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
diff --git a/package.json b/package.json
index d7785a4..34dd342 100644
--- a/package.json
+++ b/package.json
@@ -23,12 +23,15 @@
"server": "HUGO_RESOURCEDIR='../resources' HUGO_ENV=development hugo server --logLevel info --source=exampleSite --buildDrafts --buildFuture --ignoreCache --disableFastRender",
"test": "playwright test",
"lint": "eslint static/js/* assets/js/* tests/*.spec.js eslint.config.js playwright.config.js netlify-preview.js",
+ "netlify-production": "node netlify-production.js",
"netlify-preview": "node netlify-preview.js"
},
"devDependencies": {
"@playwright/test": "^1.47.2",
"@types/node": "^22.7.3",
"eslint": "^9.11.1",
- "jsdom": "^25.0.1"
+ "fs": "^0.0.1-security",
+ "jsdom": "^25.0.1",
+ "yaml": "^2.5.1"
}
}