diff --git a/src/app/bitstream-page/bitstream-download-page/bitstream-download-page.component.spec.ts b/src/app/bitstream-page/bitstream-download-page/bitstream-download-page.component.spec.ts index ec4854f8fff..9eec71fee5c 100644 --- a/src/app/bitstream-page/bitstream-download-page/bitstream-download-page.component.spec.ts +++ b/src/app/bitstream-page/bitstream-download-page/bitstream-download-page.component.spec.ts @@ -18,6 +18,7 @@ import { of as observableOf } from 'rxjs'; import { getForbiddenRoute } from '../../app-routing-paths'; import { AuthService } from '../../core/auth/auth.service'; import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; +import { ConfigurationDataService } from '../../core/data/configuration-data.service'; import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; import { SignpostingDataService } from '../../core/data/signposting-data.service'; import { HardRedirectService } from '../../core/services/hard-redirect.service'; @@ -112,7 +113,10 @@ describe('BitstreamDownloadPageComponent', () => { signpostingDataService = jasmine.createSpyObj('SignpostingDataService', { getLinks: observableOf([mocklink, mocklink2]), }); - matomoService = jasmine.createSpyObj('MatomoService', ['appendVisitorId']); + matomoService = jasmine.createSpyObj('MatomoService', { + appendVisitorId: observableOf(''), + isMatomoEnabled$: observableOf(true), + }); matomoService.appendVisitorId.and.callFake((link) => observableOf(link)); } @@ -132,6 +136,7 @@ describe('BitstreamDownloadPageComponent', () => { { provide: PLATFORM_ID, useValue: 'server' }, { provide: Location, useValue: location }, { provide: DSONameService, useValue: dsoNameService }, + { provide: ConfigurationDataService, useValue: {} }, ], }) .compileComponents(); @@ -227,4 +232,42 @@ describe('BitstreamDownloadPageComponent', () => { })); }); }); + + describe('when Matomo is enabled', () => { + beforeEach(waitForAsync(() => { + init(); + (matomoService.appendVisitorId as jasmine.Spy).and.callFake((link) => observableOf(link + '?visitorId=12345')); + initTestbed(); + })); + beforeEach(() => { + fixture = TestBed.createComponent(BitstreamDownloadPageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + it('should append visitor ID to the file link', waitForAsync(() => { + fixture.whenStable().then(() => { + expect(matomoService.appendVisitorId).toHaveBeenCalledWith('content-url-with-headers'); + expect(hardRedirectService.redirect).toHaveBeenCalledWith('content-url-with-headers?visitorId=12345'); + }); + })); + }); + + describe('when Matomo is not enabled', () => { + beforeEach(waitForAsync(() => { + init(); + (matomoService.isMatomoEnabled$ as jasmine.Spy).and.returnValue(observableOf(false)); + initTestbed(); + })); + beforeEach(() => { + fixture = TestBed.createComponent(BitstreamDownloadPageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + it('should not append visitor ID to the file link', waitForAsync(() => { + fixture.whenStable().then(() => { + expect(matomoService.appendVisitorId).not.toHaveBeenCalled(); + expect(hardRedirectService.redirect).toHaveBeenCalledWith('content-url-with-headers'); + }); + })); + }); }); diff --git a/src/app/bitstream-page/bitstream-download-page/bitstream-download-page.component.ts b/src/app/bitstream-page/bitstream-download-page/bitstream-download-page.component.ts index 7763ccd0aa5..6afa9c75532 100644 --- a/src/app/bitstream-page/bitstream-download-page/bitstream-download-page.component.ts +++ b/src/app/bitstream-page/bitstream-download-page/bitstream-download-page.component.ts @@ -6,6 +6,7 @@ import { import { Component, Inject, + inject, OnInit, PLATFORM_ID, } from '@angular/core'; @@ -30,6 +31,7 @@ import { import { getForbiddenRoute } from '../../app-routing-paths'; import { AuthService } from '../../core/auth/auth.service'; import { DSONameService } from '../../core/breadcrumbs/dso-name.service'; +import { ConfigurationDataService } from '../../core/data/configuration-data.service'; import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service'; import { FeatureID } from '../../core/data/feature-authorization/feature-id'; import { RemoteData } from '../../core/data/remote-data'; @@ -64,6 +66,8 @@ export class BitstreamDownloadPageComponent implements OnInit { bitstream$: Observable; bitstreamRD$: Observable>; + configService = inject(ConfigurationDataService); + constructor( private route: ActivatedRoute, protected router: Router, @@ -103,30 +107,33 @@ export class BitstreamDownloadPageComponent implements OnInit { switchMap((bitstream: Bitstream) => { const isAuthorized$ = this.authorizationService.isAuthorized(FeatureID.CanDownload, isNotEmpty(bitstream) ? bitstream.self : undefined); const isLoggedIn$ = this.auth.isAuthenticated(); - return observableCombineLatest([isAuthorized$, isLoggedIn$, accessToken$, observableOf(bitstream)]); + const isMatomoEnabled$ = this.matomoService.isMatomoEnabled$(); + return observableCombineLatest([isAuthorized$, isLoggedIn$, isMatomoEnabled$, accessToken$, observableOf(bitstream)]); }), - filter(([isAuthorized, isLoggedIn, accessToken, bitstream]: [boolean, boolean, string, Bitstream]) => (hasValue(isAuthorized) && hasValue(isLoggedIn)) || hasValue(accessToken)), + filter(([isAuthorized, isLoggedIn, isMatomoEnabled, accessToken, bitstream]: [boolean, boolean, boolean, string, Bitstream]) => (hasValue(isAuthorized) && hasValue(isLoggedIn)) || hasValue(accessToken)), take(1), - switchMap(([isAuthorized, isLoggedIn, accessToken, bitstream]: [boolean, boolean, string, Bitstream]) => { + switchMap(([isAuthorized, isLoggedIn, isMatomoEnabled, accessToken, bitstream]: [boolean, boolean, boolean, string, Bitstream]) => { if (isAuthorized && isLoggedIn) { return this.fileService.retrieveFileDownloadLink(bitstream._links.content.href).pipe( filter((fileLink) => hasValue(fileLink)), take(1), map((fileLink) => { - return [isAuthorized, isLoggedIn, bitstream, fileLink]; + return [isAuthorized, isLoggedIn, isMatomoEnabled, bitstream, fileLink]; })); } else if (hasValue(accessToken)) { - return [[isAuthorized, !isLoggedIn, bitstream, '', accessToken]]; + return [[isAuthorized, !isLoggedIn, isMatomoEnabled, bitstream, '', accessToken]]; } else { - return [[isAuthorized, isLoggedIn, bitstream, bitstream._links.content.href]]; + return [[isAuthorized, isLoggedIn, isMatomoEnabled, bitstream, bitstream._links.content.href]]; } }), - switchMap(([isAuthorized, isLoggedIn, bitstream, fileLink, accessToken]: [boolean, boolean, Bitstream, string, string]) => - this.matomoService.appendVisitorId(fileLink) - .pipe( + switchMap(([isAuthorized, isLoggedIn, isMatomoEnabled, bitstream, fileLink, accessToken]: [boolean, boolean, boolean, Bitstream, string, string]) => { + if (isMatomoEnabled) { + return this.matomoService.appendVisitorId(fileLink).pipe( map((fileLinkWithVisitorId) => [isAuthorized, isLoggedIn, bitstream, fileLinkWithVisitorId, accessToken]), - ), - ), + ); + } + return observableOf([isAuthorized, isLoggedIn, bitstream, fileLink, accessToken]); + }), ).subscribe(([isAuthorized, isLoggedIn, bitstream, fileLink, accessToken]: [boolean, boolean, Bitstream, string, string]) => { if (isAuthorized && isLoggedIn && isNotEmpty(fileLink)) { this.hardRedirectService.redirect(fileLink); diff --git a/src/app/shared/cookies/browser-orejime.service.ts b/src/app/shared/cookies/browser-orejime.service.ts index f52251773a9..b2d3b6a4c0a 100644 --- a/src/app/shared/cookies/browser-orejime.service.ts +++ b/src/app/shared/cookies/browser-orejime.service.ts @@ -30,6 +30,7 @@ import { NativeWindowService, } from '../../core/services/window.service'; import { getFirstCompletedRemoteData } from '../../core/shared/operators'; +import { MATOMO_ENABLED } from '../../statistics/matomo.service'; import { hasValue, isEmpty, @@ -90,7 +91,7 @@ export class BrowserOrejimeService extends OrejimeService { private readonly GOOGLE_ANALYTICS_SERVICE_NAME = 'google-analytics'; - private readonly MATOMO_ENABLED = 'matomo.enabled'; + private readonly MATOMO_ENABLED = MATOMO_ENABLED; /** * Initial Orejime configuration diff --git a/src/app/statistics/matomo.service.ts b/src/app/statistics/matomo.service.ts index 8ea89e974c6..88fc7f9476b 100644 --- a/src/app/statistics/matomo.service.ts +++ b/src/app/statistics/matomo.service.ts @@ -31,6 +31,8 @@ import { isNotEmpty } from '../shared/empty.util'; export const MATOMO_TRACKER_URL = 'matomo.tracker.url'; export const MATOMO_SITE_ID = 'matomo.request.siteid'; +export const MATOMO_ENABLED = 'matomo.enabled'; + /** * Service to manage Matomo analytics integration. * Handles initialization and consent management for Matomo tracking. @@ -143,6 +145,21 @@ export class MatomoService { ); } + /** + * Checks if Matomo tracking is enabled by retrieving the configuration property. + * @returns An Observable that emits a boolean indicating whether Matomo tracking is enabled. + */ + isMatomoEnabled$(): Observable { + return this.configService.findByPropertyName(MATOMO_ENABLED) + .pipe( + getFirstCompletedRemoteData(), + map((res: RemoteData) => { + return res.hasSucceeded && res.payload && isNotEmpty(res.payload.values) && + res.payload.values[0]?.toLowerCase() === 'true'; + }), + ); + } + /** * Appends the visitor ID as a query parameter to the given URL. * @param url - The original URL to modify