Skip to content

Commit 8393cc9

Browse files
Merged dspace-cris-2023_02_x into DSC-1486-item-page-resolver-maintenance
2 parents 9acb3b6 + 0a0227f commit 8393cc9

25 files changed

Lines changed: 472 additions & 61 deletions

src/app/core/eperson/group-data.service.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import { Operation } from 'fast-json-patch';
4141
import { RestRequestMethod } from '../data/rest-request-method';
4242
import { dataService } from '../data/base/data-service.decorator';
4343
import { getGroupEditRoute } from '../../access-control/access-control-routing-paths';
44+
import { isNotEmpty } from '../../shared/empty.util';
4445

4546
const groupRegistryStateSelector = (state: AppState) => state.groupRegistry;
4647
const editGroupSelector = createSelector(groupRegistryStateSelector, (groupRegistryState: GroupRegistryState) => groupRegistryState.editGroup);

src/app/core/metadata/metadata.service.ts

Lines changed: 68 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,13 @@ export class MetadataService {
206206
this.setCitationTechnicalReportNumberTag();
207207
}
208208

209+
this.setOpenGraphTitleTag();
210+
this.setOpenGraphDescriptionTag();
211+
this.setOpenGraphImageTag();
212+
213+
this.setTwitterTitleTag();
214+
this.setTwitterDescriptionTag();
215+
this.setTwitterImageTag();
209216
}
210217

211218
/**
@@ -401,22 +408,75 @@ export class MetadataService {
401408
* Add <meta name="citation_pdf_url" ... > to the <head>
402409
*/
403410
private setCitationPdfUrlTag(): void {
411+
this.setPrimaryBitstreamInBundleTag('ORIGINAL', 'citation_pdf_url');
412+
}
413+
414+
/**
415+
* Add <meta name="og:title" ... > to the <head>
416+
*/
417+
private setOpenGraphTitleTag(): void {
418+
const value = this.getMetaTagValue('dc.title');
419+
this.addMetaTag('og:title', value);
420+
}
421+
422+
/**
423+
* Add <meta name="og:description" ... > to the <head>
424+
*/
425+
private setOpenGraphDescriptionTag(): void {
426+
// TODO: truncate abstract
427+
const value = this.getMetaTagValue('dc.description.abstract');
428+
this.addMetaTag('og:description', value);
429+
}
430+
431+
/**
432+
* Add <meta name="og:image" ... > to the <head>
433+
*/
434+
private setOpenGraphImageTag(): void {
435+
this.setPrimaryBitstreamInBundleTag('THUMBNAIL', 'og:image');
436+
}
437+
438+
439+
/**
440+
* Add <meta name="twitter:title" ... > to the <head>
441+
*/
442+
private setTwitterTitleTag(): void {
443+
const value = this.getMetaTagValue('dc.title');
444+
this.addMetaTag('twitter:title', value);
445+
}
446+
447+
/**
448+
* Add <meta name="twitter:description" ... > to the <head>
449+
*/
450+
private setTwitterDescriptionTag(): void {
451+
// TODO: truncate abstract
452+
const value = this.getMetaTagValue('dc.description.abstract');
453+
this.addMetaTag('twitter:description', value);
454+
}
455+
456+
/**
457+
* Add <meta name="twitter:image" ... > to the <head>
458+
*/
459+
private setTwitterImageTag(): void {
460+
this.setPrimaryBitstreamInBundleTag('THUMBNAIL', 'twitter:image');
461+
}
462+
463+
private setPrimaryBitstreamInBundleTag(bundleName: string, tag: string): void {
404464
if (this.currentObject.value instanceof Item) {
405465
const item = this.currentObject.value as Item;
406466

407-
// Retrieve the ORIGINAL bundle for the item
467+
// Retrieve the bundle for the item
408468
this.bundleDataService.findByItemAndName(
409469
item,
410-
'ORIGINAL',
470+
bundleName,
411471
true,
412472
true,
413473
followLink('primaryBitstream'),
414474
followLink('bitstreams', {
415-
findListOptions: {
416-
// limit the number of bitstreams used to find the citation pdf url to the number
417-
// shown by default on an item page
418-
elementsPerPage: this.appConfig.item.bitstream.pageSize
419-
}
475+
findListOptions: {
476+
// limit the number of bitstreams used to find the citation pdf url to the number
477+
// shown by default on an item page
478+
elementsPerPage: this.appConfig.item.bitstream.pageSize
479+
}
420480
}, followLink('format')),
421481
).pipe(
422482
getFirstSucceededRemoteDataPayload(),
@@ -460,7 +520,7 @@ export class MetadataService {
460520
).subscribe((link: string) => {
461521
// Use the found link to set the <meta> tag
462522
this.addMetaTag(
463-
'citation_pdf_url',
523+
tag,
464524
new URLCombiner(this.hardRedirectService.getCurrentOrigin(), link).toString()
465525
);
466526
});

src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/thumbnail/thumbnail.component.spec.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ describe('ThumbnailComponent', () => {
152152
}));
153153

154154
it('should show default thumbnail', () => {
155-
expect(component.default).toBe('assets/images/person-placeholder.svg');
155+
expect(component.default).toBe('assets/images/file-placeholder.svg');
156156
});
157157

158158
});
@@ -172,7 +172,7 @@ describe('ThumbnailComponent', () => {
172172
}));
173173

