Skip to content
41 changes: 41 additions & 0 deletions MATOMO_CONFIGURATION.md
Original file line number Diff line number Diff line change
@@ -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.

Comment thread
Paurikova2 marked this conversation as resolved.
## 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.
1 change: 1 addition & 0 deletions config/config.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -465,5 +465,6 @@ accessibility:
cookieExpirationDuration: 7

matomo:
enabled: false
hostUrl: http://localhost:8135/
siteId: 1
Original file line number Diff line number Diff line change
@@ -1,50 +1,56 @@
<div class="container">
<div class="row px-2 py-2 rounded alert-info">
<i class="fas fa-info-circle pt-1"></i>
<span class="pl-2">{{'item.matomo-statistics.info.message' | translate}}</span>
<div *ngIf="!matomoEnabled" class="row px-2 py-2 rounded alert-warning">
<i class="fas fa-exclamation-triangle pt-1"></i>
Comment thread
Paurikova2 marked this conversation as resolved.
Outdated
<span class="pl-2">Matomo statistics are currently disabled.</span>
</div>
<div class="row pt-4 clarin-bottom-line">
<button type="button" class="col-md-2 clarin-chart-button btn btn-outline-success justify-content-start py-2 px-3"
[ngClass]="viewsButtonClicked ? 'clarin-chart-views-clicked' : 'clarin-chart-views'"
role="button"
data-bs-toggle="button"
(click)="toggleViews()">
<div class="align-items-center justify-content-center d-flex"><i class="far fa-eye"></i></div>
<div class="align-items-center justify-content-center d-flex">{{'item.page.matomo-statistics.views.button' | translate}}</div>
</button>
<button type="button" class="col-md-2 clarin-chart-button btn btn-outline-info py-2 px-3 ml-1"
[ngClass]="downloadsButtonClicked ? 'clarin-chart-downloads-clicked' : 'clarin-chart-downloads'"
data-bs-toggle="button"
(click)="toggleDownload()">
<div class="align-items-center justify-content-center d-flex"><i class="far fa-eye"></i></div>
<div class="align-items-center justify-content-center d-flex">{{'item.page.matomo-statistics.downloads.button' | translate}}</div>
</button>
</div>
<div class="row pt-2 justify-content-end">
<span class="chart-message rounded px-2 py-2">{{chartMessage}}</span>
<button class="btn clarin-btn-back rounded px-2 py-2 ml-2" *ngIf="!(this.actualPeriod === this.periodYear)" (click)="back()">Back</button>
</div>
<div class="chart pt-2">
<canvas baseChart
[datasets]="chartLabels.datasets"
[labels]="chartLabels.labels"
[options]="chartOptions"
[legend]="false"
(chartClick)="chartClicked($event)"
chartType="line">
</canvas>
</div>
<div *ngIf="downloadsButtonClicked" class="pt-3">
<table class="table table-striped">
<thead>
Filewise Statistics
</thead>
<tbody>
<tr *ngFor="let fileData of (filesDownloads | async) | dsKeys">
<td><b>{{fileData.value}}</b></td>
<td>{{fileData.key}}</td>
</tr>
</tbody>
</table>
<div *ngIf="matomoEnabled">
<div class="row px-2 py-2 rounded alert-info">
<i class="fas fa-info-circle pt-1"></i>
<span class="pl-2">{{'item.matomo-statistics.info.message' | translate}}</span>
</div>
<div class="row pt-4 clarin-bottom-line">
<button type="button" class="col-md-2 clarin-chart-button btn btn-outline-success justify-content-start py-2 px-3"
[ngClass]="viewsButtonClicked ? 'clarin-chart-views-clicked' : 'clarin-chart-views'"
role="button"
data-bs-toggle="button"
(click)="toggleViews()">
<div class="align-items-center justify-content-center d-flex"><i class="far fa-eye"></i></div>
<div class="align-items-center justify-content-center d-flex">{{'item.page.matomo-statistics.views.button' | translate}}</div>
</button>
<button type="button" class="col-md-2 clarin-chart-button btn btn-outline-info py-2 px-3 ml-1"
[ngClass]="downloadsButtonClicked ? 'clarin-chart-downloads-clicked' : 'clarin-chart-downloads'"
data-bs-toggle="button"
(click)="toggleDownload()">
<div class="align-items-center justify-content-center d-flex"><i class="far fa-eye"></i></div>
<div class="align-items-center justify-content-center d-flex">{{'item.page.matomo-statistics.downloads.button' | translate}}</div>
</button>
</div>
<div class="row pt-2 justify-content-end">
<span class="chart-message rounded px-2 py-2">{{chartMessage}}</span>
<button class="btn clarin-btn-back rounded px-2 py-2 ml-2" *ngIf="!(this.actualPeriod === this.periodYear)" (click)="back()">Back</button>
</div>
<div class="chart pt-2">
<canvas baseChart
[datasets]="chartLabels.datasets"
[labels]="chartLabels.labels"
[options]="chartOptions"
[legend]="false"
(chartClick)="chartClicked($event)"
chartType="line">
</canvas>
</div>
<div *ngIf="downloadsButtonClicked" class="pt-3">
<table class="table table-striped">
<thead>
Filewise Statistics
</thead>
<tbody>
<tr *ngFor="let fileData of (filesDownloads | async) | dsKeys">
<td><b>{{fileData.value}}</b></td>
<td>{{fileData.key}}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
Comment thread
Paurikova2 marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -26,6 +27,9 @@ export class ClarinMatomoStatisticsComponent implements OnInit {
@ViewChild(BaseChartDirective, { static: true }) chart: BaseChartDirective;

itemRD$: BehaviorSubject<Item> = new BehaviorSubject<Item>(null);

// Flag to track if Matomo is enabled
public matomoEnabled = false;

// Month shortcut with full name
public months = [
Expand Down Expand Up @@ -130,6 +134,14 @@ export class ClarinMatomoStatisticsComponent implements OnInit {


ngOnInit(): void {
// Check if Matomo is enabled in the environment
this.matomoEnabled = environment.matomo?.enabled || false;
Comment thread
Paurikova2 marked this conversation as resolved.
Outdated

// If Matomo is disabled, don't fetch statistics
if (!this.matomoEnabled) {
return;
}

this.route.data.pipe(
map((data) => data.dso as RemoteData<Item>))
.subscribe(data => {
Expand Down
1 change: 1 addition & 0 deletions src/config/default-app-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,7 @@ export class DefaultAppConfig implements AppConfig {

// Matomo configuration
matomo: MatomoConfig = {
enabled: false,
hostUrl: 'http://localhost:8135/',
siteId: '1',
dimensionId: 1
Expand Down
2 changes: 2 additions & 0 deletions src/config/matomo-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { Config } from './config.interface';
*/
export class MatomoConfig implements Config {

public enabled: boolean;

public hostUrl: string;

public siteId: string;
Expand Down
1 change: 1 addition & 0 deletions src/environments/environment.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@ export const environment: BuildConfig = {
signpostingEnabled: true,

matomo: {
enabled: false,
hostUrl: 'http://localhost:8135/',
siteId: '1',
dimensionId: 1
Expand Down
8 changes: 8 additions & 0 deletions src/environments/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ export const environment: Partial<BuildConfig> = {
},

signpostingEnabled: false,

// Matomo configuration
matomo: {
enabled: false,
hostUrl: 'http://localhost:8135/',
siteId: '1',
dimensionId: 1
},
};

/*
Expand Down
56 changes: 37 additions & 19 deletions src/main.browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -38,36 +38,54 @@ 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 || [];
// Debug: Log the matomo configuration
if (!environment.production) {
console.log('Matomo config check:', {
hasMatomo: !!environment.matomo,
enabled: environment.matomo?.enabled,
full: environment.matomo
});
}
Comment thread
Paurikova2 marked this conversation as resolved.
Outdated

// Check if Matomo is enabled in the environment configuration
if (!environment.matomo || !environment.matomo.enabled) {
if (!environment.production) {
console.log('Matomo is disabled, skipping initialization');
}
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) {
if (!environment.production) {
console.error('Error initializing Matomo:', error);
}
}
}

// support async tag or hmr
if (document.readyState === 'complete' && !hasTransferState) {
main();
void main();
} else {
document.addEventListener('DOMContentLoaded', main);
document.addEventListener('DOMContentLoaded', () => void main());
Comment thread
Paurikova2 marked this conversation as resolved.
Comment thread
Paurikova2 marked this conversation as resolved.
Comment thread
Paurikova2 marked this conversation as resolved.
Comment thread
Paurikova2 marked this conversation as resolved.
}
39 changes: 22 additions & 17 deletions src/modules/app/browser-init.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,26 +107,31 @@ 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 enabled in configuration
if (this.appConfig.matomo && this.appConfig.matomo.enabled) {
Comment thread
Paurikova2 marked this conversation as resolved.
Outdated
// 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;
Comment thread
Paurikova2 marked this conversation as resolved.
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', environment.matomo.dimensionId]);
};
this.angulartics2Matomo.startTracking();
};
let pageTrack = this.angulartics2Matomo.pageTrack;
this.angulartics2Matomo.pageTrack = function (path: string) {
pageTrack.call(this, path);
(window as any)._paq.push(['deleteCustomDimension', dimensionId]);
};
this.angulartics2Matomo.startTracking();
}

this.initKlaro();

await this.authenticationReady$().toPromise();
await firstValueFrom(this.authenticationReady$());

return true;
};
Expand Down Expand Up @@ -197,7 +202,7 @@ export class BrowserInitService extends InitService {
* @private
*/
private closeAuthCheckSubscription() {
firstValueFrom(this.authenticationReady$()).then(() => {
void firstValueFrom(this.authenticationReady$()).then(() => {
this.sub.unsubscribe();
});
}
Expand Down
Loading