Skip to content

Commit 5c10e47

Browse files
authored
Merge branch 'main' into bugfix/addressing-#2276
2 parents 1919f97 + 2fa8ab5 commit 5c10e47

37 files changed

Lines changed: 309 additions & 57 deletions

File tree

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import { of as observableOf } from 'rxjs';
2+
import { RouteService } from './route.service';
3+
import { BrowserReferrerService } from './browser.referrer.service';
4+
5+
describe(`BrowserReferrerService`, () => {
6+
let service: BrowserReferrerService;
7+
const documentReferrer = 'https://www.referrer.com';
8+
const origin = 'https://www.dspace.org';
9+
let routeService: RouteService;
10+
11+
beforeEach(() => {
12+
routeService = {
13+
getHistory: () => observableOf([])
14+
} as any;
15+
service = new BrowserReferrerService(
16+
{ referrer: documentReferrer },
17+
routeService,
18+
{ getCurrentOrigin: () => origin } as any
19+
);
20+
});
21+
22+
describe(`getReferrer`, () => {
23+
describe(`when the history is an empty`, () => {
24+
beforeEach(() => {
25+
spyOn(routeService, 'getHistory').and.returnValue(observableOf([]));
26+
});
27+
28+
it(`should return document.referrer`, (done: DoneFn) => {
29+
service.getReferrer().subscribe((emittedReferrer: string) => {
30+
expect(emittedReferrer).toBe(documentReferrer);
31+
done();
32+
});
33+
});
34+
});
35+
36+
describe(`when the history only contains the current route`, () => {
37+
beforeEach(() => {
38+
spyOn(routeService, 'getHistory').and.returnValue(observableOf(['/current/route']));
39+
});
40+
41+
it(`should return document.referrer`, (done: DoneFn) => {
42+
service.getReferrer().subscribe((emittedReferrer: string) => {
43+
expect(emittedReferrer).toBe(documentReferrer);
44+
done();
45+
});
46+
});
47+
});
48+
49+
describe(`when the history contains multiple routes`, () => {
50+
const prevUrl = '/the/route/we/need';
51+
beforeEach(() => {
52+
spyOn(routeService, 'getHistory').and.returnValue(observableOf([
53+
'/first/route',
54+
'/second/route',
55+
prevUrl,
56+
'/current/route'
57+
]));
58+
});
59+
60+
it(`should return the last route before the current one combined with the origin from HardRedirectService`, (done: DoneFn) => {
61+
service.getReferrer().subscribe((emittedReferrer: string) => {
62+
expect(emittedReferrer).toBe(origin + prevUrl);
63+
done();
64+
});
65+
});
66+
});
67+
});
68+
});
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
import { ReferrerService } from './referrer.service';
2+
import { Observable } from 'rxjs';
3+
import { map } from 'rxjs/operators';
4+
import { hasNoValue } from '../../shared/empty.util';
5+
import { URLCombiner } from '../url-combiner/url-combiner';
6+
import { Inject, Injectable } from '@angular/core';
7+
import { DOCUMENT } from '@angular/common';
8+
import { HardRedirectService } from './hard-redirect.service';
9+
import { RouteService } from './route.service';
10+
11+
/**
12+
* A service to determine the referrer
13+
*
14+
* The browser implementation will get the referrer from document.referrer, in the event that the
15+
* previous page visited was not an angular URL. If it was, the route history in the store must be
16+
* used, since document.referrer doesn't get updated on route changes
17+
*/
18+
@Injectable()
19+
export class BrowserReferrerService extends ReferrerService {
20+
21+
constructor(
22+
@Inject(DOCUMENT) protected document: any,
23+
protected routeService: RouteService,
24+
protected hardRedirectService: HardRedirectService,
25+
) {
26+
super();
27+
}
28+
29+
/**
30+
* Return the referrer
31+
*
32+
* Return the referrer URL based on the route history in the store. If there is no route history
33+
* in the store yet, document.referrer will be used
34+
*/
35+
public getReferrer(): Observable<string> {
36+
return this.routeService.getHistory().pipe(
37+
map((history: string[]) => {
38+
const currentURL = history[history.length - 1];
39+
// if the current URL isn't set yet, or the only URL in the history is the current one,
40+
// return document.referrer (note that that may be empty too, e.g. if you've just opened a
41+
// new browser tab)
42+
if (hasNoValue(currentURL) || history.every((url: string) => url === currentURL)) {
43+
return this.document.referrer;
44+
} else {
45+
// reverse the history
46+
const reversedHistory = [...history].reverse();
47+
// and find the first URL that differs from the current one
48+
const prevUrl = reversedHistory.find((url: string) => url !== currentURL);
49+
return new URLCombiner(this.hardRedirectService.getCurrentOrigin(), prevUrl).toString();
50+
}
51+
})
52+
);
53+
}
54+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { Injectable } from '@angular/core';
2+
import { Observable } from 'rxjs';
3+
4+
/**
5+
* A service to determine the referrer, i.e. the previous URL that led the user to the current one
6+
*/
7+
@Injectable()
8+
export abstract class ReferrerService {
9+
10+
/**
11+
* Return the referrer
12+
*/
13+
abstract getReferrer(): Observable<string>;
14+
15+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { ServerReferrerService } from './server.referrer.service';
2+
3+
describe(`ServerReferrerService`, () => {
4+
let service: ServerReferrerService;
5+
const referrer = 'https://www.referrer.com';
6+
7+
describe(`getReferrer`, () => {
8+
describe(`when the referer header is set`, () => {
9+
beforeEach(() => {
10+
service = new ServerReferrerService({ headers: { referer: referrer }});
11+
});
12+
13+
it(`should return the referer header`, (done: DoneFn) => {
14+
service.getReferrer().subscribe((emittedReferrer: string) => {
15+
expect(emittedReferrer).toBe(referrer);
16+
done();
17+
});
18+
});
19+
});
20+
21+
describe(`when the referer header is not set`, () => {
22+
beforeEach(() => {
23+
service = new ServerReferrerService({ headers: {}});
24+
});
25+
26+
it(`should return an empty string`, (done: DoneFn) => {
27+
service.getReferrer().subscribe((emittedReferrer: string) => {
28+
expect(emittedReferrer).toBe('');
29+
done();
30+
});
31+
});
32+
});
33+
});
34+
});
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { ReferrerService } from './referrer.service';
2+
import { Observable, of as observableOf } from 'rxjs';
3+
import { Inject, Injectable } from '@angular/core';
4+
import { REQUEST } from '@nguniversal/express-engine/tokens';
5+
6+
/**
7+
* A service to determine the referrer
8+
*
9+
* The server implementation will get the referrer from the 'Referer' header of the request sent to
10+
* the express server
11+
*/
12+
@Injectable()
13+
export class ServerReferrerService extends ReferrerService {
14+
15+
constructor(
16+
@Inject(REQUEST) protected request: any,
17+
) {
18+
super();
19+
}
20+
21+
/**
22+
* Return the referrer
23+
*
24+
* Return the 'Referer' header from the request, or an empty string if the header wasn't set
25+
* (for consistency with the document.referrer property on the browser side)
26+
*/
27+
public getReferrer(): Observable<string> {
28+
const referrer = this.request.headers.referer || '';
29+
return observableOf(referrer);
30+
}
31+
}

src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-issue/journal-issue-search-result-grid-element.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
</div>
66
<a *ngIf="linkType != linkTypes.None"
77
[target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
8-
rel="noopener noreferrer" [routerLink]="[itemPageRoute]"
8+
[attr.rel]="(linkType == linkTypes.ExternalLink) ? 'noopener noreferrer' : null" [routerLink]="[itemPageRoute]"
99
class="card-img-top full-width" [attr.title]="'search.results.view-result' | translate">
1010
<div>
1111
<ds-themed-thumbnail [thumbnail]="dso?.thumbnail | async" [limitWidth]="false">
@@ -36,7 +36,7 @@ <h4 class="card-title" [innerHTML]="dsoTitle"></h4>
3636
</p>
3737
<div *ngIf="linkType != linkTypes.None" class="text-center">
3838
<a [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
39-
rel="noopener noreferrer" [routerLink]="[itemPageRoute]"
39+
[attr.rel]="(linkType == linkTypes.ExternalLink) ? 'noopener noreferrer' : null" [routerLink]="[itemPageRoute]"
4040
class="lead btn btn-primary viewButton">{{ 'search.results.view-result' | translate}}</a>
4141
</div>
4242
</div>

src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal-volume/journal-volume-search-result-grid-element.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
</div>
66
<a *ngIf="linkType != linkTypes.None"
77
[target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
8-
rel="noopener noreferrer" [routerLink]="[itemPageRoute]"
8+
[attr.rel]="(linkType == linkTypes.ExternalLink) ? 'noopener noreferrer' : null" [routerLink]="[itemPageRoute]"
99
class="card-img-top full-width" [attr.title]="'search.results.view-result' | translate">
1010
<div>
1111
<ds-themed-thumbnail [thumbnail]="dso?.thumbnail | async" [limitWidth]="false">
@@ -36,7 +36,7 @@ <h4 class="card-title" [innerHTML]="dsoTitle"></h4>
3636
</p>
3737
<div *ngIf="linkType != linkTypes.None" class="text-center">
3838
<a [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
39-
rel="noopener noreferrer" [routerLink]="[itemPageRoute]"
39+
[attr.rel]="(linkType == linkTypes.ExternalLink) ? 'noopener noreferrer' : null" [routerLink]="[itemPageRoute]"
4040
class="lead btn btn-primary viewButton">{{ 'search.results.view-result' | translate}}</a>
4141
</div>
4242
</div>

src/app/entity-groups/journal-entities/item-grid-elements/search-result-grid-elements/journal/journal-search-result-grid-element.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
</div>
66
<a *ngIf="linkType != linkTypes.None"
77
[target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
8-
rel="noopener noreferrer" [routerLink]="[itemPageRoute]"
8+
[attr.rel]="(linkType == linkTypes.ExternalLink) ? 'noopener noreferrer' : null" [routerLink]="[itemPageRoute]"
99
class="card-img-top full-width" [attr.title]="'search.results.view-result' | translate">
1010
<div>
1111
<ds-themed-thumbnail [thumbnail]="dso?.thumbnail | async" [limitWidth]="false">
@@ -40,7 +40,7 @@ <h4 class="card-title" [innerHTML]="dsoTitle"></h4>
4040
</p>
4141
<div *ngIf="linkType != linkTypes.None" class="text-center">
4242
<a [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
43-
rel="noopener noreferrer" [routerLink]="[itemPageRoute]"
43+
[attr.rel]="(linkType == linkTypes.ExternalLink) ? 'noopener noreferrer' : null" [routerLink]="[itemPageRoute]"
4444
class="lead btn btn-primary viewButton">{{ 'search.results.view-result' | translate}}</a>
4545
</div>
4646
</div>

src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-issue/journal-issue-search-result-list-element.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<div class="row">
22
<div *ngIf="showThumbnails" class="col-3 col-md-2">
33
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
4-
rel="noopener noreferrer"
4+
[attr.rel]="(linkType == linkTypes.ExternalLink) ? 'noopener noreferrer' : null"
55
[routerLink]="[itemPageRoute]" class="lead item-list-title dont-break-out">
66
<ds-thumbnail [thumbnail]="dso?.thumbnail | async" [limitWidth]="true">
77
</ds-thumbnail>
@@ -15,7 +15,7 @@
1515
<ds-themed-badges *ngIf="showLabel" [object]="dso" [context]="context"></ds-themed-badges>
1616
<ds-truncatable [id]="dso.id">
1717
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
18-
rel="noopener noreferrer"
18+
[attr.rel]="(linkType == linkTypes.ExternalLink) ? 'noopener noreferrer' : null"
1919
[routerLink]="[itemPageRoute]" class="lead item-list-title dont-break-out"
2020
[innerHTML]="dsoTitle"></a>
2121
<span *ngIf="linkType == linkTypes.None"

src/app/entity-groups/journal-entities/item-list-elements/search-result-list-elements/journal-volume/journal-volume-search-result-list-element.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<div class="row">
22
<div *ngIf="showThumbnails" class="col-3 col-md-2">
33
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
4-
rel="noopener noreferrer"
4+
[attr.rel]="(linkType == linkTypes.ExternalLink) ? 'noopener noreferrer' : null"
55
[routerLink]="[itemPageRoute]" class="lead item-list-title dont-break-out">
66
<ds-thumbnail [thumbnail]="dso?.thumbnail | async" [limitWidth]="true">
77
</ds-thumbnail>
@@ -15,7 +15,7 @@
1515
<ds-themed-badges *ngIf="showLabel" [object]="dso" [context]="context"></ds-themed-badges>
1616
<ds-truncatable [id]="dso.id">
1717
<a *ngIf="linkType != linkTypes.None" [target]="(linkType == linkTypes.ExternalLink) ? '_blank' : '_self'"
18-
rel="noopener noreferrer"
18+
[attr.rel]="(linkType == linkTypes.ExternalLink) ? 'noopener noreferrer' : null"
1919
[routerLink]="[itemPageRoute]" class="lead item-list-title dont-break-out"
2020
[innerHTML]="dsoTitle"></a>
2121
<span *ngIf="linkType == linkTypes.None"

0 commit comments

Comments
 (0)