Skip to content

Commit 44415ba

Browse files
t0mdavid-mclaude
andauthored
Add Matomo analytics integration with GDPR consent support (#341)
* Add Matomo Tag Manager as third analytics tracking mode Adds Matomo Tag Manager support alongside existing Google Analytics and Piwik Pro integrations. Includes settings.json configuration (url + tag), build-time script injection via hook-analytics.py, Klaro GDPR consent banner integration, and runtime consent granting via MTM data layer API. https://claude.ai/code/session_0165AXHkmRZ6bx23n7Tbyz8h * Fix Matomo Tag Manager snippet to match official docs - Accept full container JS URL instead of separate url + tag fields, supporting both self-hosted and Matomo Cloud URL patterns - Match the official snippet: var _mtm alias, _mtm.push shorthand - Remove redundant type="text/javascript" attribute - Remove unused "tag" field from settings.json https://claude.ai/code/session_0165AXHkmRZ6bx23n7Tbyz8h * Split Matomo config into base url + tag fields Separate the Matomo setting into `url` (base URL, e.g. https://cdn.matomo.cloud/openms.matomo.cloud) and `tag` (container ID, e.g. yDGK8bfY), consistent with how other providers use a tag field. The script constructs the full path: {url}/container_{tag}.js https://claude.ai/code/session_0165AXHkmRZ6bx23n7Tbyz8h * install matomo tag --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent de0458a commit 44415ba

File tree

6 files changed

+58
-4
lines changed

6 files changed

+58
-4
lines changed

gdpr_consent/dist/bundle.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gdpr_consent/src/main.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,16 @@ function onRender(event: Event): void {
114114
}
115115
)
116116
}
117+
if (data.args['matomo']) {
118+
klaroConfig.services.push(
119+
{
120+
name: 'matomo',
121+
purposes: ['analytics'],
122+
onAccept: callback,
123+
onDecline: callback,
124+
}
125+
)
126+
}
117127

118128
// Create a new script element
119129
var script = document.createElement('script')

hooks/hook-analytics.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,21 @@ def piwik_pro_body(piwik_tag):
5656
"""
5757

5858

59+
def matomo_head(matomo_url, matomo_tag):
60+
return f"""
61+
<!-- Matomo Tag Manager -->
62+
<script>
63+
var _mtm = window._mtm = window._mtm || [];
64+
_mtm.push({{'mtm.startTime': (new Date().getTime()), 'event': 'mtm.Start'}});
65+
(function() {{
66+
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
67+
g.async=true; g.src='{matomo_url}/container_{matomo_tag}.js'; s.parentNode.insertBefore(g,s);
68+
}})();
69+
</script>
70+
<!-- End Matomo Tag Manager -->
71+
"""
72+
73+
5974
if __name__ == '__main__':
6075

6176
# Load configuration
@@ -79,6 +94,12 @@ def piwik_pro_body(piwik_tag):
7994
piwik_tag = settings['analytics']['piwik-pro']['tag']
8095
index = patch_body(index, piwik_pro_body(piwik_tag))
8196

97+
# Configure matomo tag manager
98+
if settings['analytics']['matomo']['enabled']:
99+
matomo_url = settings['analytics']['matomo']['url']
100+
matomo_tag = settings['analytics']['matomo']['tag']
101+
index = patch_head(index, matomo_head(matomo_url, matomo_tag))
102+
82103
# Save index.html
83104
with open(index_path, 'w') as f:
84105
f.write(index)

settings.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,13 @@
99
"tag": ""
1010
},
1111
"piwik-pro": {
12+
"enabled": false,
13+
"tag": ""
14+
},
15+
"matomo": {
1216
"enabled": true,
13-
"tag": "57690c44-d635-43b0-ab43-f8bd3064ca06"
17+
"url": "https://cdn.matomo.cloud/openms.matomo.cloud",
18+
"tag": "yDGK8bfY"
1419
}
1520
},
1621
"online_deployment": false,

src/common/captcha_.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -208,12 +208,13 @@ def captcha_control():
208208
# Check if consent for tracking was given
209209
ga = st.session_state.settings['analytics']['google-analytics']['enabled']
210210
pp = st.session_state.settings['analytics']['piwik-pro']['enabled']
211-
if (ga or pp) and (st.session_state.tracking_consent is None):
211+
mt = st.session_state.settings['analytics']['matomo']['enabled']
212+
if (ga or pp or mt) and (st.session_state.tracking_consent is None):
212213
consent_component = st_components.declare_component("gdpr_consent", path=Path("gdpr_consent"))
213214
with st.spinner():
214215
# Ask for consent
215216
st.session_state.tracking_consent = consent_component(
216-
google_analytics=ga, piwik_pro=pp
217+
google_analytics=ga, piwik_pro=pp, matomo=mt
217218
)
218219
if st.session_state.tracking_consent is None:
219220
# No response by user yet

src/common/common.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,23 @@ def page_setup(page: str = "") -> dict[str, Any]:
405405
width=1,
406406
height=1,
407407
)
408+
if (st.session_state.settings["analytics"]["matomo"]["enabled"]) and (
409+
st.session_state.tracking_consent["matomo"] == True
410+
):
411+
html(
412+
"""
413+
<!DOCTYPE html>
414+
<html lang="en">
415+
<head></head>
416+
<body><script>
417+
window.parent._mtm = window.parent._mtm || [];
418+
window.parent._mtm.push(['MTMSetConsentGiven']);
419+
</script></body>
420+
</html>
421+
""",
422+
width=1,
423+
height=1,
424+
)
408425

409426
# Determine the workspace for the current session
410427
if ("workspace" not in st.session_state) or (

0 commit comments

Comments
 (0)