Add Matomo analytics integration with GDPR consent support#341
Add Matomo analytics integration with GDPR consent support#341t0mdavid-m merged 4 commits intomainfrom
Conversation
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
|
No actionable comments were generated in the recent review. 🎉 📝 WalkthroughWalkthroughAdds Matomo analytics support: new Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant Page
participant Config
participant HeadInjector
participant ConsentChecker
participant GDPRUI
participant RuntimeHandler
participant Matomo
User->>Page: Request page
Page->>Config: Read analytics settings (includes matomo)
alt matomo.enabled == true
Page->>HeadInjector: Insert Matomo Tag Manager (matomo_head)
Page->>ConsentChecker: Get tracking_consent
ConsentChecker->>GDPRUI: Render consent UI (includes Matomo)
GDPRUI-->>User: Show banner / choices
User-->>GDPRUI: Accept or Decline
GDPRUI-->>ConsentChecker: Return consent result
ConsentChecker-->>Page: Store consent and trigger rerun if needed
alt consent granted
Page->>RuntimeHandler: Execute Matomo consent script
RuntimeHandler->>Matomo: Push MTMSetConsentGiven
else consent denied
Note right of Matomo: Do not enable tracking
end
else
Note right of Page: Skip Matomo injection and consent
end
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@hooks/hook-analytics.py`:
- Around line 59-72: The matomo_head function can produce a double-slash when
matomo_url ends with '/', so update matomo_head to normalize matomo_url by
stripping any trailing slashes (e.g., using matomo_url.rstrip('/')) before
interpolating it into the script src; use the normalized value when building the
container URL (container_{matomo_tag}.js) so the final src is always
".../js/container_{matomo_tag}.js" with a single slash regardless of input.
In `@src/common/common.py`:
- Around line 408-410: The conditional uses an explicit equality check
"st.session_state.tracking_consent['matomo'] == True" which triggers Ruff E712;
change it to a truthiness check so the condition reads as a boolean expression
(e.g., use the existing left-hand check with just
st.session_state.tracking_consent['matomo']), and apply the same change pattern
to the similar GA and Piwik Pro checks; edit the conditional containing
st.session_state.settings["analytics"]["matomo"]["enabled"] and
st.session_state.tracking_consent["matomo"] to remove the "== True" comparison
and rely on truthiness instead.
| def matomo_head(matomo_url, matomo_tag): | ||
| return f""" | ||
| <!-- Matomo Tag Manager --> | ||
| <script type="text/javascript"> | ||
| window._mtm = window._mtm || []; | ||
| window._mtm.push({{'mtm.startTime': (new Date().getTime()), 'event': 'mtm.Start'}}); | ||
| (function() {{ | ||
| var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0]; | ||
| g.async=true; g.src='{matomo_url}/js/container_{matomo_tag}.js'; | ||
| s.parentNode.insertBefore(g,s); | ||
| }})(); | ||
| </script> | ||
| <!-- End Matomo Tag Manager --> | ||
| """ |
There was a problem hiding this comment.
Strip trailing slash from matomo_url to avoid a double-slash URL.
If matomo_url is configured as https://analytics.example.com/ (with a trailing slash), line 67 produces https://analytics.example.com//js/container_{tag}.js. While many servers normalize double slashes, some CDNs and strict reverse proxies will reject them, causing the MTM container script to fail silently.
🔧 Proposed fix
def matomo_head(matomo_url, matomo_tag):
+ matomo_url = matomo_url.rstrip('/')
return f"""
<!-- Matomo Tag Manager -->
<script type="text/javascript">
window._mtm = window._mtm || [];
window._mtm.push({{'mtm.startTime': (new Date().getTime()), 'event': 'mtm.Start'}});
(function() {{
var d=document, g=d.createElement('script'), s=d.getElementsByTagName('script')[0];
g.async=true; g.src='{matomo_url}/js/container_{matomo_tag}.js';
s.parentNode.insertBefore(g,s);
}})();
</script>
<!-- End Matomo Tag Manager -->
"""🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@hooks/hook-analytics.py` around lines 59 - 72, The matomo_head function can
produce a double-slash when matomo_url ends with '/', so update matomo_head to
normalize matomo_url by stripping any trailing slashes (e.g., using
matomo_url.rstrip('/')) before interpolating it into the script src; use the
normalized value when building the container URL (container_{matomo_tag}.js) so
the final src is always ".../js/container_{matomo_tag}.js" with a single slash
regardless of input.
| if (st.session_state.settings["analytics"]["matomo"]["enabled"]) and ( | ||
| st.session_state.tracking_consent["matomo"] == True | ||
| ): |
There was a problem hiding this comment.
Replace == True equality comparison with a truthiness check (Ruff E712).
Ruff flags this as E712. The same pattern exists for the pre-existing GA and Piwik Pro blocks (lines 368, 386), but should be fixed here in the new code.
🔧 Proposed fix
- if (st.session_state.settings["analytics"]["matomo"]["enabled"]) and (
- st.session_state.tracking_consent["matomo"] == True
- ):
+ if (st.session_state.settings["analytics"]["matomo"]["enabled"]) and (
+ st.session_state.tracking_consent["matomo"]
+ ):📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (st.session_state.settings["analytics"]["matomo"]["enabled"]) and ( | |
| st.session_state.tracking_consent["matomo"] == True | |
| ): | |
| if (st.session_state.settings["analytics"]["matomo"]["enabled"]) and ( | |
| st.session_state.tracking_consent["matomo"] | |
| ): |
🧰 Tools
🪛 Ruff (0.15.1)
[error] 409-409: Avoid equality comparisons to True; use st.session_state.tracking_consent["matomo"]: for truth checks
Replace with st.session_state.tracking_consent["matomo"]
(E712)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/common/common.py` around lines 408 - 410, The conditional uses an
explicit equality check "st.session_state.tracking_consent['matomo'] == True"
which triggers Ruff E712; change it to a truthiness check so the condition reads
as a boolean expression (e.g., use the existing left-hand check with just
st.session_state.tracking_consent['matomo']), and apply the same change pattern
to the similar GA and Piwik Pro checks; edit the conditional containing
st.session_state.settings["analytics"]["matomo"]["enabled"] and
st.session_state.tracking_consent["matomo"] to remove the "== True" comparison
and rely on truthiness instead.
- 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
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@settings.json`:
- Around line 11-13: The Piwik Pro analytics config in settings.json currently
contains a real container ID and is enabled; update the "piwik-pro" block by
setting "enabled" to false and replacing the live "tag" value
("57690c44-d635-43b0-ab43-f8bd3064ca06") with a placeholder like
"YOUR_PIWIK_PRO_TAG_HERE" so templates do not send data to the original account;
ensure the changes are made to the "piwik-pro" object keys "enabled" and "tag".
- Around line 15-18: The matomo config can produce a broken <script src="">
because hook-analytics.py's matomo_head() interpolates matomo_url without
validation; update hook-analytics.py to check the matomo["url"] (and enabled
flag) and only generate/inject the Matomo script when the URL is non-empty and
matches a simple URL pattern (e.g., starts with http:// or https:// and contains
a domain), returning early or skipping injection otherwise, and add a clear
error/info log when enabled is true but URL is missing/invalid; alternatively
(or additionally) add README docs describing the matomo.url expected value (full
container script URL like https://<your-domain>/js/container_<id>.js) and note
the field-name asymmetry versus tag for other providers.
settings.json
Outdated
| "piwik-pro": { | ||
| "enabled": true, | ||
| "tag": "57690c44-d635-43b0-ab43-f8bd3064ca06" |
There was a problem hiding this comment.
Pre-existing: real Piwik Pro container ID committed in a public template
"tag": "57690c44-d635-43b0-ab43-f8bd3064ca06" with "enabled": true appears to be a live container ID. In a public template repository, every fork/deployment that doesn't override this value will silently send analytics to the original account. Since the analytics block is being touched in this PR, this is a good opportunity to replace the real tag with a placeholder (e.g., "YOUR_PIWIK_PRO_TAG_HERE") and flip enabled to false, matching the pattern used by the other two providers.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@settings.json` around lines 11 - 13, The Piwik Pro analytics config in
settings.json currently contains a real container ID and is enabled; update the
"piwik-pro" block by setting "enabled" to false and replacing the live "tag"
value ("57690c44-d635-43b0-ab43-f8bd3064ca06") with a placeholder like
"YOUR_PIWIK_PRO_TAG_HERE" so templates do not send data to the original account;
ensure the changes are made to the "piwik-pro" object keys "enabled" and "tag".
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
There was a problem hiding this comment.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@hooks/hook-analytics.py`:
- Around line 59-71: The matomo_head function can produce a double-slash when
matomo_url ends with '/', so normalize matomo_url by stripping trailing slashes
before using it in the template (e.g., compute a local variable like base_url =
matomo_url.rstrip('/') and use base_url when building the container script src).
Update matomo_head to use the normalized URL (reference: matomo_head(matomo_url,
matomo_tag)) so the generated src is always single-slash between host and
container path.
Summary
This PR adds support for Matomo Tag Manager as an analytics provider alongside existing Google Analytics and Piwik Pro integrations. The implementation includes GDPR consent management integration to ensure user privacy compliance.
Key Changes
hooks/hook-analytics.py): Addedmatomo_head()function to inject Matomo Tag Manager script into the HTML head section with proper initializationsettings.json): Added Matomo configuration section withenabled,url, andtagfields (disabled by default)src/common/captcha_.py): Extended consent flow to include Matomo alongside existing analytics providers, passing matomo flag to GDPR consent componentsrc/common/common.py): Added Matomo consent tracking that pushesMTMSetConsentGivenevent to Matomo when user grants consentgdpr_consent/src/main.ts): Extended Klaro configuration to register Matomo as a service with analytics purpose and consent callbacksgdpr_consent/dist/bundle.js): Compiled bundle reflecting TypeScript changes to consent componentImplementation Details
https://claude.ai/code/session_0165AXHkmRZ6bx23n7Tbyz8h
Summary by CodeRabbit