174174
it('should show default thumbnail', () => {
175-
expect(component.default).toBe('assets/images/person-placeholder.svg');
175+
expect(component.default).toBe('assets/images/file-placeholder.svg');
176176
});
177177

178178
});
@@ -239,7 +239,7 @@ describe('ThumbnailComponent', () => {
239239
});
240240

241241
it('should show default thumbnail', () => {
242-
expect(component.default).toBe('assets/images/person-placeholder.svg');
242+
expect(component.default).toBe('assets/images/file-placeholder.svg');
243243
});
244244

245245
});
@@ -254,7 +254,7 @@ describe('ThumbnailComponent', () => {
254254
});
255255

256256
it('should not show bitstream content image src but the default image', () => {
257-
expect(component.default).toBe('assets/images/person-placeholder.svg');
257+
expect(component.default).toBe('assets/images/file-placeholder.svg');
258258
});
259259

260260
});
@@ -269,7 +269,7 @@ describe('ThumbnailComponent', () => {
269269
});
270270

271271
it('should not show thumbnail content image src but the default image', () => {
272-
expect(component.default).toBe('assets/images/person-placeholder.svg');
272+
expect(component.default).toBe('assets/images/file-placeholder.svg');
273273
});
274274

275275
});
@@ -284,7 +284,7 @@ describe('ThumbnailComponent', () => {
284284
});
285285

286286
it('should not show thumbnail content image src but the default image', () => {
287-
expect(component.default).toBe('assets/images/person-placeholder.svg');
287+
expect(component.default).toBe('assets/images/file-placeholder.svg');
288288
});
289289

