Skip to content

Commit a099e15

Browse files
Andrea Barbassovins01-4science
authored andcommitted
[DURACOM-339] fix downloads not working when matomo is disabled
1 parent 8c2dc24 commit a099e15

4 files changed

Lines changed: 91 additions & 24 deletions

File tree

src/app/bitstream-page/bitstream-download-page/bitstream-download-page.component.spec.ts

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { of as observableOf } from 'rxjs';
1818
import { getForbiddenRoute } from '../../app-routing-paths';
1919
import { AuthService } from '../../core/auth/auth.service';
2020
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
21+
import { ConfigurationDataService } from '../../core/data/configuration-data.service';
2122
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
2223
import { SignpostingDataService } from '../../core/data/signposting-data.service';
2324
import { HardRedirectService } from '../../core/services/hard-redirect.service';
@@ -91,16 +92,16 @@ describe('BitstreamDownloadPageComponent', () => {
9192
self: { href: 'bitstream-self-link' },
9293
},
9394
});
94-
9595
activatedRoute = {
9696
data: observableOf({
97-
bitstream: createSuccessfulRemoteDataObject(
98-
bitstream,
99-
),
97+
bitstream: createSuccessfulRemoteDataObject(bitstream),
10098
}),
10199
params: observableOf({
102100
id: 'testid',
103101
}),
102+
queryParams: observableOf({
103+
accessToken: undefined,
104+
}),
104105
};
105106

106107
router = jasmine.createSpyObj('router', ['navigateByUrl']);
@@ -112,7 +113,10 @@ describe('BitstreamDownloadPageComponent', () => {
112113
signpostingDataService = jasmine.createSpyObj('SignpostingDataService', {
113114
getLinks: observableOf([mocklink, mocklink2]),
114115
});
115-
matomoService = jasmine.createSpyObj('MatomoService', ['appendVisitorId']);
116+
matomoService = jasmine.createSpyObj('MatomoService', {
117+
appendVisitorId: observableOf(''),
118+
isMatomoEnabled$: observableOf(true),
119+
});
116120
matomoService.appendVisitorId.and.callFake((link) => observableOf(link));
117121
}
118122

@@ -132,6 +136,7 @@ describe('BitstreamDownloadPageComponent', () => {
132136
{ provide: PLATFORM_ID, useValue: 'server' },
133137
{ provide: Location, useValue: location },
134138
{ provide: DSONameService, useValue: dsoNameService },
139+
{ provide: ConfigurationDataService, useValue: {} },
135140
],
136141
})
137142
.compileComponents();
@@ -227,4 +232,42 @@ describe('BitstreamDownloadPageComponent', () => {
227232
}));
228233
});
229234
});
235+
236+
describe('when Matomo is enabled', () => {
237+
beforeEach(waitForAsync(() => {
238+
init();
239+
(matomoService.appendVisitorId as jasmine.Spy).and.callFake((link) => observableOf(link + '?visitorId=12345'));
240+
initTestbed();
241+
}));
242+
beforeEach(() => {
243+
fixture = TestBed.createComponent(BitstreamDownloadPageComponent);
244+
component = fixture.componentInstance;
245+
fixture.detectChanges();
246+
});
247+
it('should append visitor ID to the file link', waitForAsync(() => {
248+
fixture.whenStable().then(() => {
249+
expect(matomoService.appendVisitorId).toHaveBeenCalledWith('content-url-with-headers');
250+
expect(hardRedirectService.redirect).toHaveBeenCalledWith('content-url-with-headers?visitorId=12345');
251+
});
252+
}));
253+
});
254+
255+
describe('when Matomo is not enabled', () => {
256+
beforeEach(waitForAsync(() => {
257+
init();
258+
(matomoService.isMatomoEnabled$ as jasmine.Spy).and.returnValue(observableOf(false));
259+
initTestbed();
260+
}));
261+
beforeEach(() => {
262+
fixture = TestBed.createComponent(BitstreamDownloadPageComponent);
263+
component = fixture.componentInstance;
264+
fixture.detectChanges();
265+
});
266+
it('should not append visitor ID to the file link', waitForAsync(() => {
267+
fixture.whenStable().then(() => {
268+
expect(matomoService.appendVisitorId).not.toHaveBeenCalled();
269+
expect(hardRedirectService.redirect).toHaveBeenCalledWith('content-url-with-headers');
270+
});
271+
}));
272+
});
230273
});

