diff --git a/MATOMO_CONFIGURATION.md b/MATOMO_CONFIGURATION.md new file mode 100644 index 00000000000..bb952d8c676 --- /dev/null +++ b/MATOMO_CONFIGURATION.md @@ -0,0 +1,41 @@ +# Matomo Statistics Configuration + +## Enable/Disable Matomo + +### To Disable Matomo (Default) +Edit `src/environments/environment.ts`: +```typescript +matomo: { + enabled: false, + hostUrl: 'https://your-matomo-server.com/', + siteId: '1', + dimensionId: 1 +} +``` + +### To Enable Matomo +Edit `src/environments/environment.ts`: +```typescript +matomo: { + enabled: true, + hostUrl: 'https://your-matomo-server.com/', + siteId: '1', + dimensionId: 1 +} +``` + +## Files to Modify + +- **Development**: `src/environments/environment.ts` +- **Production**: `src/environments/environment.production.ts` +- **Global/Server-side**: `config/config.example.yml` (and `config/config.yml` in production) +- **Runtime (if applicable)**: `config.json` +> **Note:** The `enabled` flag for Matomo may be present in multiple configuration files. Ensure you update all relevant files (`environment.ts`, `environment.production.ts`, `config/config.example.yml`, and any runtime `config.json`) to avoid conflicts or unexpected behavior. + +## After Changes + +1. Restart the application +2. Clear browser cache + +When disabled: No Matomo scripts load, statistics page shows "disabled" message. +When enabled: Matomo tracking works normally. diff --git a/config/config.example.yml b/config/config.example.yml index 8b56711c7d2..c8500545df4 100644 --- a/config/config.example.yml +++ b/config/config.example.yml @@ -465,5 +465,6 @@ accessibility: cookieExpirationDuration: 7 matomo: + enabled: false hostUrl: http://localhost:8135/ siteId: 1 diff --git a/src/app/item-page/clarin-matomo-statistics/clarin-matomo-statistics.component.html b/src/app/item-page/clarin-matomo-statistics/clarin-matomo-statistics.component.html index 4e1e768abf7..ab179b15869 100644 --- a/src/app/item-page/clarin-matomo-statistics/clarin-matomo-statistics.component.html +++ b/src/app/item-page/clarin-matomo-statistics/clarin-matomo-statistics.component.html @@ -1,50 +1,56 @@
-
- - {{'item.matomo-statistics.info.message' | translate}} +
+ + Matomo statistics are currently disabled.
-
- - -
-
- {{chartMessage}} - -
-
- - -
-
- - - Filewise Statistics - - - - - - - -
{{fileData.value}}{{fileData.key}}
+
+
+ + {{'item.matomo-statistics.info.message' | translate}} +
+
+ + +
+
+ {{chartMessage}} + +
+
+ + +
+
+ + + Filewise Statistics + + + + + + + +
{{fileData.value}}{{fileData.key}}
+
diff --git a/src/app/item-page/clarin-matomo-statistics/clarin-matomo-statistics.component.ts b/src/app/item-page/clarin-matomo-statistics/clarin-matomo-statistics.component.ts index 8f73e51e766..858d26a7e7a 100644 --- a/src/app/item-page/clarin-matomo-statistics/clarin-matomo-statistics.component.ts +++ b/src/app/item-page/clarin-matomo-statistics/clarin-matomo-statistics.component.ts @@ -10,6 +10,7 @@ import { map } from 'rxjs/operators'; import { ActivatedRoute } from '@angular/router'; import { RemoteData } from '../../core/data/remote-data'; import { Item } from '../../core/shared/item.model'; +import { environment } from '../../../environments/environment'; @Component({ selector: 'ds-clarin-matomo-statistics', @@ -27,6 +28,8 @@ export class ClarinMatomoStatisticsComponent implements OnInit { itemRD$: BehaviorSubject = new BehaviorSubject(null); + public matomoEnabled = false; + // Month shortcut with full name public months = [ ['Jan', 'January'], @@ -130,6 +133,13 @@ export class ClarinMatomoStatisticsComponent implements OnInit { ngOnInit(): void { + this.matomoEnabled = environment.matomo?.enabled === true; + + // If Matomo is disabled, don't fetch statistics + if (!this.matomoEnabled) { + return; + } + this.route.data.pipe( map((data) => data.dso as RemoteData)) .subscribe(data => { diff --git a/src/config/default-app-config.ts b/src/config/default-app-config.ts index 38cb942e457..2fff7f31fc3 100644 --- a/src/config/default-app-config.ts +++ b/src/config/default-app-config.ts @@ -452,6 +452,7 @@ export class DefaultAppConfig implements AppConfig { // Matomo configuration matomo: MatomoConfig = { + enabled: false, hostUrl: 'http://localhost:8135/', siteId: '1', dimensionId: 1 diff --git a/src/config/matomo-config.ts b/src/config/matomo-config.ts index 548f02aee4c..cc71a3cb6fc 100644 --- a/src/config/matomo-config.ts +++ b/src/config/matomo-config.ts @@ -5,6 +5,8 @@ import { Config } from './config.interface'; */ export class MatomoConfig implements Config { + public enabled: boolean; + public hostUrl: string; public siteId: string; diff --git a/src/environments/environment.test.ts b/src/environments/environment.test.ts index a0cab16ef83..ce1998535d6 100644 --- a/src/environments/environment.test.ts +++ b/src/environments/environment.test.ts @@ -356,6 +356,7 @@ export const environment: BuildConfig = { signpostingEnabled: true, matomo: { + enabled: false, hostUrl: 'http://localhost:8135/', siteId: '1', dimensionId: 1 diff --git a/src/environments/environment.ts b/src/environments/environment.ts index 8be4ee7dbfe..a229e75036b 100644 --- a/src/environments/environment.ts +++ b/src/environments/environment.ts @@ -49,6 +49,14 @@ export const environment: Partial = { }, signpostingEnabled: false, + + // Matomo configuration + matomo: { + enabled: false, + hostUrl: 'http://localhost:8135/', + siteId: '1', + dimensionId: 1 + }, }; /* diff --git a/src/main.browser.ts b/src/main.browser.ts index d17101d07a6..49e9242b8ab 100644 --- a/src/main.browser.ts +++ b/src/main.browser.ts @@ -27,9 +27,9 @@ const main = () => { enableProdMode(); } - addMatomoStatistics(); if (hasTransferState) { // Configuration will be taken from transfer state during initialization + addMatomoStatistics(); return bootstrap(); } else { // Configuration must be fetched explicitly @@ -38,36 +38,41 @@ const main = () => { .then((appConfig: AppConfig) => { // extend environment with app config for browser when not prerendered extendEnvironmentWithAppConfig(environment, appConfig); + addMatomoStatistics(); return bootstrap(); }); } }; function addMatomoStatistics() { - (window as any)._paq = (window as any)._paq || []; + // Check if Matomo is configured with required properties + if (!environment.matomo || !environment.matomo.hostUrl || !environment.matomo.siteId) { + return; + } - void fetch('assets/config.json') - .then((response) => response.json()) - .then((config) => { - const matomoConfig = config.matomo; + try { + (window as any)._paq = (window as any)._paq || []; - // Push all configuration commands first - (window as any)._paq.push(['setTrackerUrl', matomoConfig.hostUrl + 'matomo.php']); - (window as any)._paq.push(['setSiteId', matomoConfig.siteId]); - (window as any)._paq.push(['enableLinkTracking']); + // Push all configuration commands first + (window as any)._paq.push(['setTrackerUrl', environment.matomo.hostUrl + 'matomo.php']); + (window as any)._paq.push(['setSiteId', environment.matomo.siteId]); + (window as any)._paq.push(['enableLinkTracking']); - const g = document.createElement('script'); - g.type = 'text/javascript'; - g.async = true; - g.defer = true; - g.src = matomoConfig.hostUrl + 'matomo.js'; - document.getElementsByTagName('head')[0].appendChild(g); - }); + const g = document.createElement('script'); + g.type = 'text/javascript'; + g.async = true; + g.defer = true; + g.src = environment.matomo.hostUrl + 'matomo.js'; + document.getElementsByTagName('head')[0].appendChild(g); + } catch (error) { + console.error('Matomo initialization failed:', error); + return; + } } // support async tag or hmr if (document.readyState === 'complete' && !hasTransferState) { - main(); + void main(); } else { - document.addEventListener('DOMContentLoaded', main); + document.addEventListener('DOMContentLoaded', () => void main()); } diff --git a/src/modules/app/browser-init.service.ts b/src/modules/app/browser-init.service.ts index 905485637d6..fd7716e570a 100644 --- a/src/modules/app/browser-init.service.ts +++ b/src/modules/app/browser-init.service.ts @@ -107,26 +107,36 @@ export class BrowserInitService extends InitService { this.initRouteListeners(); this.themeService.listenForThemeChanges(true); this.trackAuthTokenExpiration(); - // ideally we'd add the custom dimension to the 'trackPageView' action only, but don't have that information - // in pageTrack context. So we add it to page_view events, and remove it after the page view. - // page_view events are fired via view-track.component, and exposes dc.identifier.uri via properties - this.angulartics2Matomo.eventTrack = function (action: string, properties?: any) { - if (action === 'page_view') { - if (properties.dc_identifier) { - (window as any)._paq.push(['setCustomDimension', environment.matomo.dimensionId, properties.dc_identifier]); - } + + // Only initialize Matomo if it's configured with required properties + if (this.appConfig.matomo && this.appConfig.matomo.hostUrl && this.appConfig.matomo.siteId) { + // ideally we'd add the custom dimension to the 'trackPageView' action only, but don't have that information + // in pageTrack context. So we add it to page_view events, and remove it after the page view. + // page_view events are fired via view-track.component, and exposes dc.identifier.uri via properties + const dimensionId = this.appConfig.matomo.dimensionId; + + // Only set up custom dimensions if dimensionId is configured + if (dimensionId) { + this.angulartics2Matomo.eventTrack = function (action: string, properties?: any) { + if (action === 'page_view') { + if (properties.dc_identifier) { + (window as any)._paq.push(['setCustomDimension', dimensionId, properties.dc_identifier]); + } + } + }; + let pageTrack = this.angulartics2Matomo.pageTrack; + this.angulartics2Matomo.pageTrack = function (path: string) { + pageTrack.call(this, path); + (window as any)._paq.push(['deleteCustomDimension', dimensionId]); + }; } - }; - let pageTrack = this.angulartics2Matomo.pageTrack; - this.angulartics2Matomo.pageTrack = function (path: string) { - pageTrack.call(this, path); - (window as any)._paq.push(['deleteCustomDimension', environment.matomo.dimensionId]); - }; - this.angulartics2Matomo.startTracking(); + + this.angulartics2Matomo.startTracking(); + } this.initKlaro(); - await this.authenticationReady$().toPromise(); + await firstValueFrom(this.authenticationReady$()); return true; }; @@ -197,7 +207,7 @@ export class BrowserInitService extends InitService { * @private */ private closeAuthCheckSubscription() { - firstValueFrom(this.authenticationReady$()).then(() => { + void firstValueFrom(this.authenticationReady$()).then(() => { this.sub.unsubscribe(); }); }