290290
});

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

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,22 @@
11
import { Inject, Injectable, InjectionToken } from '@angular/core';
2-
import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs';
2+
import { BehaviorSubject, combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs';
33
import { AuthService } from '../../core/auth/auth.service';
44
import { TranslateService } from '@ngx-translate/core';
55
import { environment } from '../../../environments/environment';
66
import { map, switchMap, take } from 'rxjs/operators';
77
import { EPerson } from '../../core/eperson/models/eperson.model';
8-
import { KlaroService } from './klaro.service';
8+
import { CookieConsents, KlaroService } from './klaro.service';
99
import { hasValue, isEmpty, isNotEmpty } from '../empty.util';
1010
import { CookieService } from '../../core/services/cookie.service';
1111
import { EPersonDataService } from '../../core/eperson/eperson-data.service';
1212
import cloneDeep from 'lodash/cloneDeep';
1313
import debounce from 'lodash/debounce';
1414
import { ANONYMOUS_STORAGE_NAME_KLARO, klaroConfiguration } from './klaro-configuration';
15-
import { Operation } from 'fast-json-patch';
15+
import { deepClone, Operation } from 'fast-json-patch';
1616
import { getFirstCompletedRemoteData } from '../../core/shared/operators';
1717
import { ConfigurationDataService } from '../../core/data/configuration-data.service';
1818
import { CAPTCHA_NAME } from '../../core/google-recaptcha/google-recaptcha.service';
19+
import isEqual from 'lodash/isEqual';
1920

2021
/**
2122
* Metadata field to store a user's cookie consent preferences in
@@ -65,11 +66,19 @@ export class BrowserKlaroService extends KlaroService {
6566

6667
private readonly GOOGLE_ANALYTICS_SERVICE_NAME = 'google-analytics';
6768

69+
private lastCookiesConsents: CookieConsents;
70+
6871
/**
6972
* Initial Klaro configuration
7073
*/
7174
klaroConfig = cloneDeep(klaroConfiguration);
7275

76+
/**
77+
* Subject to emit updates in the consents
78+
*/
79+
consentsUpdates$: BehaviorSubject<CookieConsents> = new BehaviorSubject<CookieConsents>(null);
80+
81+
7382
constructor(
7483
private translateService: TranslateService,
7584
private authService: AuthService,
@@ -94,6 +103,20 @@ export class BrowserKlaroService extends KlaroService {
94103
this.klaroConfig.translations.zz.consentNotice.description = 'cookies.consent.content-notice.description.no-privacy';
95104
}
96105

106+
if (hasValue(environment.info.metricsConsents)) {
107+
environment.info.metricsConsents.forEach((metric) => {
108+
if (metric.enabled) {
109+
this.klaroConfig.services.push(
110+
{
111+
name: metric.key,
112+
purposes: ['thirdPartyJs'],
113+
required: false,
114+
}
115+
);
116+
}
117+
});
118+
}
119+
97120
const hideGoogleAnalytics$ = this.configService.findByPropertyName(this.GOOGLE_ANALYTICS_KEY).pipe(
98121
getFirstCompletedRemoteData(),
99122
map(remoteData => !remoteData.hasSucceeded || !remoteData.payload || isEmpty(remoteData.payload.values)),
@@ -331,6 +354,25 @@ export class BrowserKlaroService extends KlaroService {
331354
return 'klaro-' + identifier;
332355
}
333356

357+
watchConsentUpdates(): void {
358+
this.lazyKlaro.then(({getManager}) => {
359+
const manager = getManager(this.klaroConfig);
360+
const consentsSubject$ = this.consentsUpdates$;
361+
let lastCookiesConsents = this.lastCookiesConsents;
362+
363+
consentsSubject$.next(manager.consents);
364+
manager.watch({
365+
update(_, eventName, consents) {
366+
367+
if (eventName === 'consents' && !isEqual(consents, lastCookiesConsents)) {
368+
lastCookiesConsents = deepClone(consents);
369+
consentsSubject$.next(consents);
370+
}
371+
}
372+
});
373+
});
374+
}
375+
334376
/**
335377
* remove the google analytics from the services
336378
*/
Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import { Injectable } from '@angular/core';
22

3-
import { Observable } from 'rxjs';
4-
3+
import { BehaviorSubject, Observable } from 'rxjs';
4+
export interface CookieConsents {
5+
[key: string]: boolean;
6+
}
57
/**
68
* Abstract class representing a service for handling Klaro consent preferences and UI
79
*/
@@ -10,15 +12,25 @@ export abstract class KlaroService {
1012
/**
1113
* Initializes the service
1214
*/
13-
abstract initialize();
15+
abstract initialize(): void;
1416

1517
/**
1618
* Shows a dialog with the current consent preferences
1719
*/
18-
abstract showSettings();
20+
abstract showSettings(): void;
1921

2022
/**
2123
* Return saved preferences stored in the klaro cookie
2224
*/
2325
abstract getSavedPreferences(): Observable<any>;
26+
27+
/**
28+
* Watch for changes in consents
29+
*/
30+
abstract watchConsentUpdates(): void;
31+
32+
/**
33+
* Subject to emit updates in the consents
34+
*/
35+
abstract consentsUpdates$: BehaviorSubject<CookieConsents>;
2436
}

src/app/shared/dso-selector/modal-wrappers/export-batch-selector/export-batch-selector.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import { FeatureID } from '../../../../core/data/feature-authorization/feature-i
3030
templateUrl: '../dso-selector-modal-wrapper.component.html',
3131
})
3232
export class ExportBatchSelectorComponent extends DSOSelectorModalWrapperComponent implements OnInit {
33-
configuration = 'backend';
33+
configuration = 'communityOrCollection';
3434
objectType = DSpaceObjectType.DSPACEOBJECT;
3535
selectorTypes = [DSpaceObjectType.COLLECTION];
3636
action = SelectorActionType.EXPORT_BATCH;

src/app/shared/dso-selector/modal-wrappers/import-batch-selector/import-batch-selector.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { Observable, of } from 'rxjs';
1616
templateUrl: '../dso-selector-modal-wrapper.component.html',
1717
})
1818
export class ImportBatchSelectorComponent extends DSOSelectorModalWrapperComponent implements OnInit {
19-
configuration = 'backend';
19+
configuration = 'communityOrCollection';
2020
objectType = DSpaceObjectType.DSPACEOBJECT;
2121
selectorTypes = [DSpaceObjectType.COLLECTION];
2222
action = SelectorActionType.IMPORT_BATCH;

src/app/shared/metric/metric-altmetric/metric-altmetric.component.html

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
<div class="row d-flex align-items-center"
2-
*ngIf="!failed && !(isHidden$ | async) && (remark | dsListMetricProps: 'data-badge-enabled':isListElement == true)">
2+
*ngIf="(!failed &&
3+
canLoadScript &&
4+
!(isHidden$ | async) &&
5+
(remark | dsListMetricProps: 'data-badge-enabled':isListElement == true))"
6+
>
37
<div class="col-5 text-left">
48
<div #metricChild>
59
<div
610
class="altmetric-embed"
7-
[attr.data-hide-no-mentions]="remark | dsListMetricProps : 'data-hide-no-mentions' : isListElement"
11+
[attr.data-hide-no-mentions]="visibleWithoutData ? false : (remark | dsListMetricProps : 'data-hide-no-mentions' : isListElement)"
812
[attr.data-hide-less-than]="remark | dsListMetricProps : 'data-hide-less-than' : isListElement"
913
[attr.data-badge-details]="remark | dsListMetricProps : 'data-badge-details' : isListElement"
1014
[attr.data-badge-type]="remark | dsListMetricProps : 'badgeType' : isListElement"
@@ -21,3 +25,9 @@
2125
</div>
2226
</div>
2327
</div>
28+
<div class="row d-flex align-items-center justify-content-center m-2" *ngIf="!canLoadScript && !isListElement">
29+
<div>
30+
{{ "third-party-metrics-cookies.message" | translate: {metricType: metric.metricType | titlecase} }}
31+
<div role="button" class="btn-link" (click)="requestSettingsConsent.emit(true)">{{"third-party-metrics-cookies.consent-settings" | translate}}</div>
32+
</div>
33+
</div>