src/app/bitstream-page/bitstream-download-page/bitstream-download-page.component.ts

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ import {
66
import {
77
Component,
88
Inject,
9+
inject,
910
OnInit,
1011
PLATFORM_ID,
1112
} from '@angular/core';
1213
import {
1314
ActivatedRoute,
15+
Params,
1416
Router,
1517
} from '@angular/router';
1618
import { TranslateModule } from '@ngx-translate/core';
@@ -29,6 +31,7 @@ import {
2931
import { getForbiddenRoute } from '../../app-routing-paths';
3032
import { AuthService } from '../../core/auth/auth.service';
3133
import { DSONameService } from '../../core/breadcrumbs/dso-name.service';
34+
import { ConfigurationDataService } from '../../core/data/configuration-data.service';
3235
import { AuthorizationDataService } from '../../core/data/feature-authorization/authorization-data.service';
3336
import { FeatureID } from '../../core/data/feature-authorization/feature-id';
3437
import { RemoteData } from '../../core/data/remote-data';
@@ -63,6 +66,8 @@ export class BitstreamDownloadPageComponent implements OnInit {
6366
bitstream$: Observable<Bitstream>;
6467
bitstreamRD$: Observable<RemoteData<Bitstream>>;
6568

69+
configService = inject(ConfigurationDataService);
70+
6671
constructor(
6772
private route: ActivatedRoute,
6873
protected router: Router,
@@ -85,6 +90,10 @@ export class BitstreamDownloadPageComponent implements OnInit {
8590
}
8691

8792
ngOnInit(): void {
93+
const accessToken$: Observable<string> = this.route.queryParams.pipe(
94+
map((queryParams: Params) => queryParams?.accessToken || null),
95+
take(1),
96+
);
8897

8998
this.bitstreamRD$ = this.route.data.pipe(
9099
map((data) => data.bitstream));
@@ -98,38 +107,49 @@ export class BitstreamDownloadPageComponent implements OnInit {
98107
switchMap((bitstream: Bitstream) => {
99108
const isAuthorized$ = this.authorizationService.isAuthorized(FeatureID.CanDownload, isNotEmpty(bitstream) ? bitstream.self : undefined);
100109
const isLoggedIn$ = this.auth.isAuthenticated();
101-
return observableCombineLatest([isAuthorized$, isLoggedIn$, observableOf(bitstream)]);
110+
const isMatomoEnabled$ = this.matomoService.isMatomoEnabled$();
111+
return observableCombineLatest([isAuthorized$, isLoggedIn$, isMatomoEnabled$, accessToken$, observableOf(bitstream)]);
102112
}),
103-
filter(([isAuthorized, isLoggedIn, bitstream]: [boolean, boolean, Bitstream]) => hasValue(isAuthorized) && hasValue(isLoggedIn)),
113+
filter(([isAuthorized, isLoggedIn, isMatomoEnabled, accessToken, bitstream]: [boolean, boolean, boolean, string, Bitstream]) => (hasValue(isAuthorized) && hasValue(isLoggedIn)) || hasValue(accessToken)),
104114
take(1),
105-
switchMap(([isAuthorized, isLoggedIn, bitstream]: [boolean, boolean, Bitstream]) => {
115+
switchMap(([isAuthorized, isLoggedIn, isMatomoEnabled, accessToken, bitstream]: [boolean, boolean, boolean, string, Bitstream]) => {
106116
if (isAuthorized && isLoggedIn) {
107117
return this.fileService.retrieveFileDownloadLink(bitstream._links.content.href).pipe(
108118
filter((fileLink) => hasValue(fileLink)),
109119
take(1),
110120
map((fileLink) => {
111-
return [isAuthorized, isLoggedIn, bitstream, fileLink];
121+
return [isAuthorized, isLoggedIn, isMatomoEnabled, bitstream, fileLink];
112122
}));
123+
} else if (hasValue(accessToken)) {
124+
return [[isAuthorized, !isLoggedIn, isMatomoEnabled, bitstream, '', accessToken]];
113125
} else {
114-
return [[isAuthorized, isLoggedIn, bitstream, bitstream._links.content.href]];
126+
return [[isAuthorized, isLoggedIn, isMatomoEnabled, bitstream, bitstream._links.content.href]];
115127
}
116128
}),
117-
switchMap(([isAuthorized, isLoggedIn, bitstream, fileLink]: [boolean, boolean, Bitstream, string]) =>
118-
this.matomoService.appendVisitorId(fileLink)
119-
.pipe(
120-
map((fileLinkWithVisitorId) => [isAuthorized, isLoggedIn, bitstream, fileLinkWithVisitorId]),
121-
),
122-
),
123-
).subscribe(([isAuthorized, isLoggedIn, , fileLink]: [boolean, boolean, Bitstream, string]) => {
129+
switchMap(([isAuthorized, isLoggedIn, isMatomoEnabled, bitstream, fileLink, accessToken]: [boolean, boolean, boolean, Bitstream, string, string]) => {
130+
if (isMatomoEnabled) {
131+
return this.matomoService.appendVisitorId(fileLink).pipe(
132+
map((fileLinkWithVisitorId) => [isAuthorized, isLoggedIn, bitstream, fileLinkWithVisitorId, accessToken]),
133+
);
134+
}
135+
return observableOf([isAuthorized, isLoggedIn, bitstream, fileLink, accessToken]);
136+
}),
137+
).subscribe(([isAuthorized, isLoggedIn, bitstream, fileLink, accessToken]: [boolean, boolean, Bitstream, string, string]) => {
124138
if (isAuthorized && isLoggedIn && isNotEmpty(fileLink)) {
125139
this.hardRedirectService.redirect(fileLink);
126-
} else if (isAuthorized && !isLoggedIn) {
140+
} else if (isAuthorized && !isLoggedIn && !hasValue(accessToken)) {
127141
this.hardRedirectService.redirect(fileLink);
128-
} else if (!isAuthorized && isLoggedIn) {
129-
this.router.navigateByUrl(getForbiddenRoute(), { skipLocationChange: true });
130-
} else if (!isAuthorized && !isLoggedIn) {
131-
this.auth.setRedirectUrl(this.router.url);
132-
this.router.navigateByUrl('login');
142+
} else if (!isAuthorized) {
143+
// Either we have an access token, or we are logged in, or we are not logged in.
144+
// For now, the access token does not care if we are logged in or not.
145+
if (hasValue(accessToken)) {
146+
this.hardRedirectService.redirect(bitstream._links.content.href + '?accessToken=' + accessToken);
147+
} else if (isLoggedIn) {
148+
this.router.navigateByUrl(getForbiddenRoute(), { skipLocationChange: true });
149+
} else if (!isLoggedIn) {
150+
this.auth.setRedirectUrl(this.router.url);
151+
this.router.navigateByUrl('login');
152+
}
133153
}
134154
});
135155
}

src/app/shared/cookies/browser-klaro.service.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { EPerson } from '../../core/eperson/models/eperson.model';
2626
import { CAPTCHA_NAME } from '../../core/google-recaptcha/google-recaptcha.service';
2727
import { CookieService } from '../../core/services/cookie.service';
2828
import { getFirstCompletedRemoteData } from '../../core/shared/operators';
29+
import { MATOMO_ENABLED } from '../../statistics/matomo.service';
2930
import {
3031
hasValue,
3132
isEmpty,
@@ -86,7 +87,7 @@ export class BrowserKlaroService extends KlaroService {
8687

8788
private readonly GOOGLE_ANALYTICS_SERVICE_NAME = 'google-analytics';
8889

89-
private readonly MATOMO_ENABLED = 'matomo.enabled';
90+
private readonly MATOMO_ENABLED = MATOMO_ENABLED;
9091

9192
/**
9293
* Initial Klaro configuration

src/app/statistics/matomo.service.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ export class MatomoService {
106106
* @returns An Observable that emits the URL with the visitor ID appended.
107107
*/
108108
appendVisitorId(url: string): Observable<string> {
109+
if (!this.matomoTracker) {
110+
return of(url);
111+
}
109112
return fromPromise(this.matomoTracker?.getVisitorId())
110113
.pipe(
111114
map(visitorId => this.appendTrackerId(url, visitorId)),

0 commit comments

Comments
 (0)