src/app/shared/metric/metric-dimensions/metric-dimensions.component.html

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
<div class="row d-flex align-items-center"
2-
*ngIf="!(isHidden$ | async) && !failed && (remark | dsListMetricProps: 'data-badge-enabled':isListElement == true)">
2+
*ngIf="!(isHidden$ | async) &&
3+
canLoadScript &&
4+
!failed &&
5+
(remark | dsListMetricProps: 'data-badge-enabled':isListElement == true)"
6+
>
37
<div class="col-5 text-left">
48
<div
59
#metricChild
610
class="__dimensions_badge_embed__"
7-
[attr.data-hide-zero-citations]="remark | dsListMetricProps: 'data-hide-zero-citations':isListElement"
11+
[attr.data-hide-zero-citations]="visibleWithoutData ? false : (remark | dsListMetricProps: 'data-hide-zero-citations':isListElement)"
812
[attr.data-pmid]="
913
(remark | dsListMetricProps: 'data-doi':isListElement)
1014
? null
@@ -21,3 +25,9 @@
2125
</div>
2226
</div>
2327
</div>
28+
<div class="row d-flex align-items-center justify-content-center m-2" *ngIf="!canLoadScript && !isListElement">
29+
<div>
30+
{{ "third-party-metrics-cookies.message" | translate: {metricType: metric.metricType | titlecase} }}
31+
<div role="button" class="btn-link" (click)="requestSettingsConsent.emit(true)">{{"third-party-metrics-cookies.consent-settings" | translate}}</div>
32+
</div>
33+
</div>

src/app/shared/metric/metric-loader/base-embedded-metric.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export abstract class BaseEmbeddedMetricComponent extends BaseMetricComponent im
4646
* When the html content has been initialized, initialize the script.
4747
*/
4848
ngAfterViewInit() {
49-
if (this.metric) {
49+
if (this.metric && this.canLoadScript) {
5050
this.initScript();
5151
}
5252
}

0 commit comments

Comments
 (0)