From b207e74f47d6db1fea0a9c5a195d29e646be9a46 Mon Sep 17 00:00:00 2001 From: "yevhenii.lohatskyi" Date: Tue, 16 Jan 2024 10:00:44 +0200 Subject: [PATCH 01/25] [DSC-1433] add OpenGraph and Twitter card tags to setDSOMetaTags() --- src/app/core/metadata/metadata.service.ts | 57 +++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/src/app/core/metadata/metadata.service.ts b/src/app/core/metadata/metadata.service.ts index 0c13b94c749..e76ee2425a0 100644 --- a/src/app/core/metadata/metadata.service.ts +++ b/src/app/core/metadata/metadata.service.ts @@ -206,6 +206,13 @@ export class MetadataService { this.setCitationTechnicalReportNumberTag(); } + this.setOpenGraphTitleTag(); + this.setOpenGraphDescriptionTag(); + //this.setOpenGraphImageTag(); + + this.setTwitterTitleTag(); + this.setTwitterDescriptionTag(); + //this.setTwitterImageTag(); } /** @@ -467,6 +474,56 @@ export class MetadataService { } } + /** + * Add to the + */ + private setOpenGraphTitleTag(): void { + const value = this.getMetaTagValue('dc.title'); + this.addMetaTag('og:title', value); + } + + /** + * Add to the + */ + private setOpenGraphDescriptionTag(): void { + // TODO: truncate abstract + const value = this.getMetaTagValue('dc.description.abstract'); + this.addMetaTag('og:description', value); + } + + /** + * Add to the + */ + private setOpenGraphImageTag(): void { + const value = ''; + this.addMetaTag('og:image', value); + } + + /** + * Add to the + */ + private setTwitterTitleTag(): void { + const value = this.getMetaTagValue('dc.title'); + this.addMetaTag('twitter:title', value); + } + + /** + * Add to the + */ + private setTwitterDescriptionTag(): void { + // TODO: truncate abstract + const value = this.getMetaTagValue('dc.description.abstract'); + this.addMetaTag('twitter:description', value); + } + + /** + * Add to the + */ + private setTwitterImageTag(): void { + const value = ''; + this.addMetaTag('twitter:image', value); + } + getBitLinkIfDownloadable(bitstream: Bitstream, bitstreamRd: RemoteData>): Observable { return observableOf(bitstream).pipe( getDownloadableBitstream(this.authorizationService), From 5bb3060f7d4760bd066e07013086d3465ba804e6 Mon Sep 17 00:00:00 2001 From: "yevhenii.lohatskyi" Date: Tue, 16 Jan 2024 17:47:37 +0200 Subject: [PATCH 02/25] [DSC-1433] add OpenGraph and Twitter image card tags to setDSOMetaTags() --- src/app/core/metadata/metadata.service.ts | 123 +++++++++++----------- 1 file changed, 63 insertions(+), 60 deletions(-) diff --git a/src/app/core/metadata/metadata.service.ts b/src/app/core/metadata/metadata.service.ts index e76ee2425a0..9850c89210c 100644 --- a/src/app/core/metadata/metadata.service.ts +++ b/src/app/core/metadata/metadata.service.ts @@ -208,11 +208,11 @@ export class MetadataService { this.setOpenGraphTitleTag(); this.setOpenGraphDescriptionTag(); - //this.setOpenGraphImageTag(); + this.setOpenGraphImageTag(); this.setTwitterTitleTag(); this.setTwitterDescriptionTag(); - //this.setTwitterImageTag(); + this.setTwitterImageTag(); } /** @@ -408,22 +408,75 @@ export class MetadataService { * Add to the */ private setCitationPdfUrlTag(): void { + this.setSocialImageTag('ORIGINAL', 'citation_pdf_url'); + } + + /** + * Add to the + */ + private setOpenGraphTitleTag(): void { + const value = this.getMetaTagValue('dc.title'); + this.addMetaTag('og:title', value); + } + + /** + * Add to the + */ + private setOpenGraphDescriptionTag(): void { + // TODO: truncate abstract + const value = this.getMetaTagValue('dc.description.abstract'); + this.addMetaTag('og:description', value); + } + + /** + * Add to the + */ + private setOpenGraphImageTag(): void { + this.setSocialImageTag('THUMBNAIL', 'og:image'); + } + + + /** + * Add to the + */ + private setTwitterTitleTag(): void { + const value = this.getMetaTagValue('dc.title'); + this.addMetaTag('twitter:title', value); + } + + /** + * Add to the + */ + private setTwitterDescriptionTag(): void { + // TODO: truncate abstract + const value = this.getMetaTagValue('dc.description.abstract'); + this.addMetaTag('twitter:description', value); + } + + /** + * Add to the + */ + private setTwitterImageTag(): void { + this.setSocialImageTag('THUMBNAIL', 'twitter:image'); + } + + private setSocialImageTag(bundleName: string, tag: string): void { if (this.currentObject.value instanceof Item) { const item = this.currentObject.value as Item; - // Retrieve the ORIGINAL bundle for the item + // Retrieve the THUMBNAIL bundle for the item this.bundleDataService.findByItemAndName( item, - 'ORIGINAL', + bundleName, true, true, followLink('primaryBitstream'), followLink('bitstreams', { - findListOptions: { - // limit the number of bitstreams used to find the citation pdf url to the number - // shown by default on an item page - elementsPerPage: this.appConfig.item.bitstream.pageSize - } + findListOptions: { + // limit the number of bitstreams used to find the citation pdf url to the number + // shown by default on an item page + elementsPerPage: this.appConfig.item.bitstream.pageSize + } }, followLink('format')), ).pipe( getFirstSucceededRemoteDataPayload(), @@ -467,63 +520,13 @@ export class MetadataService { ).subscribe((link: string) => { // Use the found link to set the tag this.addMetaTag( - 'citation_pdf_url', + tag, new URLCombiner(this.hardRedirectService.getCurrentOrigin(), link).toString() ); }); } } - /** - * Add to the - */ - private setOpenGraphTitleTag(): void { - const value = this.getMetaTagValue('dc.title'); - this.addMetaTag('og:title', value); - } - - /** - * Add to the - */ - private setOpenGraphDescriptionTag(): void { - // TODO: truncate abstract - const value = this.getMetaTagValue('dc.description.abstract'); - this.addMetaTag('og:description', value); - } - - /** - * Add to the - */ - private setOpenGraphImageTag(): void { - const value = ''; - this.addMetaTag('og:image', value); - } - - /** - * Add to the - */ - private setTwitterTitleTag(): void { - const value = this.getMetaTagValue('dc.title'); - this.addMetaTag('twitter:title', value); - } - - /** - * Add to the - */ - private setTwitterDescriptionTag(): void { - // TODO: truncate abstract - const value = this.getMetaTagValue('dc.description.abstract'); - this.addMetaTag('twitter:description', value); - } - - /** - * Add to the - */ - private setTwitterImageTag(): void { - const value = ''; - this.addMetaTag('twitter:image', value); - } - getBitLinkIfDownloadable(bitstream: Bitstream, bitstreamRd: RemoteData>): Observable { return observableOf(bitstream).pipe( getDownloadableBitstream(this.authorizationService), From 13ac71bed419759570cce1f8552411b8937c6486 Mon Sep 17 00:00:00 2001 From: "yevhenii.lohatskyi" Date: Tue, 16 Jan 2024 17:51:49 +0200 Subject: [PATCH 03/25] [DSC-1433] fix small comment error --- src/app/core/metadata/metadata.service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/core/metadata/metadata.service.ts b/src/app/core/metadata/metadata.service.ts index 9850c89210c..579517c6f50 100644 --- a/src/app/core/metadata/metadata.service.ts +++ b/src/app/core/metadata/metadata.service.ts @@ -464,7 +464,7 @@ export class MetadataService { if (this.currentObject.value instanceof Item) { const item = this.currentObject.value as Item; - // Retrieve the THUMBNAIL bundle for the item + // Retrieve the bundle for the item this.bundleDataService.findByItemAndName( item, bundleName, From e155f25169d64294cb6960ef443ca8cc30ac00c9 Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Fri, 19 Jan 2024 18:10:25 +0100 Subject: [PATCH 04/25] add basic implementation to prevent script loading --- src/app/shared/cookies/klaro-configuration.ts | 2 +- .../metric-altmetric.component.html | 5 +- .../metric-dimensions.component.html | 5 +- .../base-embedded-metric.component.ts | 2 +- .../metric-loader/base-metric.component.ts | 10 ++++ .../metric-loader/metric-loader.component.ts | 60 ++++++++++++++++--- .../metric-loader/metric-loader.service.ts | 4 +- .../metric-plumx/metric-plumx.component.html | 5 +- .../metric-plumx/metric-plumx.component.ts | 2 +- src/app/shared/metric/metrics.module.ts | 8 +++ src/assets/i18n/en.json5 | 2 + 11 files changed, 89 insertions(+), 16 deletions(-) diff --git a/src/app/shared/cookies/klaro-configuration.ts b/src/app/shared/cookies/klaro-configuration.ts index c818ddc19cb..dbf0bed44a1 100644 --- a/src/app/shared/cookies/klaro-configuration.ts +++ b/src/app/shared/cookies/klaro-configuration.ts @@ -133,7 +133,7 @@ export const klaroConfiguration: any = { cookies: [ [/^klaro-.+$/], HAS_AGREED_END_USER - ] + ], }, { name: GOOGLE_ANALYTICS_KLARO_KEY, diff --git a/src/app/shared/metric/metric-altmetric/metric-altmetric.component.html b/src/app/shared/metric/metric-altmetric/metric-altmetric.component.html index 805dc630900..e14aef4bd69 100644 --- a/src/app/shared/metric/metric-altmetric/metric-altmetric.component.html +++ b/src/app/shared/metric/metric-altmetric/metric-altmetric.component.html @@ -1,5 +1,5 @@
+ *ngIf="!failed && canLoadScript && !(isHidden$ | async) && (remark | dsListMetricProps: 'data-badge-enabled':isListElement == true)">
+
+ {{ "third-party-metrics-cookies.message" | translate: {metricType: metric.metricType | titlecase} }} +
diff --git a/src/app/shared/metric/metric-dimensions/metric-dimensions.component.html b/src/app/shared/metric/metric-dimensions/metric-dimensions.component.html index eeb4eed38e8..d498c1a3da6 100644 --- a/src/app/shared/metric/metric-dimensions/metric-dimensions.component.html +++ b/src/app/shared/metric/metric-dimensions/metric-dimensions.component.html @@ -1,5 +1,5 @@
+ *ngIf="!(isHidden$ | async) && canLoadScript && !failed && (remark | dsListMetricProps: 'data-badge-enabled':isListElement == true)">
+
+ {{ "third-party-metrics-cookies.message" | translate: {metricType: metric.metricType | titlecase} }} +
diff --git a/src/app/shared/metric/metric-loader/base-embedded-metric.component.ts b/src/app/shared/metric/metric-loader/base-embedded-metric.component.ts index a92783b4849..cdb00867b1d 100644 --- a/src/app/shared/metric/metric-loader/base-embedded-metric.component.ts +++ b/src/app/shared/metric/metric-loader/base-embedded-metric.component.ts @@ -46,7 +46,7 @@ export abstract class BaseEmbeddedMetricComponent extends BaseMetricComponent im * When the html content has been initialized, initialize the script. */ ngAfterViewInit() { - if (this.metric) { + if (this.metric && this.canLoadScript) { this.initScript(); } } diff --git a/src/app/shared/metric/metric-loader/base-metric.component.ts b/src/app/shared/metric/metric-loader/base-metric.component.ts index c5c9231551d..ee85a689330 100644 --- a/src/app/shared/metric/metric-loader/base-metric.component.ts +++ b/src/app/shared/metric/metric-loader/base-metric.component.ts @@ -20,11 +20,21 @@ export abstract class BaseMetricComponent { @Output() hide: EventEmitter = new EventEmitter(); + /** + * Emitter to trigger a new prompt of the cookies modal + */ + @Output() requestSettingsConsent: EventEmitter = new EventEmitter(); + /** * A boolean representing if the metric content is hidden or not */ isHidden$: BehaviorSubject = new BehaviorSubject(false); + /** + * A boolean to check if the component can load the associated script + */ + canLoadScript: boolean; + /** * Get the detail url form metric remark if present. */ diff --git a/src/app/shared/metric/metric-loader/metric-loader.component.ts b/src/app/shared/metric/metric-loader/metric-loader.component.ts index 3fbcfb48fda..f9dd0e492cf 100644 --- a/src/app/shared/metric/metric-loader/metric-loader.component.ts +++ b/src/app/shared/metric/metric-loader/metric-loader.component.ts @@ -1,7 +1,7 @@ import { Component, ComponentFactoryResolver, - EventEmitter, + EventEmitter, Inject, Input, OnDestroy, OnInit, @@ -14,6 +14,10 @@ import { Metric } from '../../../core/shared/metric.model'; import { BaseMetricComponent } from './base-metric.component'; import { MetricLoaderService } from './metric-loader.service'; import { hasValue } from '../../empty.util'; +import { BrowserKlaroService } from '../../cookies/browser-klaro.service'; +import { KlaroService } from '../../cookies/klaro.service'; +import { Router } from '@angular/router'; +import { DOCUMENT } from '@angular/common'; @Component({ // eslint-disable-next-line @angular-eslint/component-selector @@ -39,25 +43,44 @@ export class MetricLoaderComponent implements OnInit, OnDestroy { subscription: Subscription; + cookiesSubscription: Subscription; + + settingsSubscription: Subscription; + + private thirdPartyMetrics = ['plumX', 'altmetric', 'dimensions']; + constructor( private componentFactoryResolver: ComponentFactoryResolver, - private metricLoaderService: MetricLoaderService - ) { } + private metricLoaderService: MetricLoaderService, + private browserKlaroService: BrowserKlaroService, + private cookies: KlaroService, + private router: Router, + @Inject(DOCUMENT) private _document: Document, + ) { + this.router.routeReuseStrategy.shouldReuseRoute = () => { + return false; + }; + } ngOnInit() { - this.loadComponent(this.metric); + this.cookiesSubscription = this.browserKlaroService.getSavedPreferences().subscribe((preferences) => { + const canLoadScript = (hasValue(preferences) && preferences.acknowledgement && this.thirdPartyMetrics.includes(this.metric.metricType)) + || !this.thirdPartyMetrics.includes(this.metric.metricType); + + this.loadComponent(this.metric, canLoadScript); + }); } - loadComponent(metric: Metric) { + loadComponent(metric: Metric, canLoadScript: boolean) { if (!metric) { return; } - this.metricLoaderService.loadMetricTypeComponent(metric.metricType).then((component) => { - this.instantiateComponent(component, metric); + this.metricLoaderService.loadMetricTypeComponent(metric.metricType, canLoadScript).then((component) => { + this.instantiateComponent(component, metric, canLoadScript); }); } - instantiateComponent(component: any, metric: Metric) { + instantiateComponent(component: any, metric: Metric, canLoadScript: boolean) { const factory = this.componentFactoryResolver.resolveComponentFactory(component); this.componentType = component; const ref = this.container.createComponent(factory); @@ -65,6 +88,15 @@ export class MetricLoaderComponent implements OnInit, OnDestroy { componentInstance.metric = metric; componentInstance.hideLabel = this.hideLabel; componentInstance.isListElement = this.isListElement; + componentInstance.canLoadScript = canLoadScript; + + + if (!canLoadScript) { + this.settingsSubscription = componentInstance.requestSettingsConsent.subscribe(() => { + this.cookies.showSettings(); + //TODO: find a way to reload page once setting have been accepted also via footer + }); + } this.subscription = componentInstance.hide.subscribe((event) => { this.isVisible$.next(!event); @@ -74,9 +106,21 @@ export class MetricLoaderComponent implements OnInit, OnDestroy { ref.changeDetectorRef.detectChanges(); } + + public refreshPageForMetricsBadge(): void { + this.router.navigateByUrl(this.router.url); + } + + ngOnDestroy(): void { if (hasValue(this.subscription)) { this.subscription.unsubscribe(); } + + if (hasValue(this.settingsSubscription)) { + this.settingsSubscription.unsubscribe(); + } + + this.cookiesSubscription.unsubscribe(); } } diff --git a/src/app/shared/metric/metric-loader/metric-loader.service.ts b/src/app/shared/metric/metric-loader/metric-loader.service.ts index 115442c8dd0..3f70a9c9593 100644 --- a/src/app/shared/metric/metric-loader/metric-loader.service.ts +++ b/src/app/shared/metric/metric-loader/metric-loader.service.ts @@ -60,10 +60,10 @@ export class MetricLoaderService { * @param metricType * @return the ComponentClass for the metricType */ - public loadMetricTypeComponent(metricType: string): Promise { + public loadMetricTypeComponent(metricType: string, canLoadScript = true): Promise { const component = this.getComponent(metricType); const scriptSrc = this.getScript(metricType); - if (scriptSrc) { + if (scriptSrc && canLoadScript) { return this.metricLoadScriptService.loadScript(metricType, scriptSrc).then(() => component); } return of(component).toPromise(); diff --git a/src/app/shared/metric/metric-plumx/metric-plumx.component.html b/src/app/shared/metric/metric-plumx/metric-plumx.component.html index 088361c2be4..d69be5eb59a 100644 --- a/src/app/shared/metric/metric-plumx/metric-plumx.component.html +++ b/src/app/shared/metric/metric-plumx/metric-plumx.component.html @@ -1,4 +1,4 @@ -
+ +
+ {{ "third-party-metrics-cookies.message" | translate: {metricType: metric.metricType | titlecase} }} +
diff --git a/src/app/shared/metric/metric-plumx/metric-plumx.component.ts b/src/app/shared/metric/metric-plumx/metric-plumx.component.ts index d6c4afb5fc7..07f92276d4d 100644 --- a/src/app/shared/metric/metric-plumx/metric-plumx.component.ts +++ b/src/app/shared/metric/metric-plumx/metric-plumx.component.ts @@ -27,7 +27,7 @@ export class MetricPlumxComponent extends BaseMetricComponent implements OnInit, } async ngOnInit() { - if (hasValue(this.metric.remark)) { + if (hasValue(this.metric.remark) && this.canLoadScript) { this.remark = this.parseRemark(); const script = this.isListElement ? this.remark['list-src'] diff --git a/src/app/shared/metric/metrics.module.ts b/src/app/shared/metric/metrics.module.ts index 825eba26a69..93bf8686f59 100644 --- a/src/app/shared/metric/metrics.module.ts +++ b/src/app/shared/metric/metrics.module.ts @@ -19,6 +19,7 @@ import { MetricBadgesComponent } from '../object-list/metric-badges/metric-badge import { MetricDonutsComponent } from '../object-list/metric-donuts/metric-donuts.component'; import { FormsModule } from '@angular/forms'; import { DirectivesModule } from '../../directives/directives.module'; +import { BrowserKlaroService } from '../cookies/browser-klaro.service'; const PIPES = [ MetricStyleConfigPipe, @@ -45,6 +46,10 @@ const COMPONENTS = [ MetricDefaultComponent ]; +const PROVIDERS = [ + BrowserKlaroService +]; + @NgModule({ declarations: [ ...PIPES, @@ -57,6 +62,9 @@ const COMPONENTS = [ useDefaultLang: true }), ], + providers: [ + ...PROVIDERS + ], exports: [ ...COMPONENTS, ] diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 766b502d657..09f592c46a3 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -7292,4 +7292,6 @@ "external-login-page.provide-email.create-account.notifications.error.content": "Please check again your email address and try again.", "external-login-page.confirm-email.create-account.notifications.error.no-netId": "Something went wrong with this email account. Try again or use a different method to login.", + + "third-party-metrics-cookies.message": "{{ metricType }} badge is blocked by your consent settings", } From 001dae4c9c56035c4bc07cea194accc81a3340c0 Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Sun, 21 Jan 2024 12:27:51 +0100 Subject: [PATCH 05/25] add logic to force rerendering and listen to consents changes --- .../shared/cookies/browser-klaro.service.ts | 27 +++++- .../metric-altmetric.component.html | 9 +- .../metric-dimensions.component.html | 9 +- .../metric-loader/base-metric.component.ts | 5 ++ .../metric-loader/metric-loader.component.ts | 84 +++++++++++++------ .../metric-loader/metric-loader.service.ts | 1 + .../metric-plumx/metric-plumx.component.html | 8 +- 7 files changed, 110 insertions(+), 33 deletions(-) diff --git a/src/app/shared/cookies/browser-klaro.service.ts b/src/app/shared/cookies/browser-klaro.service.ts index 2b09c0bf155..34ba2aa81b0 100644 --- a/src/app/shared/cookies/browser-klaro.service.ts +++ b/src/app/shared/cookies/browser-klaro.service.ts @@ -1,5 +1,5 @@ import { Inject, Injectable, InjectionToken } from '@angular/core'; -import { combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs'; +import { BehaviorSubject, combineLatest as observableCombineLatest, Observable, of as observableOf } from 'rxjs'; import { AuthService } from '../../core/auth/auth.service'; import { TranslateService } from '@ngx-translate/core'; import { environment } from '../../../environments/environment'; @@ -17,6 +17,11 @@ import { getFirstCompletedRemoteData } from '../../core/shared/operators'; import { ConfigurationDataService } from '../../core/data/configuration-data.service'; import { CAPTCHA_NAME } from '../../core/google-recaptcha/google-recaptcha.service'; +export interface CookieConsents { + acknowledgement: boolean; + authentication: boolean; + preferences: boolean; +} /** * Metadata field to store a user's cookie consent preferences in */ @@ -70,6 +75,12 @@ export class BrowserKlaroService extends KlaroService { */ klaroConfig = cloneDeep(klaroConfiguration); + /** + * Subject to emit updates in the consents + */ + consentsUpdates$: BehaviorSubject = new BehaviorSubject(null); + + constructor( private translateService: TranslateService, private authService: AuthService, @@ -331,6 +342,20 @@ export class BrowserKlaroService extends KlaroService { return 'klaro-' + identifier; } + watchConsentUpdates() { + const that = this; + this.lazyKlaro.then(({getManager}) => { + const manager = getManager(this.klaroConfig) + manager.watch({ + update(_, eventName, consents) { + if(eventName === 'consents') { + that.consentsUpdates$.next(consents) + } + } + }) + }) + } + /** * remove the google analytics from the services */ diff --git a/src/app/shared/metric/metric-altmetric/metric-altmetric.component.html b/src/app/shared/metric/metric-altmetric/metric-altmetric.component.html index e14aef4bd69..eea80b52118 100644 --- a/src/app/shared/metric/metric-altmetric/metric-altmetric.component.html +++ b/src/app/shared/metric/metric-altmetric/metric-altmetric.component.html @@ -1,10 +1,15 @@
+ *ngIf="(!failed && + canLoadScript && + !(isHidden$ | async) && + (remark | dsListMetricProps: 'data-badge-enabled':isListElement == true)) || + (canLoadScript && visibleWithoutData)" +>
+ *ngIf="!(isHidden$ | async) && + canLoadScript && + !failed && + (remark | dsListMetricProps: 'data-badge-enabled':isListElement == true) || + (canLoadScript && visibleWithoutData)" +>
= new BehaviorSubject(true); + consentUpdates$: BehaviorSubject; + subscription: Subscription; cookiesSubscription: Subscription; @@ -53,34 +54,41 @@ export class MetricLoaderComponent implements OnInit, OnDestroy { private componentFactoryResolver: ComponentFactoryResolver, private metricLoaderService: MetricLoaderService, private browserKlaroService: BrowserKlaroService, - private cookies: KlaroService, - private router: Router, - @Inject(DOCUMENT) private _document: Document, + private klaroService: KlaroService, ) { - this.router.routeReuseStrategy.shouldReuseRoute = () => { - return false; - }; + (this.klaroService as BrowserKlaroService).watchConsentUpdates(); + this.consentUpdates$ = (this.klaroService as BrowserKlaroService).consentsUpdates$; } ngOnInit() { - this.cookiesSubscription = this.browserKlaroService.getSavedPreferences().subscribe((preferences) => { - const canLoadScript = (hasValue(preferences) && preferences.acknowledgement && this.thirdPartyMetrics.includes(this.metric.metricType)) - || !this.thirdPartyMetrics.includes(this.metric.metricType); - - this.loadComponent(this.metric, canLoadScript); + this.cookiesSubscription = this.browserKlaroService.getSavedPreferences().subscribe((consents) => { + this.loadComponent(this.metric, this.getCanLoadScript(consents)); }); } - loadComponent(metric: Metric, canLoadScript: boolean) { + loadComponent(metric: Metric, canLoadScript: boolean, forceRendering?: boolean) { if (!metric) { return; } this.metricLoaderService.loadMetricTypeComponent(metric.metricType, canLoadScript).then((component) => { - this.instantiateComponent(component, metric, canLoadScript); + if(hasValue(this.cookiesSubscription) && canLoadScript) { + this.container.clear(); + this.cookiesSubscription.unsubscribe() + } + this.instantiateComponent(component, metric, canLoadScript, forceRendering); }); } - instantiateComponent(component: any, metric: Metric, canLoadScript: boolean) { + /** + * Instantiate component in view container + * + * @param component + * @param metric + * @param canLoadScript + * @param forceRendering + */ + + instantiateComponent(component: any, metric: Metric, canLoadScript: boolean, forceRendering?: boolean) { const factory = this.componentFactoryResolver.resolveComponentFactory(component); this.componentType = component; const ref = this.container.createComponent(factory); @@ -89,13 +97,11 @@ export class MetricLoaderComponent implements OnInit, OnDestroy { componentInstance.hideLabel = this.hideLabel; componentInstance.isListElement = this.isListElement; componentInstance.canLoadScript = canLoadScript; + componentInstance.visibleWithoutData = forceRendering; if (!canLoadScript) { - this.settingsSubscription = componentInstance.requestSettingsConsent.subscribe(() => { - this.cookies.showSettings(); - //TODO: find a way to reload page once setting have been accepted also via footer - }); + this.reloadComponentOnConsentsChange(componentInstance, canLoadScript) } this.subscription = componentInstance.hide.subscribe((event) => { @@ -106,9 +112,39 @@ export class MetricLoaderComponent implements OnInit, OnDestroy { ref.changeDetectorRef.detectChanges(); } + /** + * get condition to check if badge can load a script + * @param consents + */ + private getCanLoadScript(consents: CookieConsents) : boolean { + return (hasValue(consents) && consents.acknowledgement && this.thirdPartyMetrics.includes(this.metric.metricType)) + || !this.thirdPartyMetrics.includes(this.metric.metricType); + } - public refreshPageForMetricsBadge(): void { - this.router.navigateByUrl(this.router.url); + /** + * Listen to cookie consents change and reload component + * + * @param componentInstance + * @param canLoadScript + * @private + */ + private reloadComponentOnConsentsChange(componentInstance: BaseMetricComponent, canLoadScript: boolean) : void { + this.settingsSubscription = combineLatest([ + this.consentUpdates$.pipe(distinctUntilChanged( + (previousConsents, currentConsents) => JSON.stringify(previousConsents) === JSON.stringify(currentConsents)) + ), + componentInstance.requestSettingsConsent.pipe(startWith(undefined)) + ]).subscribe(([consents, request]) => { + canLoadScript = this.getCanLoadScript(consents); + + if(request && !canLoadScript) { + this.klaroService.showSettings(); + } + + if(canLoadScript) { + this.loadComponent(this.metric, canLoadScript, true) + } + }); } diff --git a/src/app/shared/metric/metric-loader/metric-loader.service.ts b/src/app/shared/metric/metric-loader/metric-loader.service.ts index 3f70a9c9593..f082cd5d4bb 100644 --- a/src/app/shared/metric/metric-loader/metric-loader.service.ts +++ b/src/app/shared/metric/metric-loader/metric-loader.service.ts @@ -58,6 +58,7 @@ export class MetricLoaderService { /** * Load required data for the metricType and then return the Component type. * @param metricType + * @param canLoadScript * @return the ComponentClass for the metricType */ public loadMetricTypeComponent(metricType: string, canLoadScript = true): Promise { diff --git a/src/app/shared/metric/metric-plumx/metric-plumx.component.html b/src/app/shared/metric/metric-plumx/metric-plumx.component.html index d69be5eb59a..d1720adb0f7 100644 --- a/src/app/shared/metric/metric-plumx/metric-plumx.component.html +++ b/src/app/shared/metric/metric-plumx/metric-plumx.component.html @@ -1,7 +1,7 @@ - -
+
{{ "third-party-metrics-cookies.message" | translate: {metricType: metric.metricType | titlecase} }} +
diff --git a/src/app/shared/metric/metric-dimensions/metric-dimensions.component.html b/src/app/shared/metric/metric-dimensions/metric-dimensions.component.html index fa5f10d5ad4..96ab0e4b1c5 100644 --- a/src/app/shared/metric/metric-dimensions/metric-dimensions.component.html +++ b/src/app/shared/metric/metric-dimensions/metric-dimensions.component.html @@ -26,6 +26,7 @@
-
+
{{ "third-party-metrics-cookies.message" | translate: {metricType: metric.metricType | titlecase} }} +
diff --git a/src/app/shared/metric/metric-loader/metric-loader.component.ts b/src/app/shared/metric/metric-loader/metric-loader.component.ts index 890f75e2170..19e4e2b803b 100644 --- a/src/app/shared/metric/metric-loader/metric-loader.component.ts +++ b/src/app/shared/metric/metric-loader/metric-loader.component.ts @@ -50,14 +50,16 @@ export class MetricLoaderComponent implements OnInit, OnDestroy { private thirdPartyMetrics = ['plumX', 'altmetric', 'dimensions']; + private browserKlaroService: BrowserKlaroService; + constructor( private componentFactoryResolver: ComponentFactoryResolver, private metricLoaderService: MetricLoaderService, - private browserKlaroService: BrowserKlaroService, private klaroService: KlaroService, ) { - (this.klaroService as BrowserKlaroService).watchConsentUpdates(); - this.consentUpdates$ = (this.klaroService as BrowserKlaroService).consentsUpdates$; + this.browserKlaroService = (this.klaroService as BrowserKlaroService); + this.browserKlaroService.watchConsentUpdates(); + this.consentUpdates$ = this.browserKlaroService.consentsUpdates$; } ngOnInit() { @@ -135,10 +137,11 @@ export class MetricLoaderComponent implements OnInit, OnDestroy { ), componentInstance.requestSettingsConsent.pipe(startWith(undefined)) ]).subscribe(([consents, request]) => { + console.log(consents) canLoadScript = this.getCanLoadScript(consents); if(request && !canLoadScript) { - this.klaroService.showSettings(); + this.browserKlaroService.showSettings(); } if(canLoadScript) { diff --git a/src/app/shared/metric/metric-plumx/metric-plumx.component.html b/src/app/shared/metric/metric-plumx/metric-plumx.component.html index d1720adb0f7..bb17f639dfa 100644 --- a/src/app/shared/metric/metric-plumx/metric-plumx.component.html +++ b/src/app/shared/metric/metric-plumx/metric-plumx.component.html @@ -31,6 +31,7 @@
-
+
{{ "third-party-metrics-cookies.message" | translate: {metricType: metric.metricType | titlecase} }} +
diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 09f592c46a3..5258aaab3a5 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -7293,5 +7293,7 @@ "external-login-page.confirm-email.create-account.notifications.error.no-netId": "Something went wrong with this email account. Try again or use a different method to login.", - "third-party-metrics-cookies.message": "{{ metricType }} badge is blocked by your consent settings", + "third-party-metrics-cookies.message": "{{ metricType }} badge is blocked by your ", + + "third-party-metrics-cookies.consent-settings": "consent settings", } From d5a10d04ed75adf618eae641909169eda8d76951 Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Sun, 21 Jan 2024 13:04:50 +0100 Subject: [PATCH 07/25] fix lint --- .../shared/cookies/browser-klaro.service.ts | 12 +++++------ .../metric-loader/metric-loader.component.ts | 21 +++++++++---------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/app/shared/cookies/browser-klaro.service.ts b/src/app/shared/cookies/browser-klaro.service.ts index 34ba2aa81b0..b01debdf0bc 100644 --- a/src/app/shared/cookies/browser-klaro.service.ts +++ b/src/app/shared/cookies/browser-klaro.service.ts @@ -343,17 +343,17 @@ export class BrowserKlaroService extends KlaroService { } watchConsentUpdates() { - const that = this; this.lazyKlaro.then(({getManager}) => { - const manager = getManager(this.klaroConfig) + const manager = getManager(this.klaroConfig); + const consentsSubject$ = this.consentsUpdates$; manager.watch({ update(_, eventName, consents) { - if(eventName === 'consents') { - that.consentsUpdates$.next(consents) + if (eventName === 'consents') { + consentsSubject$.next(consents); } } - }) - }) + }); + }); } /** diff --git a/src/app/shared/metric/metric-loader/metric-loader.component.ts b/src/app/shared/metric/metric-loader/metric-loader.component.ts index 19e4e2b803b..8ecaa89a465 100644 --- a/src/app/shared/metric/metric-loader/metric-loader.component.ts +++ b/src/app/shared/metric/metric-loader/metric-loader.component.ts @@ -1,7 +1,7 @@ import { Component, ComponentFactoryResolver, - EventEmitter, Inject, + EventEmitter, Input, OnDestroy, OnInit, @@ -16,7 +16,7 @@ import { MetricLoaderService } from './metric-loader.service'; import { hasValue } from '../../empty.util'; import { BrowserKlaroService, CookieConsents } from '../../cookies/browser-klaro.service'; import { KlaroService } from '../../cookies/klaro.service'; -import { distinctUntilChanged, startWith } from "rxjs/operators"; +import { distinctUntilChanged, startWith } from 'rxjs/operators'; @Component({ // eslint-disable-next-line @angular-eslint/component-selector @@ -73,9 +73,9 @@ export class MetricLoaderComponent implements OnInit, OnDestroy { return; } this.metricLoaderService.loadMetricTypeComponent(metric.metricType, canLoadScript).then((component) => { - if(hasValue(this.cookiesSubscription) && canLoadScript) { + if (hasValue(this.cookiesSubscription) && canLoadScript) { this.container.clear(); - this.cookiesSubscription.unsubscribe() + this.cookiesSubscription.unsubscribe(); } this.instantiateComponent(component, metric, canLoadScript, forceRendering); }); @@ -103,7 +103,7 @@ export class MetricLoaderComponent implements OnInit, OnDestroy { if (!canLoadScript) { - this.reloadComponentOnConsentsChange(componentInstance, canLoadScript) + this.reloadComponentOnConsentsChange(componentInstance, canLoadScript); } this.subscription = componentInstance.hide.subscribe((event) => { @@ -118,7 +118,7 @@ export class MetricLoaderComponent implements OnInit, OnDestroy { * get condition to check if badge can load a script * @param consents */ - private getCanLoadScript(consents: CookieConsents) : boolean { + private getCanLoadScript(consents: CookieConsents): boolean { return (hasValue(consents) && consents.acknowledgement && this.thirdPartyMetrics.includes(this.metric.metricType)) || !this.thirdPartyMetrics.includes(this.metric.metricType); } @@ -130,22 +130,21 @@ export class MetricLoaderComponent implements OnInit, OnDestroy { * @param canLoadScript * @private */ - private reloadComponentOnConsentsChange(componentInstance: BaseMetricComponent, canLoadScript: boolean) : void { + private reloadComponentOnConsentsChange(componentInstance: BaseMetricComponent, canLoadScript: boolean): void { this.settingsSubscription = combineLatest([ this.consentUpdates$.pipe(distinctUntilChanged( (previousConsents, currentConsents) => JSON.stringify(previousConsents) === JSON.stringify(currentConsents)) ), componentInstance.requestSettingsConsent.pipe(startWith(undefined)) ]).subscribe(([consents, request]) => { - console.log(consents) canLoadScript = this.getCanLoadScript(consents); - if(request && !canLoadScript) { + if (request && !canLoadScript) { this.browserKlaroService.showSettings(); } - if(canLoadScript) { - this.loadComponent(this.metric, canLoadScript, true) + if (canLoadScript) { + this.loadComponent(this.metric, canLoadScript, true); } }); } From 2f498a2e48a006c80c880043c2f9420ab81ff62a Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Mon, 22 Jan 2024 14:47:47 +0100 Subject: [PATCH 08/25] clean up, add tests, refine solution --- .../metric-altmetric.component.html | 3 +- .../metric-dimensions.component.html | 3 +- .../metric-loader/base-metric.component.ts | 4 +- .../metric-loader.component.spec.ts | 76 +++++++++++++++++-- .../metric-loader/metric-loader.component.ts | 2 +- .../metric-plumx/metric-plumx.component.html | 8 +- .../metric-plumx/metric-plumx.component.ts | 15 ++-- 7 files changed, 87 insertions(+), 24 deletions(-) diff --git a/src/app/shared/metric/metric-altmetric/metric-altmetric.component.html b/src/app/shared/metric/metric-altmetric/metric-altmetric.component.html index fcb434610f1..42bbb6488ab 100644 --- a/src/app/shared/metric/metric-altmetric/metric-altmetric.component.html +++ b/src/app/shared/metric/metric-altmetric/metric-altmetric.component.html @@ -2,8 +2,7 @@ *ngIf="(!failed && canLoadScript && !(isHidden$ | async) && - (remark | dsListMetricProps: 'data-badge-enabled':isListElement == true)) || - (canLoadScript && visibleWithoutData)" + (remark | dsListMetricProps: 'data-badge-enabled':isListElement == true))" >
diff --git a/src/app/shared/metric/metric-dimensions/metric-dimensions.component.html b/src/app/shared/metric/metric-dimensions/metric-dimensions.component.html index 96ab0e4b1c5..46182caf1b4 100644 --- a/src/app/shared/metric/metric-dimensions/metric-dimensions.component.html +++ b/src/app/shared/metric/metric-dimensions/metric-dimensions.component.html @@ -2,8 +2,7 @@ *ngIf="!(isHidden$ | async) && canLoadScript && !failed && - (remark | dsListMetricProps: 'data-badge-enabled':isListElement == true) || - (canLoadScript && visibleWithoutData)" + (remark | dsListMetricProps: 'data-badge-enabled':isListElement == true)" >
{ let component: MetricLoaderComponent; let fixture: ComponentFixture; let metricLoaderService: SpyObj; + let klaroServiceSpy: jasmine.SpyObj; + + + const consentsAccepted: CookieConsents = { + acknowledgement: true, + authentication: true, + preferences: true + }; + beforeEach(waitForAsync(() => { + (TestComponent as unknown as BaseMetricComponent).hide = new EventEmitter(); + (TestComponent as unknown as BaseMetricComponent).requestSettingsConsent = new EventEmitter(); metricLoaderService = jasmine.createSpyObj('MetricLoaderService', { loadMetricTypeComponent: jasmine.createSpy('loadMetricTypeComponent') }); metricLoaderService.loadMetricTypeComponent.and.returnValue(of(TestComponent).toPromise()); + klaroServiceSpy = jasmine.createSpyObj('KlaroService', { + getSavedPreferences: jasmine.createSpy('getSavedPreferences'), + watchConsentUpdates: jasmine.createSpy('watchConsentUpdates') + },{ + consentsUpdates$: of(consentsAccepted) + }); + + klaroServiceSpy.getSavedPreferences.and.returnValue(of(consentsAccepted)); + + TestBed.configureTestingModule({ declarations: [ MetricLoaderComponent, MetricStyleConfigPipe ], providers: [ - { provide: MetricLoaderService, useValue: metricLoaderService } + { provide: MetricLoaderService, useValue: metricLoaderService }, + { provide: KlaroService, useValue: klaroServiceSpy }, ], }) .compileComponents(); @@ -32,6 +59,7 @@ describe('MetricLoaderComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(MetricLoaderComponent); component = fixture.componentInstance; + component.metric = metric1Mock; spyOn(component, 'loadComponent').and.callThrough(); fixture.detectChanges(); @@ -39,7 +67,7 @@ describe('MetricLoaderComponent', () => { it('should create', () => { expect(component).toBeTruthy(); - expect(component.loadComponent).toHaveBeenCalledWith(component.metric); + expect(component.loadComponent).toHaveBeenCalledWith(component.metric, true); }); describe('loadComponent', () => { @@ -50,16 +78,49 @@ describe('MetricLoaderComponent', () => { it('should instantiate the component loaded from service', fakeAsync(() => { - component.loadComponent(metric1Mock); + component.loadComponent(metric1Mock, true); tick(); // wait loadMetricT - expect(metricLoaderService.loadMetricTypeComponent).toHaveBeenCalledWith(metric1Mock.metricType); - expect(component.instantiateComponent).toHaveBeenCalledWith(TestComponent, metric1Mock); + expect(metricLoaderService.loadMetricTypeComponent).toHaveBeenCalledWith(metric1Mock.metricType, true); + expect(component.instantiateComponent).toHaveBeenCalledWith(TestComponent, metric1Mock, true, undefined); })); }); + describe('Script handling', () => { + + beforeEach(() => { + klaroServiceSpy.getSavedPreferences.and.returnValue(of(consentsAccepted)); + }); + + it('should instantiate the component without loading the script', fakeAsync(() => { + + component.loadComponent(metric1Mock, false); + tick(); // wait loadMetricT + + expect(metricLoaderService.loadMetricTypeComponent).toHaveBeenCalledWith(metric1Mock.metricType, false); + expect(((TestComponent as unknown as BaseMetricComponent).canLoadScript)).toBeFalsy(); + })); + + }); + + + describe('getCanLoadScript', () => { + + it('should return true for not restricted metrics', fakeAsync(() => { + expect((component as any).getCanLoadScript(consentsAccepted)).toBeTruthy(); + })); + + it('should return false for restricted metrics', fakeAsync(() => { + const consentRejected = {...consentsAccepted, acknowledgement: false}; + const restrictedMetric = {...metric1Mock, metricType: 'altmetric'}; + component.metric = restrictedMetric; + expect((component as any).getCanLoadScript(consentRejected)).toBeFalsy(); + })); + + }); + }); // declare a test component @@ -68,5 +129,6 @@ describe('MetricLoaderComponent', () => { template: `` }) class TestComponent { - + hide = new EventEmitter(); + requestSettingsConsent = new EventEmitter(); } diff --git a/src/app/shared/metric/metric-loader/metric-loader.component.ts b/src/app/shared/metric/metric-loader/metric-loader.component.ts index 8ecaa89a465..dd8bab9f8a8 100644 --- a/src/app/shared/metric/metric-loader/metric-loader.component.ts +++ b/src/app/shared/metric/metric-loader/metric-loader.component.ts @@ -40,7 +40,7 @@ export class MetricLoaderComponent implements OnInit, OnDestroy { isVisible$: BehaviorSubject = new BehaviorSubject(true); - consentUpdates$: BehaviorSubject; + consentUpdates$: BehaviorSubject; subscription: Subscription; diff --git a/src/app/shared/metric/metric-plumx/metric-plumx.component.html b/src/app/shared/metric/metric-plumx/metric-plumx.component.html index bb17f639dfa..e5009467144 100644 --- a/src/app/shared/metric/metric-plumx/metric-plumx.component.html +++ b/src/app/shared/metric/metric-plumx/metric-plumx.component.html @@ -1,7 +1,7 @@ -
+ -
+
{{ "third-party-metrics-cookies.message" | translate: {metricType: metric.metricType | titlecase} }}
diff --git a/src/app/shared/metric/metric-plumx/metric-plumx.component.ts b/src/app/shared/metric/metric-plumx/metric-plumx.component.ts index 07f92276d4d..7c8f383a9bd 100644 --- a/src/app/shared/metric/metric-plumx/metric-plumx.component.ts +++ b/src/app/shared/metric/metric-plumx/metric-plumx.component.ts @@ -38,12 +38,15 @@ export class MetricPlumxComponent extends BaseMetricComponent implements OnInit, // use the method to find and render all placeholders that haven't already been initialized this._window.nativeWindow.__plumX.widgets.init(); - this.unlistenerEmpty = this.renderer2.listen( - this._window.nativeWindow, - 'plum:widget-empty', event => { - this.isHidden$.next(true); - this.hide.emit(true); - }); + if (!this.visibleWithoutData) { + this.unlistenerEmpty = this.renderer2.listen( + this._window.nativeWindow, + 'plum:widget-empty', event => { + this.isHidden$.next(true); + this.hide.emit(true); + }); + } + this.unlistenerError = this.renderer2.listen( this._window.nativeWindow, 'plum:widget-error', event => { From 6b988b4a97bf264661bd7c53d47fcaad01fe3efc Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Mon, 22 Jan 2024 14:48:41 +0100 Subject: [PATCH 09/25] remove redundant const --- .../metric/metric-loader/metric-loader.component.spec.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/app/shared/metric/metric-loader/metric-loader.component.spec.ts b/src/app/shared/metric/metric-loader/metric-loader.component.spec.ts index ba8f9324d49..6742184bdc5 100644 --- a/src/app/shared/metric/metric-loader/metric-loader.component.spec.ts +++ b/src/app/shared/metric/metric-loader/metric-loader.component.spec.ts @@ -114,8 +114,7 @@ describe('MetricLoaderComponent', () => { it('should return false for restricted metrics', fakeAsync(() => { const consentRejected = {...consentsAccepted, acknowledgement: false}; - const restrictedMetric = {...metric1Mock, metricType: 'altmetric'}; - component.metric = restrictedMetric; + component.metric = {...metric1Mock, metricType: 'altmetric'}; expect((component as any).getCanLoadScript(consentRejected)).toBeFalsy(); })); From 13e1a0281bc149bb25881e2fcc443984cc4e4903 Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Mon, 22 Jan 2024 17:05:34 +0100 Subject: [PATCH 10/25] remove pointless comma --- src/app/shared/cookies/klaro-configuration.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/shared/cookies/klaro-configuration.ts b/src/app/shared/cookies/klaro-configuration.ts index dbf0bed44a1..c818ddc19cb 100644 --- a/src/app/shared/cookies/klaro-configuration.ts +++ b/src/app/shared/cookies/klaro-configuration.ts @@ -133,7 +133,7 @@ export const klaroConfiguration: any = { cookies: [ [/^klaro-.+$/], HAS_AGREED_END_USER - ], + ] }, { name: GOOGLE_ANALYTICS_KLARO_KEY, From e6d1c9b890290e1392d6df26fb1955805717ee44 Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Mon, 29 Jan 2024 09:14:52 +0100 Subject: [PATCH 11/25] adjust style --- .../metric-altmetric/metric-altmetric.component.html | 8 +++++--- .../metric-dimensions/metric-dimensions.component.html | 8 +++++--- .../metric/metric-plumx/metric-plumx.component.html | 8 +++++--- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/app/shared/metric/metric-altmetric/metric-altmetric.component.html b/src/app/shared/metric/metric-altmetric/metric-altmetric.component.html index 42bbb6488ab..52bd77d7cd5 100644 --- a/src/app/shared/metric/metric-altmetric/metric-altmetric.component.html +++ b/src/app/shared/metric/metric-altmetric/metric-altmetric.component.html @@ -25,7 +25,9 @@
-
- {{ "third-party-metrics-cookies.message" | translate: {metricType: metric.metricType | titlecase} }} - +
+
+ {{ "third-party-metrics-cookies.message" | translate: {metricType: metric.metricType | titlecase} }} + +
diff --git a/src/app/shared/metric/metric-dimensions/metric-dimensions.component.html b/src/app/shared/metric/metric-dimensions/metric-dimensions.component.html index 46182caf1b4..a3ad46dec12 100644 --- a/src/app/shared/metric/metric-dimensions/metric-dimensions.component.html +++ b/src/app/shared/metric/metric-dimensions/metric-dimensions.component.html @@ -25,7 +25,9 @@
-
- {{ "third-party-metrics-cookies.message" | translate: {metricType: metric.metricType | titlecase} }} - +
+
+ {{ "third-party-metrics-cookies.message" | translate: {metricType: metric.metricType | titlecase} }} + +
diff --git a/src/app/shared/metric/metric-plumx/metric-plumx.component.html b/src/app/shared/metric/metric-plumx/metric-plumx.component.html index e5009467144..052c2bdf901 100644 --- a/src/app/shared/metric/metric-plumx/metric-plumx.component.html +++ b/src/app/shared/metric/metric-plumx/metric-plumx.component.html @@ -31,7 +31,9 @@
-
- {{ "third-party-metrics-cookies.message" | translate: {metricType: metric.metricType | titlecase} }} - +
+
+ {{ "third-party-metrics-cookies.message" | translate: {metricType: metric.metricType | titlecase} }} + +
From e580c429c439e1246fb98fdb6f2004aae4182f3e Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Wed, 31 Jan 2024 12:55:29 +0100 Subject: [PATCH 12/25] add new klaro section config for metrics, refactor loading --- src/app/shared/cookies/browser-klaro.service.ts | 10 ++++++++-- src/app/shared/cookies/klaro-configuration.ts | 15 +++++++++++++++ .../metric-loader/metric-loader.component.ts | 16 ++++++++-------- src/assets/i18n/en.json5 | 14 +++++++++++++- 4 files changed, 44 insertions(+), 11 deletions(-) diff --git a/src/app/shared/cookies/browser-klaro.service.ts b/src/app/shared/cookies/browser-klaro.service.ts index b01debdf0bc..73829622818 100644 --- a/src/app/shared/cookies/browser-klaro.service.ts +++ b/src/app/shared/cookies/browser-klaro.service.ts @@ -12,10 +12,11 @@ import { EPersonDataService } from '../../core/eperson/eperson-data.service'; import cloneDeep from 'lodash/cloneDeep'; import debounce from 'lodash/debounce'; import { ANONYMOUS_STORAGE_NAME_KLARO, klaroConfiguration } from './klaro-configuration'; -import { Operation } from 'fast-json-patch'; +import { deepClone, Operation } from 'fast-json-patch'; import { getFirstCompletedRemoteData } from '../../core/shared/operators'; import { ConfigurationDataService } from '../../core/data/configuration-data.service'; import { CAPTCHA_NAME } from '../../core/google-recaptcha/google-recaptcha.service'; +import isEqual from 'lodash/isEqual'; export interface CookieConsents { acknowledgement: boolean; @@ -70,6 +71,8 @@ export class BrowserKlaroService extends KlaroService { private readonly GOOGLE_ANALYTICS_SERVICE_NAME = 'google-analytics'; + private lastCookiesConsents: CookieConsents; + /** * Initial Klaro configuration */ @@ -346,9 +349,12 @@ export class BrowserKlaroService extends KlaroService { this.lazyKlaro.then(({getManager}) => { const manager = getManager(this.klaroConfig); const consentsSubject$ = this.consentsUpdates$; + let lastCookiesConsents = this.lastCookiesConsents; manager.watch({ update(_, eventName, consents) { - if (eventName === 'consents') { + + if (eventName === 'consents' && !isEqual(consents, lastCookiesConsents)) { + lastCookiesConsents = deepClone(consents); consentsSubject$.next(consents); } } diff --git a/src/app/shared/cookies/klaro-configuration.ts b/src/app/shared/cookies/klaro-configuration.ts index c818ddc19cb..a1954d5ad90 100644 --- a/src/app/shared/cookies/klaro-configuration.ts +++ b/src/app/shared/cookies/klaro-configuration.ts @@ -135,6 +135,21 @@ export const klaroConfiguration: any = { HAS_AGREED_END_USER ] }, + { + name: 'plumX', + purposes: ['thirdPartiesJs'], + required: false, + }, + { + name: 'altmetric', + purposes: ['thirdPartiesJs'], + required: false, + }, + { + name: 'dimensions', + purposes: ['thirdPartiesJs'], + required: false, + }, { name: GOOGLE_ANALYTICS_KLARO_KEY, purposes: ['statistical'], diff --git a/src/app/shared/metric/metric-loader/metric-loader.component.ts b/src/app/shared/metric/metric-loader/metric-loader.component.ts index dd8bab9f8a8..2dabf31baf1 100644 --- a/src/app/shared/metric/metric-loader/metric-loader.component.ts +++ b/src/app/shared/metric/metric-loader/metric-loader.component.ts @@ -16,7 +16,7 @@ import { MetricLoaderService } from './metric-loader.service'; import { hasValue } from '../../empty.util'; import { BrowserKlaroService, CookieConsents } from '../../cookies/browser-klaro.service'; import { KlaroService } from '../../cookies/klaro.service'; -import { distinctUntilChanged, startWith } from 'rxjs/operators'; +import { startWith } from 'rxjs/operators'; @Component({ // eslint-disable-next-line @angular-eslint/component-selector @@ -52,6 +52,8 @@ export class MetricLoaderComponent implements OnInit, OnDestroy { private browserKlaroService: BrowserKlaroService; + private hasLoadedScript: boolean; + constructor( private componentFactoryResolver: ComponentFactoryResolver, private metricLoaderService: MetricLoaderService, @@ -72,6 +74,7 @@ export class MetricLoaderComponent implements OnInit, OnDestroy { if (!metric) { return; } + this.hasLoadedScript = !!canLoadScript; this.metricLoaderService.loadMetricTypeComponent(metric.metricType, canLoadScript).then((component) => { if (hasValue(this.cookiesSubscription) && canLoadScript) { this.container.clear(); @@ -101,8 +104,7 @@ export class MetricLoaderComponent implements OnInit, OnDestroy { componentInstance.canLoadScript = canLoadScript; componentInstance.visibleWithoutData = forceRendering; - - if (!canLoadScript) { + if (!canLoadScript && !this.settingsSubscription) { this.reloadComponentOnConsentsChange(componentInstance, canLoadScript); } @@ -119,7 +121,7 @@ export class MetricLoaderComponent implements OnInit, OnDestroy { * @param consents */ private getCanLoadScript(consents: CookieConsents): boolean { - return (hasValue(consents) && consents.acknowledgement && this.thirdPartyMetrics.includes(this.metric.metricType)) + return (hasValue(consents) && consents[this.metric.metricType] && this.thirdPartyMetrics.includes(this.metric.metricType)) || !this.thirdPartyMetrics.includes(this.metric.metricType); } @@ -132,9 +134,7 @@ export class MetricLoaderComponent implements OnInit, OnDestroy { */ private reloadComponentOnConsentsChange(componentInstance: BaseMetricComponent, canLoadScript: boolean): void { this.settingsSubscription = combineLatest([ - this.consentUpdates$.pipe(distinctUntilChanged( - (previousConsents, currentConsents) => JSON.stringify(previousConsents) === JSON.stringify(currentConsents)) - ), + this.consentUpdates$, componentInstance.requestSettingsConsent.pipe(startWith(undefined)) ]).subscribe(([consents, request]) => { canLoadScript = this.getCanLoadScript(consents); @@ -143,7 +143,7 @@ export class MetricLoaderComponent implements OnInit, OnDestroy { this.browserKlaroService.showSettings(); } - if (canLoadScript) { + if (canLoadScript && !this.hasLoadedScript) { this.loadComponent(this.metric, canLoadScript, true); } }); diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index 5258aaab3a5..b484a3a7a41 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -1807,9 +1807,21 @@ "cookies.consent.purpose.sharing": "Sharing", + "cookies.consent.app.title.plumX": "PlumX", - "curation-task.task.citationpage.label": "Generate Citation Page", + "cookies.consent.app.description.plumX": "PlumX Metrics provide insights into the ways people interact with individual pieces of research output (articles, conference proceedings, book chapters, and many more) in the online environment. (https://plumanalytics.com/)", + + "cookies.consent.app.title.altmetric": "Altmetric", + + "cookies.consent.app.description.altmetric": "Altmetric’s interface tracks online engagement to reveal how and where your research is being visualized. (https://www.altmetric.com)", + + "cookies.consent.app.title.dimensions": "Dimensions", + "cookies.consent.app.description.dimensions": "Dimensions analyses references from publications and calculates a set of article-level indicators. (https://www.dimensions.ai)", + + "cookies.consent.purpose.thirdPartiesJs": "Third parties JavaScript", + + "curation-task.task.citationpage.label": "Generate Citation Page", "cris-layout.toggle.open": "Open section", From 7a5510997b2f7305444f14f00b906d03ea3f29ed Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Wed, 31 Jan 2024 15:23:43 +0100 Subject: [PATCH 13/25] add metrics config --- .../shared/cookies/browser-klaro.service.ts | 23 ++++++++++++++++--- src/app/shared/cookies/klaro-configuration.ts | 15 ------------ src/config/app-config.interface.ts | 2 ++ src/config/default-app-config.ts | 17 ++++++++++++++ src/environments/environment.test.ts | 17 ++++++++++++++ 5 files changed, 56 insertions(+), 18 deletions(-) diff --git a/src/app/shared/cookies/browser-klaro.service.ts b/src/app/shared/cookies/browser-klaro.service.ts index 73829622818..c7fbc9f5b18 100644 --- a/src/app/shared/cookies/browser-klaro.service.ts +++ b/src/app/shared/cookies/browser-klaro.service.ts @@ -19,9 +19,12 @@ import { CAPTCHA_NAME } from '../../core/google-recaptcha/google-recaptcha.servi import isEqual from 'lodash/isEqual'; export interface CookieConsents { - acknowledgement: boolean; - authentication: boolean; - preferences: boolean; + [key: string]: boolean; +} + +export interface ThirdPartyMetric { + key: string; + enabled: boolean } /** * Metadata field to store a user's cookie consent preferences in @@ -108,6 +111,20 @@ export class BrowserKlaroService extends KlaroService { this.klaroConfig.translations.zz.consentNotice.description = 'cookies.consent.content-notice.description.no-privacy'; } + if(environment.metricsConsents) { + environment.metricsConsents.forEach((metric) => { + if(metric.enabled) { + this.klaroConfig.services.push( + { + name: metric.key, + purposes: ['thirdPartiesJs'], + required: false, + } + ) + } + }) + } + const hideGoogleAnalytics$ = this.configService.findByPropertyName(this.GOOGLE_ANALYTICS_KEY).pipe( getFirstCompletedRemoteData(), map(remoteData => !remoteData.hasSucceeded || !remoteData.payload || isEmpty(remoteData.payload.values)), diff --git a/src/app/shared/cookies/klaro-configuration.ts b/src/app/shared/cookies/klaro-configuration.ts index a1954d5ad90..c818ddc19cb 100644 --- a/src/app/shared/cookies/klaro-configuration.ts +++ b/src/app/shared/cookies/klaro-configuration.ts @@ -135,21 +135,6 @@ export const klaroConfiguration: any = { HAS_AGREED_END_USER ] }, - { - name: 'plumX', - purposes: ['thirdPartiesJs'], - required: false, - }, - { - name: 'altmetric', - purposes: ['thirdPartiesJs'], - required: false, - }, - { - name: 'dimensions', - purposes: ['thirdPartiesJs'], - required: false, - }, { name: GOOGLE_ANALYTICS_KLARO_KEY, purposes: ['statistical'], diff --git a/src/config/app-config.interface.ts b/src/config/app-config.interface.ts index f95a82dd399..26ee6994795 100644 --- a/src/config/app-config.interface.ts +++ b/src/config/app-config.interface.ts @@ -31,6 +31,7 @@ import { FollowAuthorityMetadata } from './search-follow-metadata.interface'; import { AdvancedAttachmentRenderingConfig } from './advanced-attachment-rendering.config'; import { AttachmentRenderingConfig } from './attachment-rendering.config'; import { SearchResultConfig } from './search-result-config.interface'; +import { CookieConsents, ThirdPartyMetric } from "../app/shared/cookies/browser-klaro.service"; interface AppConfig extends Config { ui: UIServerConfig; @@ -68,6 +69,7 @@ interface AppConfig extends Config { attachmentRendering: AttachmentRenderingConfig; advancedAttachmentRendering: AdvancedAttachmentRenderingConfig; searchResult: SearchResultConfig; + metricsConsents: ThirdPartyMetric[]; } /** diff --git a/src/config/default-app-config.ts b/src/config/default-app-config.ts index 55b4cdd2f01..15a11acf34f 100644 --- a/src/config/default-app-config.ts +++ b/src/config/default-app-config.ts @@ -34,6 +34,7 @@ import { } from './advanced-attachment-rendering.config'; import { AttachmentRenderingConfig } from './attachment-rendering.config'; import { SearchResultConfig } from './search-result-config.interface'; +import { CookieConsents, ThirdPartyMetric } from "../app/shared/cookies/browser-klaro.service"; export class DefaultAppConfig implements AppConfig { production = false; @@ -735,4 +736,20 @@ export class DefaultAppConfig implements AppConfig { additionalMetadataFields: [], authorMetadata: ['dc.contributor.author', 'dc.creator', 'dc.contributor.*'], }; + + //Configuration for third-party metrics in Klaro + metricsConsents: ThirdPartyMetric[] = [ + { + key: 'plumX', + enabled: true + }, + { + key: 'altmetric', + enabled: true + }, + { + key: 'dimensions', + enabled: true + }, + ]; } diff --git a/src/environments/environment.test.ts b/src/environments/environment.test.ts index 7e5f5c280f6..5f45b86c52a 100644 --- a/src/environments/environment.test.ts +++ b/src/environments/environment.test.ts @@ -3,6 +3,7 @@ import { BuildConfig } from 'src/config/build-config.interface'; import { RestRequestMethod } from '../app/core/data/rest-request-method'; import { NotificationAnimationsType } from '../app/shared/notifications/models/notification-animations-type'; import { AdvancedAttachmentElementType } from '../config/advanced-attachment-rendering.config'; +import { ThirdPartyMetric } from "../app/shared/cookies/browser-klaro.service"; export const environment: BuildConfig = { production: false, @@ -553,4 +554,20 @@ export const environment: BuildConfig = { ], authorMetadata: ['dc.contributor.author', 'dc.contributor.editor', 'dc.contributor.contributor', 'dc.creator'], }, + + //Configuration for third-party metrics in Klaro + metricsConsents: [ + { + key: 'plumX', + enabled: true + }, + { + key: 'altmetric', + enabled: true + }, + { + key: 'dimensions', + enabled: true + }, + ], }; From 63d65cbe23d94bcd835a8958a9ace11119fbeea8 Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Wed, 31 Jan 2024 15:57:41 +0100 Subject: [PATCH 14/25] fix lint --- src/app/shared/cookies/browser-klaro.service.ts | 8 ++++---- src/config/app-config.interface.ts | 2 +- src/config/default-app-config.ts | 2 +- src/environments/environment.test.ts | 1 - 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/app/shared/cookies/browser-klaro.service.ts b/src/app/shared/cookies/browser-klaro.service.ts index c7fbc9f5b18..1f1fb058992 100644 --- a/src/app/shared/cookies/browser-klaro.service.ts +++ b/src/app/shared/cookies/browser-klaro.service.ts @@ -111,18 +111,18 @@ export class BrowserKlaroService extends KlaroService { this.klaroConfig.translations.zz.consentNotice.description = 'cookies.consent.content-notice.description.no-privacy'; } - if(environment.metricsConsents) { + if (environment.metricsConsents) { environment.metricsConsents.forEach((metric) => { - if(metric.enabled) { + if (metric.enabled) { this.klaroConfig.services.push( { name: metric.key, purposes: ['thirdPartiesJs'], required: false, } - ) + ); } - }) + }); } const hideGoogleAnalytics$ = this.configService.findByPropertyName(this.GOOGLE_ANALYTICS_KEY).pipe( diff --git a/src/config/app-config.interface.ts b/src/config/app-config.interface.ts index 26ee6994795..9c0294dc768 100644 --- a/src/config/app-config.interface.ts +++ b/src/config/app-config.interface.ts @@ -31,7 +31,7 @@ import { FollowAuthorityMetadata } from './search-follow-metadata.interface'; import { AdvancedAttachmentRenderingConfig } from './advanced-attachment-rendering.config'; import { AttachmentRenderingConfig } from './attachment-rendering.config'; import { SearchResultConfig } from './search-result-config.interface'; -import { CookieConsents, ThirdPartyMetric } from "../app/shared/cookies/browser-klaro.service"; +import { ThirdPartyMetric } from '../app/shared/cookies/browser-klaro.service'; interface AppConfig extends Config { ui: UIServerConfig; diff --git a/src/config/default-app-config.ts b/src/config/default-app-config.ts index 15a11acf34f..202cf352a73 100644 --- a/src/config/default-app-config.ts +++ b/src/config/default-app-config.ts @@ -34,7 +34,7 @@ import { } from './advanced-attachment-rendering.config'; import { AttachmentRenderingConfig } from './attachment-rendering.config'; import { SearchResultConfig } from './search-result-config.interface'; -import { CookieConsents, ThirdPartyMetric } from "../app/shared/cookies/browser-klaro.service"; +import { ThirdPartyMetric } from '../app/shared/cookies/browser-klaro.service'; export class DefaultAppConfig implements AppConfig { production = false; diff --git a/src/environments/environment.test.ts b/src/environments/environment.test.ts index 5f45b86c52a..055ffc17cfe 100644 --- a/src/environments/environment.test.ts +++ b/src/environments/environment.test.ts @@ -3,7 +3,6 @@ import { BuildConfig } from 'src/config/build-config.interface'; import { RestRequestMethod } from '../app/core/data/rest-request-method'; import { NotificationAnimationsType } from '../app/shared/notifications/models/notification-animations-type'; import { AdvancedAttachmentElementType } from '../config/advanced-attachment-rendering.config'; -import { ThirdPartyMetric } from "../app/shared/cookies/browser-klaro.service"; export const environment: BuildConfig = { production: false, From 96cad9528429f5396a6755bc98a65af3e43bf43c Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Wed, 31 Jan 2024 16:50:31 +0100 Subject: [PATCH 15/25] finalize config --- src/app/shared/cookies/browser-klaro.service.ts | 9 ++------- .../metric-altmetric/metric-altmetric.component.html | 2 +- .../metric-dimensions/metric-dimensions.component.html | 2 +- .../metric/metric-plumx/metric-plumx.component.html | 2 +- src/assets/i18n/en.json5 | 4 ++-- src/config/app-config.interface.ts | 2 +- src/config/default-app-config.ts | 2 +- src/config/third-party-metric-config.ts | 4 ++++ 8 files changed, 13 insertions(+), 14 deletions(-) create mode 100644 src/config/third-party-metric-config.ts diff --git a/src/app/shared/cookies/browser-klaro.service.ts b/src/app/shared/cookies/browser-klaro.service.ts index 1f1fb058992..846ab15ea2e 100644 --- a/src/app/shared/cookies/browser-klaro.service.ts +++ b/src/app/shared/cookies/browser-klaro.service.ts @@ -21,11 +21,6 @@ import isEqual from 'lodash/isEqual'; export interface CookieConsents { [key: string]: boolean; } - -export interface ThirdPartyMetric { - key: string; - enabled: boolean -} /** * Metadata field to store a user's cookie consent preferences in */ @@ -111,13 +106,13 @@ export class BrowserKlaroService extends KlaroService { this.klaroConfig.translations.zz.consentNotice.description = 'cookies.consent.content-notice.description.no-privacy'; } - if (environment.metricsConsents) { + if (hasValue(environment.metricsConsents)) { environment.metricsConsents.forEach((metric) => { if (metric.enabled) { this.klaroConfig.services.push( { name: metric.key, - purposes: ['thirdPartiesJs'], + purposes: ['thirdPartyJs'], required: false, } ); diff --git a/src/app/shared/metric/metric-altmetric/metric-altmetric.component.html b/src/app/shared/metric/metric-altmetric/metric-altmetric.component.html index 52bd77d7cd5..3976c79b876 100644 --- a/src/app/shared/metric/metric-altmetric/metric-altmetric.component.html +++ b/src/app/shared/metric/metric-altmetric/metric-altmetric.component.html @@ -25,7 +25,7 @@
-
+
{{ "third-party-metrics-cookies.message" | translate: {metricType: metric.metricType | titlecase} }} diff --git a/src/app/shared/metric/metric-dimensions/metric-dimensions.component.html b/src/app/shared/metric/metric-dimensions/metric-dimensions.component.html index a3ad46dec12..a5d9205ce93 100644 --- a/src/app/shared/metric/metric-dimensions/metric-dimensions.component.html +++ b/src/app/shared/metric/metric-dimensions/metric-dimensions.component.html @@ -25,7 +25,7 @@
-
+
{{ "third-party-metrics-cookies.message" | translate: {metricType: metric.metricType | titlecase} }} diff --git a/src/app/shared/metric/metric-plumx/metric-plumx.component.html b/src/app/shared/metric/metric-plumx/metric-plumx.component.html index 052c2bdf901..9fc6c34f0c3 100644 --- a/src/app/shared/metric/metric-plumx/metric-plumx.component.html +++ b/src/app/shared/metric/metric-plumx/metric-plumx.component.html @@ -31,7 +31,7 @@
-
+
{{ "third-party-metrics-cookies.message" | translate: {metricType: metric.metricType | titlecase} }} diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index b484a3a7a41..f008bdbfde4 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -1757,7 +1757,7 @@ "cookies.consent.ok": "That's ok", - "cookies.consent.save": "Save", + "cookies.consent.save": "Close", "cookies.consent.content-notice.title": "Cookie Consent", @@ -1819,7 +1819,7 @@ "cookies.consent.app.description.dimensions": "Dimensions analyses references from publications and calculates a set of article-level indicators. (https://www.dimensions.ai)", - "cookies.consent.purpose.thirdPartiesJs": "Third parties JavaScript", + "cookies.consent.purpose.thirdPartyJs": "Third-party JavaScript", "curation-task.task.citationpage.label": "Generate Citation Page", diff --git a/src/config/app-config.interface.ts b/src/config/app-config.interface.ts index 9c0294dc768..677bf6eff02 100644 --- a/src/config/app-config.interface.ts +++ b/src/config/app-config.interface.ts @@ -31,7 +31,7 @@ import { FollowAuthorityMetadata } from './search-follow-metadata.interface'; import { AdvancedAttachmentRenderingConfig } from './advanced-attachment-rendering.config'; import { AttachmentRenderingConfig } from './attachment-rendering.config'; import { SearchResultConfig } from './search-result-config.interface'; -import { ThirdPartyMetric } from '../app/shared/cookies/browser-klaro.service'; +import { ThirdPartyMetric } from "./third-party-metric-config"; interface AppConfig extends Config { ui: UIServerConfig; diff --git a/src/config/default-app-config.ts b/src/config/default-app-config.ts index 202cf352a73..c000a9bc06c 100644 --- a/src/config/default-app-config.ts +++ b/src/config/default-app-config.ts @@ -34,7 +34,7 @@ import { } from './advanced-attachment-rendering.config'; import { AttachmentRenderingConfig } from './attachment-rendering.config'; import { SearchResultConfig } from './search-result-config.interface'; -import { ThirdPartyMetric } from '../app/shared/cookies/browser-klaro.service'; +import { ThirdPartyMetric } from "./third-party-metric-config"; export class DefaultAppConfig implements AppConfig { production = false; diff --git a/src/config/third-party-metric-config.ts b/src/config/third-party-metric-config.ts new file mode 100644 index 00000000000..fdc0fa175fb --- /dev/null +++ b/src/config/third-party-metric-config.ts @@ -0,0 +1,4 @@ +export interface ThirdPartyMetric { + key: string; + enabled: boolean +} From e09343161e41eb90a002aac0cfcf139b0d9b110d Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Thu, 1 Feb 2024 10:55:20 +0100 Subject: [PATCH 16/25] fix lint on import --- src/config/app-config.interface.ts | 2 +- src/config/default-app-config.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/config/app-config.interface.ts b/src/config/app-config.interface.ts index 677bf6eff02..88875f89479 100644 --- a/src/config/app-config.interface.ts +++ b/src/config/app-config.interface.ts @@ -31,7 +31,7 @@ import { FollowAuthorityMetadata } from './search-follow-metadata.interface'; import { AdvancedAttachmentRenderingConfig } from './advanced-attachment-rendering.config'; import { AttachmentRenderingConfig } from './attachment-rendering.config'; import { SearchResultConfig } from './search-result-config.interface'; -import { ThirdPartyMetric } from "./third-party-metric-config"; +import { ThirdPartyMetric } from './third-party-metric-config'; interface AppConfig extends Config { ui: UIServerConfig; diff --git a/src/config/default-app-config.ts b/src/config/default-app-config.ts index c000a9bc06c..cfe8fd3fa70 100644 --- a/src/config/default-app-config.ts +++ b/src/config/default-app-config.ts @@ -34,7 +34,7 @@ import { } from './advanced-attachment-rendering.config'; import { AttachmentRenderingConfig } from './attachment-rendering.config'; import { SearchResultConfig } from './search-result-config.interface'; -import { ThirdPartyMetric } from "./third-party-metric-config"; +import { ThirdPartyMetric } from './third-party-metric-config'; export class DefaultAppConfig implements AppConfig { production = false; From c19ecb9479c0a43a643d0732716db50cada970d5 Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Thu, 1 Feb 2024 15:58:39 +0100 Subject: [PATCH 17/25] DSC-1522: implement unified text for blocked third-party metrics --- .../thumbnail/thumbnail.component.spec.ts | 12 ++-- .../shared/cookies/browser-klaro.service.ts | 2 + .../metric-loader/metric-loader.component.ts | 3 +- ...-search-result-list-element.component.html | 4 ++ ...arch-result-list-element.component.spec.ts | 4 +- ...em-search-result-list-element.component.ts | 59 ++++++++++++++++++- src/assets/i18n/en.json5 | 2 + 7 files changed, 76 insertions(+), 10 deletions(-) diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/thumbnail/thumbnail.component.spec.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/thumbnail/thumbnail.component.spec.ts index 9ca63334666..5aab0e0dbc5 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/thumbnail/thumbnail.component.spec.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/thumbnail/thumbnail.component.spec.ts @@ -152,7 +152,7 @@ describe('ThumbnailComponent', () => { })); it('should show default thumbnail', () => { - expect(component.default).toBe('assets/images/person-placeholder.svg'); + expect(component.default).toBe('assets/images/file-placeholder.svg'); }); }); @@ -172,7 +172,7 @@ describe('ThumbnailComponent', () => { })); it('should show default thumbnail', () => { - expect(component.default).toBe('assets/images/person-placeholder.svg'); + expect(component.default).toBe('assets/images/file-placeholder.svg'); }); }); @@ -239,7 +239,7 @@ describe('ThumbnailComponent', () => { }); it('should show default thumbnail', () => { - expect(component.default).toBe('assets/images/person-placeholder.svg'); + expect(component.default).toBe('assets/images/file-placeholder.svg'); }); }); @@ -254,7 +254,7 @@ describe('ThumbnailComponent', () => { }); it('should not show bitstream content image src but the default image', () => { - expect(component.default).toBe('assets/images/person-placeholder.svg'); + expect(component.default).toBe('assets/images/file-placeholder.svg'); }); }); @@ -269,7 +269,7 @@ describe('ThumbnailComponent', () => { }); it('should not show thumbnail content image src but the default image', () => { - expect(component.default).toBe('assets/images/person-placeholder.svg'); + expect(component.default).toBe('assets/images/file-placeholder.svg'); }); }); @@ -284,7 +284,7 @@ describe('ThumbnailComponent', () => { }); it('should not show thumbnail content image src but the default image', () => { - expect(component.default).toBe('assets/images/person-placeholder.svg'); + expect(component.default).toBe('assets/images/file-placeholder.svg'); }); }); diff --git a/src/app/shared/cookies/browser-klaro.service.ts b/src/app/shared/cookies/browser-klaro.service.ts index 846ab15ea2e..a772eec989d 100644 --- a/src/app/shared/cookies/browser-klaro.service.ts +++ b/src/app/shared/cookies/browser-klaro.service.ts @@ -362,6 +362,8 @@ export class BrowserKlaroService extends KlaroService { const manager = getManager(this.klaroConfig); const consentsSubject$ = this.consentsUpdates$; let lastCookiesConsents = this.lastCookiesConsents; + + consentsSubject$.next(manager.consents); manager.watch({ update(_, eventName, consents) { diff --git a/src/app/shared/metric/metric-loader/metric-loader.component.ts b/src/app/shared/metric/metric-loader/metric-loader.component.ts index 2dabf31baf1..a4290a7f049 100644 --- a/src/app/shared/metric/metric-loader/metric-loader.component.ts +++ b/src/app/shared/metric/metric-loader/metric-loader.component.ts @@ -17,6 +17,7 @@ import { hasValue } from '../../empty.util'; import { BrowserKlaroService, CookieConsents } from '../../cookies/browser-klaro.service'; import { KlaroService } from '../../cookies/klaro.service'; import { startWith } from 'rxjs/operators'; +import { environment } from '../../../../environments/environment'; @Component({ // eslint-disable-next-line @angular-eslint/component-selector @@ -48,7 +49,7 @@ export class MetricLoaderComponent implements OnInit, OnDestroy { settingsSubscription: Subscription; - private thirdPartyMetrics = ['plumX', 'altmetric', 'dimensions']; + private thirdPartyMetrics = environment.metricsConsents.map(metrics => metrics.key); private browserKlaroService: BrowserKlaroService; diff --git a/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.html b/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.html index 25206b8c277..14bb8603451 100644 --- a/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.html +++ b/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.html @@ -13,6 +13,10 @@
+ + {{ "third-party-metrics-blocked" | translate }}  + +
diff --git a/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.spec.ts b/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.spec.ts index 75f4759fa54..35654240a5d 100644 --- a/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.spec.ts +++ b/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.spec.ts @@ -11,6 +11,7 @@ import { DSONameService } from '../../../../../../core/breadcrumbs/dso-name.serv import { DSONameServiceMock, UNDEFINED_NAME } from '../../../../../mocks/dso-name.service.mock'; import { VarDirective } from '../../../../../utils/var.directive'; import { APP_CONFIG } from '../../../../../../../config/app-config.interface'; +import { TranslateModule } from '@ngx-translate/core'; let publicationListElementComponent: ItemSearchResultListElementComponent; let fixture: ComponentFixture; @@ -188,13 +189,13 @@ const enviromentNoThumbs = { describe('ItemSearchResultListElementComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot()], declarations: [ItemSearchResultListElementComponent, TruncatePipe, VarDirective], providers: [ { provide: TruncatableService, useValue: {} }, { provide: DSONameService, useClass: DSONameServiceMock }, { provide: APP_CONFIG, useValue: environmentUseThumbs } ], - schemas: [NO_ERRORS_SCHEMA] }).overrideComponent(ItemSearchResultListElementComponent, { set: { changeDetection: ChangeDetectionStrategy.Default } @@ -371,6 +372,7 @@ describe('ItemSearchResultListElementComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot()], declarations: [ItemSearchResultListElementComponent, TruncatePipe], providers: [ {provide: TruncatableService, useValue: {}}, diff --git a/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.ts b/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.ts index 4c760e2f5b8..5ed41b09aaa 100644 --- a/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.ts +++ b/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { AfterViewInit, Component, Inject, Input, OnInit, Optional } from '@angular/core'; import { listableObjectComponent } from '../../../../../object-collection/shared/listable-object/listable-object.decorator'; @@ -9,6 +9,14 @@ import { Item } from '../../../../../../core/shared/item.model'; import { getItemPageRoute } from '../../../../../../item-page/item-page-routing-paths'; import { Context } from '../../../../../../core/shared/context.model'; import { environment } from '../../../../../../../environments/environment'; +import { BrowserKlaroService } from '../../../../../cookies/browser-klaro.service'; +import { KlaroService } from '../../../../../cookies/klaro.service'; +import { combineLatest, Observable } from 'rxjs'; +import { TruncatableService } from '../../../../../truncatable/truncatable.service'; +import { DSONameService } from '../../../../../../core/breadcrumbs/dso-name.service'; +import { APP_CONFIG, AppConfig } from '../../../../../../../config/app-config.interface'; +import { getFirstSucceededRemoteListPayload } from '../../../../../../core/shared/operators'; +import { map } from 'rxjs/operators'; @listableObjectComponent('PublicationSearchResult', ViewMode.ListElement) @listableObjectComponent(ItemSearchResult, ViewMode.ListElement) @@ -21,7 +29,7 @@ import { environment } from '../../../../../../../environments/environment'; /** * The component for displaying a list element for an item search result of the type Publication */ -export class ItemSearchResultListElementComponent extends SearchResultListElementComponent implements OnInit { +export class ItemSearchResultListElementComponent extends SearchResultListElementComponent implements OnInit, AfterViewInit { /** * Whether to show the metrics badges @@ -35,10 +43,57 @@ export class ItemSearchResultListElementComponent extends SearchResultListElemen authorMetadata = environment.searchResult.authorMetadata; + hasLoadedThirdPartyMetrics$: Observable; + + private thirdPartyMetrics = environment.metricsConsents.map(metrics => metrics.key); + + private browserKlaroService: BrowserKlaroService; + + constructor( + protected truncatableService: TruncatableService, + public dsoNameService: DSONameService, + @Inject(APP_CONFIG) protected appConfig?: AppConfig, + @Optional() private klaroService?: KlaroService, + ) { + super(truncatableService, dsoNameService); + this.browserKlaroService = (this.klaroService as BrowserKlaroService); + } + ngOnInit(): void { super.ngOnInit(); this.showThumbnails = this.showThumbnails ?? this.appConfig.browseBy.showThumbnails; this.itemPageRoute = getItemPageRoute(this.dso); } + /** + * Check if item has Third-party metrics blocked by consents + */ + ngAfterViewInit() { + if (this.showMetrics && this.browserKlaroService) { + this.browserKlaroService.watchConsentUpdates(); + + this.hasLoadedThirdPartyMetrics$ = combineLatest([ + this.browserKlaroService.consentsUpdates$, + this.dso.metrics?.pipe( + getFirstSucceededRemoteListPayload(), + map(metrics => { + return metrics.filter(metric => this.thirdPartyMetrics.includes(metric.metricType)); + }) + ) + ]).pipe( + map(([consents, metrics]) => { + return metrics.reduce((previous, current) => { + return consents[current.metricType] && previous; + }, true); + }) + ); + } + } + + /** + * Prompt user for consents settings + */ + showSettings() { + this.browserKlaroService.showSettings(); + } } diff --git a/src/assets/i18n/en.json5 b/src/assets/i18n/en.json5 index f008bdbfde4..1f5be9c6263 100644 --- a/src/assets/i18n/en.json5 +++ b/src/assets/i18n/en.json5 @@ -7308,4 +7308,6 @@ "third-party-metrics-cookies.message": "{{ metricType }} badge is blocked by your ", "third-party-metrics-cookies.consent-settings": "consent settings", + + "third-party-metrics-blocked": "Some of the metrics are blocked by your", } From 5bd3bf4a2f7b6380885d91aca9e7be6c72cc1d66 Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Fri, 2 Feb 2024 09:59:50 +0100 Subject: [PATCH 18/25] add filter for disabled metrics in config --- src/app/shared/metric/metric-loader/metric-loader.component.ts | 2 +- .../item/item-search-result-list-element.component.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/shared/metric/metric-loader/metric-loader.component.ts b/src/app/shared/metric/metric-loader/metric-loader.component.ts index a4290a7f049..b24f9f994eb 100644 --- a/src/app/shared/metric/metric-loader/metric-loader.component.ts +++ b/src/app/shared/metric/metric-loader/metric-loader.component.ts @@ -49,7 +49,7 @@ export class MetricLoaderComponent implements OnInit, OnDestroy { settingsSubscription: Subscription; - private thirdPartyMetrics = environment.metricsConsents.map(metrics => metrics.key); + private thirdPartyMetrics = environment.metricsConsents.filter(metric => metric.enabled).map(metric => metric.key); private browserKlaroService: BrowserKlaroService; diff --git a/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.ts b/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.ts index 5ed41b09aaa..53a3a52fa2f 100644 --- a/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.ts +++ b/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.ts @@ -45,7 +45,7 @@ export class ItemSearchResultListElementComponent extends SearchResultListElemen hasLoadedThirdPartyMetrics$: Observable; - private thirdPartyMetrics = environment.metricsConsents.map(metrics => metrics.key); + private thirdPartyMetrics = environment.metricsConsents.filter(metric => metric.enabled).map(metric => metric.key); private browserKlaroService: BrowserKlaroService; From c2c5e1a67d4746bc30ed52db003c0be9834af561 Mon Sep 17 00:00:00 2001 From: Francesco Pio Scognamiglio Date: Thu, 25 Jan 2024 09:25:21 +0100 Subject: [PATCH 19/25] [GLAM-637] fix discovery configuration for batch import and export --- .../export-batch-selector/export-batch-selector.component.ts | 2 +- .../import-batch-selector/import-batch-selector.component.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/shared/dso-selector/modal-wrappers/export-batch-selector/export-batch-selector.component.ts b/src/app/shared/dso-selector/modal-wrappers/export-batch-selector/export-batch-selector.component.ts index 47c38b6f748..9c6320a38b8 100644 --- a/src/app/shared/dso-selector/modal-wrappers/export-batch-selector/export-batch-selector.component.ts +++ b/src/app/shared/dso-selector/modal-wrappers/export-batch-selector/export-batch-selector.component.ts @@ -30,7 +30,7 @@ import { FeatureID } from '../../../../core/data/feature-authorization/feature-i templateUrl: '../dso-selector-modal-wrapper.component.html', }) export class ExportBatchSelectorComponent extends DSOSelectorModalWrapperComponent implements OnInit { - configuration = 'backend'; + configuration = 'communityOrCollection'; objectType = DSpaceObjectType.DSPACEOBJECT; selectorTypes = [DSpaceObjectType.COLLECTION]; action = SelectorActionType.EXPORT_BATCH; diff --git a/src/app/shared/dso-selector/modal-wrappers/import-batch-selector/import-batch-selector.component.ts b/src/app/shared/dso-selector/modal-wrappers/import-batch-selector/import-batch-selector.component.ts index 4d0e2926c47..42eb8e9325f 100644 --- a/src/app/shared/dso-selector/modal-wrappers/import-batch-selector/import-batch-selector.component.ts +++ b/src/app/shared/dso-selector/modal-wrappers/import-batch-selector/import-batch-selector.component.ts @@ -16,7 +16,7 @@ import { Observable, of } from 'rxjs'; templateUrl: '../dso-selector-modal-wrapper.component.html', }) export class ImportBatchSelectorComponent extends DSOSelectorModalWrapperComponent implements OnInit { - configuration = 'backend'; + configuration = 'communityOrCollection'; objectType = DSpaceObjectType.DSPACEOBJECT; selectorTypes = [DSpaceObjectType.COLLECTION]; action = SelectorActionType.IMPORT_BATCH; From ec91c1ed0b48a37e38903f06224d7abfd4d4419a Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Wed, 7 Feb 2024 09:07:52 +0100 Subject: [PATCH 20/25] DSC-1446 fix tests, add missing imports --- src/app/core/eperson/group-data.service.ts | 1 + .../thumbnail/thumbnail.component.spec.ts | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/app/core/eperson/group-data.service.ts b/src/app/core/eperson/group-data.service.ts index 02a68a8513e..c4859892c82 100644 --- a/src/app/core/eperson/group-data.service.ts +++ b/src/app/core/eperson/group-data.service.ts @@ -41,6 +41,7 @@ import { Operation } from 'fast-json-patch'; import { RestRequestMethod } from '../data/rest-request-method'; import { dataService } from '../data/base/data-service.decorator'; import { getGroupEditRoute } from '../../access-control/access-control-routing-paths'; +import { isNotEmpty } from '../../shared/empty.util'; const groupRegistryStateSelector = (state: AppState) => state.groupRegistry; const editGroupSelector = createSelector(groupRegistryStateSelector, (groupRegistryState: GroupRegistryState) => groupRegistryState.editGroup); diff --git a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/thumbnail/thumbnail.component.spec.ts b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/thumbnail/thumbnail.component.spec.ts index 9ca63334666..5aab0e0dbc5 100644 --- a/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/thumbnail/thumbnail.component.spec.ts +++ b/src/app/cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metadata/rendering-types/thumbnail/thumbnail.component.spec.ts @@ -152,7 +152,7 @@ describe('ThumbnailComponent', () => { })); it('should show default thumbnail', () => { - expect(component.default).toBe('assets/images/person-placeholder.svg'); + expect(component.default).toBe('assets/images/file-placeholder.svg'); }); }); @@ -172,7 +172,7 @@ describe('ThumbnailComponent', () => { })); it('should show default thumbnail', () => { - expect(component.default).toBe('assets/images/person-placeholder.svg'); + expect(component.default).toBe('assets/images/file-placeholder.svg'); }); }); @@ -239,7 +239,7 @@ describe('ThumbnailComponent', () => { }); it('should show default thumbnail', () => { - expect(component.default).toBe('assets/images/person-placeholder.svg'); + expect(component.default).toBe('assets/images/file-placeholder.svg'); }); }); @@ -254,7 +254,7 @@ describe('ThumbnailComponent', () => { }); it('should not show bitstream content image src but the default image', () => { - expect(component.default).toBe('assets/images/person-placeholder.svg'); + expect(component.default).toBe('assets/images/file-placeholder.svg'); }); }); @@ -269,7 +269,7 @@ describe('ThumbnailComponent', () => { }); it('should not show thumbnail content image src but the default image', () => { - expect(component.default).toBe('assets/images/person-placeholder.svg'); + expect(component.default).toBe('assets/images/file-placeholder.svg'); }); }); @@ -284,7 +284,7 @@ describe('ThumbnailComponent', () => { }); it('should not show thumbnail content image src but the default image', () => { - expect(component.default).toBe('assets/images/person-placeholder.svg'); + expect(component.default).toBe('assets/images/file-placeholder.svg'); }); }); From 13da7d8f092d7d52be49a85d1da8669c3a765baa Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Wed, 7 Feb 2024 14:02:36 +0100 Subject: [PATCH 21/25] DSC-1413 refactor --- .../shared/cookies/browser-klaro.service.ts | 6 ++-- src/app/shared/cookies/klaro.service.ts | 17 ++++++++-- .../metric-loader/metric-loader.component.ts | 21 +++++++----- src/app/shared/metric/metrics.module.ts | 9 ----- src/config/app-config.interface.ts | 2 -- src/config/default-app-config.ts | 34 +++++++++---------- src/config/info-config.interface.ts | 2 ++ src/environments/environment.test.ts | 31 ++++++++--------- 8 files changed, 62 insertions(+), 60 deletions(-) diff --git a/src/app/shared/cookies/browser-klaro.service.ts b/src/app/shared/cookies/browser-klaro.service.ts index 846ab15ea2e..75bac2005b8 100644 --- a/src/app/shared/cookies/browser-klaro.service.ts +++ b/src/app/shared/cookies/browser-klaro.service.ts @@ -106,8 +106,8 @@ export class BrowserKlaroService extends KlaroService { this.klaroConfig.translations.zz.consentNotice.description = 'cookies.consent.content-notice.description.no-privacy'; } - if (hasValue(environment.metricsConsents)) { - environment.metricsConsents.forEach((metric) => { + if (hasValue(environment.info.metricsConsents)) { + environment.info.metricsConsents.forEach((metric) => { if (metric.enabled) { this.klaroConfig.services.push( { @@ -357,7 +357,7 @@ export class BrowserKlaroService extends KlaroService { return 'klaro-' + identifier; } - watchConsentUpdates() { + watchConsentUpdates(): void { this.lazyKlaro.then(({getManager}) => { const manager = getManager(this.klaroConfig); const consentsSubject$ = this.consentsUpdates$; diff --git a/src/app/shared/cookies/klaro.service.ts b/src/app/shared/cookies/klaro.service.ts index d54fed8b30a..e365b95af5c 100644 --- a/src/app/shared/cookies/klaro.service.ts +++ b/src/app/shared/cookies/klaro.service.ts @@ -1,6 +1,7 @@ import { Injectable } from '@angular/core'; -import { Observable } from 'rxjs'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { CookieConsents } from './browser-klaro.service'; /** * Abstract class representing a service for handling Klaro consent preferences and UI @@ -10,15 +11,25 @@ export abstract class KlaroService { /** * Initializes the service */ - abstract initialize(); + abstract initialize(): void; /** * Shows a dialog with the current consent preferences */ - abstract showSettings(); + abstract showSettings(): void; /** * Return saved preferences stored in the klaro cookie */ abstract getSavedPreferences(): Observable; + + /** + * Watch for changes in consents + */ + abstract watchConsentUpdates(): void; + + /** + * Subject to emit updates in the consents + */ + abstract consentsUpdates$: BehaviorSubject; } diff --git a/src/app/shared/metric/metric-loader/metric-loader.component.ts b/src/app/shared/metric/metric-loader/metric-loader.component.ts index 2dabf31baf1..8d162732f30 100644 --- a/src/app/shared/metric/metric-loader/metric-loader.component.ts +++ b/src/app/shared/metric/metric-loader/metric-loader.component.ts @@ -14,9 +14,9 @@ import { Metric } from '../../../core/shared/metric.model'; import { BaseMetricComponent } from './base-metric.component'; import { MetricLoaderService } from './metric-loader.service'; import { hasValue } from '../../empty.util'; -import { BrowserKlaroService, CookieConsents } from '../../cookies/browser-klaro.service'; +import { CookieConsents } from '../../cookies/browser-klaro.service'; import { KlaroService } from '../../cookies/klaro.service'; -import { startWith } from 'rxjs/operators'; +import { startWith } from 'rxjs/operators'; @Component({ // eslint-disable-next-line @angular-eslint/component-selector @@ -50,8 +50,6 @@ export class MetricLoaderComponent implements OnInit, OnDestroy { private thirdPartyMetrics = ['plumX', 'altmetric', 'dimensions']; - private browserKlaroService: BrowserKlaroService; - private hasLoadedScript: boolean; constructor( @@ -59,13 +57,12 @@ export class MetricLoaderComponent implements OnInit, OnDestroy { private metricLoaderService: MetricLoaderService, private klaroService: KlaroService, ) { - this.browserKlaroService = (this.klaroService as BrowserKlaroService); - this.browserKlaroService.watchConsentUpdates(); - this.consentUpdates$ = this.browserKlaroService.consentsUpdates$; + this.klaroService.watchConsentUpdates(); + this.consentUpdates$ = this.klaroService.consentsUpdates$; } ngOnInit() { - this.cookiesSubscription = this.browserKlaroService.getSavedPreferences().subscribe((consents) => { + this.cookiesSubscription = this.klaroService.getSavedPreferences().subscribe((consents) => { this.loadComponent(this.metric, this.getCanLoadScript(consents)); }); } @@ -96,6 +93,8 @@ export class MetricLoaderComponent implements OnInit, OnDestroy { instantiateComponent(component: any, metric: Metric, canLoadScript: boolean, forceRendering?: boolean) { const factory = this.componentFactoryResolver.resolveComponentFactory(component); this.componentType = component; + this.container.clear(); + const ref = this.container.createComponent(factory); const componentInstance = ref.instance as BaseMetricComponent; componentInstance.metric = metric; @@ -133,6 +132,10 @@ export class MetricLoaderComponent implements OnInit, OnDestroy { * @private */ private reloadComponentOnConsentsChange(componentInstance: BaseMetricComponent, canLoadScript: boolean): void { + if (hasValue(this.settingsSubscription)) { + return; + } + this.settingsSubscription = combineLatest([ this.consentUpdates$, componentInstance.requestSettingsConsent.pipe(startWith(undefined)) @@ -140,7 +143,7 @@ export class MetricLoaderComponent implements OnInit, OnDestroy { canLoadScript = this.getCanLoadScript(consents); if (request && !canLoadScript) { - this.browserKlaroService.showSettings(); + this.klaroService.showSettings(); } if (canLoadScript && !this.hasLoadedScript) { diff --git a/src/app/shared/metric/metrics.module.ts b/src/app/shared/metric/metrics.module.ts index 93bf8686f59..972a0255f0a 100644 --- a/src/app/shared/metric/metrics.module.ts +++ b/src/app/shared/metric/metrics.module.ts @@ -19,7 +19,6 @@ import { MetricBadgesComponent } from '../object-list/metric-badges/metric-badge import { MetricDonutsComponent } from '../object-list/metric-donuts/metric-donuts.component'; import { FormsModule } from '@angular/forms'; import { DirectivesModule } from '../../directives/directives.module'; -import { BrowserKlaroService } from '../cookies/browser-klaro.service'; const PIPES = [ MetricStyleConfigPipe, @@ -45,11 +44,6 @@ const COMPONENTS = [ MetricLoaderComponent, MetricDefaultComponent ]; - -const PROVIDERS = [ - BrowserKlaroService -]; - @NgModule({ declarations: [ ...PIPES, @@ -62,9 +56,6 @@ const PROVIDERS = [ useDefaultLang: true }), ], - providers: [ - ...PROVIDERS - ], exports: [ ...COMPONENTS, ] diff --git a/src/config/app-config.interface.ts b/src/config/app-config.interface.ts index 88875f89479..f95a82dd399 100644 --- a/src/config/app-config.interface.ts +++ b/src/config/app-config.interface.ts @@ -31,7 +31,6 @@ import { FollowAuthorityMetadata } from './search-follow-metadata.interface'; import { AdvancedAttachmentRenderingConfig } from './advanced-attachment-rendering.config'; import { AttachmentRenderingConfig } from './attachment-rendering.config'; import { SearchResultConfig } from './search-result-config.interface'; -import { ThirdPartyMetric } from './third-party-metric-config'; interface AppConfig extends Config { ui: UIServerConfig; @@ -69,7 +68,6 @@ interface AppConfig extends Config { attachmentRendering: AttachmentRenderingConfig; advancedAttachmentRendering: AdvancedAttachmentRenderingConfig; searchResult: SearchResultConfig; - metricsConsents: ThirdPartyMetric[]; } /** diff --git a/src/config/default-app-config.ts b/src/config/default-app-config.ts index cfe8fd3fa70..d8d8b7bb42b 100644 --- a/src/config/default-app-config.ts +++ b/src/config/default-app-config.ts @@ -34,7 +34,6 @@ import { } from './advanced-attachment-rendering.config'; import { AttachmentRenderingConfig } from './attachment-rendering.config'; import { SearchResultConfig } from './search-result-config.interface'; -import { ThirdPartyMetric } from './third-party-metric-config'; export class DefaultAppConfig implements AppConfig { production = false; @@ -481,7 +480,22 @@ export class DefaultAppConfig implements AppConfig { // - All mentions of the privacy policy being removed from the UI (e.g. in the footer) info: InfoConfig = { enableEndUserAgreement: true, - enablePrivacyStatement: true + enablePrivacyStatement: true, + //Configuration for third-party metrics in Klaro + metricsConsents: [ + { + key: 'plumX', + enabled: true + }, + { + key: 'altmetric', + enabled: true + }, + { + key: 'dimensions', + enabled: true + }, + ] }; // Whether to enable Markdown (https://commonmark.org/) and MathJax (https://www.mathjax.org/) @@ -736,20 +750,4 @@ export class DefaultAppConfig implements AppConfig { additionalMetadataFields: [], authorMetadata: ['dc.contributor.author', 'dc.creator', 'dc.contributor.*'], }; - - //Configuration for third-party metrics in Klaro - metricsConsents: ThirdPartyMetric[] = [ - { - key: 'plumX', - enabled: true - }, - { - key: 'altmetric', - enabled: true - }, - { - key: 'dimensions', - enabled: true - }, - ]; } diff --git a/src/config/info-config.interface.ts b/src/config/info-config.interface.ts index b1831962b5b..bec8bc709f6 100644 --- a/src/config/info-config.interface.ts +++ b/src/config/info-config.interface.ts @@ -1,6 +1,8 @@ import { Config } from './config.interface'; +import { ThirdPartyMetric } from './third-party-metric-config'; export interface InfoConfig extends Config { enableEndUserAgreement: boolean; enablePrivacyStatement: boolean; + metricsConsents: ThirdPartyMetric[]; } diff --git a/src/environments/environment.test.ts b/src/environments/environment.test.ts index 055ffc17cfe..1d932a8172b 100644 --- a/src/environments/environment.test.ts +++ b/src/environments/environment.test.ts @@ -323,6 +323,21 @@ export const environment: BuildConfig = { info: { enableEndUserAgreement: true, enablePrivacyStatement: true, + //Configuration for third-party metrics in Klaro + metricsConsents: [ + { + key: 'plumX', + enabled: true + }, + { + key: 'altmetric', + enabled: true + }, + { + key: 'dimensions', + enabled: true + }, + ] }, markdown: { enabled: false, @@ -553,20 +568,4 @@ export const environment: BuildConfig = { ], authorMetadata: ['dc.contributor.author', 'dc.contributor.editor', 'dc.contributor.contributor', 'dc.creator'], }, - - //Configuration for third-party metrics in Klaro - metricsConsents: [ - { - key: 'plumX', - enabled: true - }, - { - key: 'altmetric', - enabled: true - }, - { - key: 'dimensions', - enabled: true - }, - ], }; From 663f6fc8af9bde48f12042048dc95b6d34619b22 Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Wed, 7 Feb 2024 14:53:46 +0100 Subject: [PATCH 22/25] DSC-1413 remove unnecessary block --- .../shared/metric/metric-loader/metric-loader.component.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/app/shared/metric/metric-loader/metric-loader.component.ts b/src/app/shared/metric/metric-loader/metric-loader.component.ts index 8d162732f30..df79cf7af59 100644 --- a/src/app/shared/metric/metric-loader/metric-loader.component.ts +++ b/src/app/shared/metric/metric-loader/metric-loader.component.ts @@ -73,10 +73,6 @@ export class MetricLoaderComponent implements OnInit, OnDestroy { } this.hasLoadedScript = !!canLoadScript; this.metricLoaderService.loadMetricTypeComponent(metric.metricType, canLoadScript).then((component) => { - if (hasValue(this.cookiesSubscription) && canLoadScript) { - this.container.clear(); - this.cookiesSubscription.unsubscribe(); - } this.instantiateComponent(component, metric, canLoadScript, forceRendering); }); } From 317b1524ebd4d35b52a3c426e440a6bc6058da87 Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Wed, 7 Feb 2024 15:11:13 +0100 Subject: [PATCH 23/25] DSC-1413 fix circular dependency --- src/app/shared/cookies/browser-klaro.service.ts | 5 +---- src/app/shared/cookies/klaro.service.ts | 5 +++-- .../metric/metric-loader/metric-loader.component.spec.ts | 3 +-- .../shared/metric/metric-loader/metric-loader.component.ts | 3 +-- 4 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/app/shared/cookies/browser-klaro.service.ts b/src/app/shared/cookies/browser-klaro.service.ts index 75bac2005b8..28f21ea17fd 100644 --- a/src/app/shared/cookies/browser-klaro.service.ts +++ b/src/app/shared/cookies/browser-klaro.service.ts @@ -5,7 +5,7 @@ import { TranslateService } from '@ngx-translate/core'; import { environment } from '../../../environments/environment'; import { map, switchMap, take } from 'rxjs/operators'; import { EPerson } from '../../core/eperson/models/eperson.model'; -import { KlaroService } from './klaro.service'; +import { CookieConsents, KlaroService } from './klaro.service'; import { hasValue, isEmpty, isNotEmpty } from '../empty.util'; import { CookieService } from '../../core/services/cookie.service'; import { EPersonDataService } from '../../core/eperson/eperson-data.service'; @@ -18,9 +18,6 @@ import { ConfigurationDataService } from '../../core/data/configuration-data.ser import { CAPTCHA_NAME } from '../../core/google-recaptcha/google-recaptcha.service'; import isEqual from 'lodash/isEqual'; -export interface CookieConsents { - [key: string]: boolean; -} /** * Metadata field to store a user's cookie consent preferences in */ diff --git a/src/app/shared/cookies/klaro.service.ts b/src/app/shared/cookies/klaro.service.ts index e365b95af5c..fb34b773755 100644 --- a/src/app/shared/cookies/klaro.service.ts +++ b/src/app/shared/cookies/klaro.service.ts @@ -1,8 +1,9 @@ import { Injectable } from '@angular/core'; import { BehaviorSubject, Observable } from 'rxjs'; -import { CookieConsents } from './browser-klaro.service'; - +export interface CookieConsents { + [key: string]: boolean; +} /** * Abstract class representing a service for handling Klaro consent preferences and UI */ diff --git a/src/app/shared/metric/metric-loader/metric-loader.component.spec.ts b/src/app/shared/metric/metric-loader/metric-loader.component.spec.ts index 6742184bdc5..4cf46d71901 100644 --- a/src/app/shared/metric/metric-loader/metric-loader.component.spec.ts +++ b/src/app/shared/metric/metric-loader/metric-loader.component.spec.ts @@ -8,8 +8,7 @@ import { MetricLoaderService } from './metric-loader.service'; import { metric1Mock } from '../../../cris-layout/cris-layout-matrix/cris-layout-box-container/boxes/metrics/cris-layout-metrics-box.component.spec'; import { MetricStyleConfigPipe } from '../pipes/metric-style-config/metric-style-config.pipe'; import SpyObj = jasmine.SpyObj; -import { KlaroService } from '../../cookies/klaro.service'; -import { CookieConsents } from '../../cookies/browser-klaro.service'; +import { CookieConsents, KlaroService } from '../../cookies/klaro.service'; import { BaseMetricComponent } from './base-metric.component'; diff --git a/src/app/shared/metric/metric-loader/metric-loader.component.ts b/src/app/shared/metric/metric-loader/metric-loader.component.ts index df79cf7af59..1e85d026e74 100644 --- a/src/app/shared/metric/metric-loader/metric-loader.component.ts +++ b/src/app/shared/metric/metric-loader/metric-loader.component.ts @@ -14,8 +14,7 @@ import { Metric } from '../../../core/shared/metric.model'; import { BaseMetricComponent } from './base-metric.component'; import { MetricLoaderService } from './metric-loader.service'; import { hasValue } from '../../empty.util'; -import { CookieConsents } from '../../cookies/browser-klaro.service'; -import { KlaroService } from '../../cookies/klaro.service'; +import { CookieConsents, KlaroService } from '../../cookies/klaro.service'; import { startWith } from 'rxjs/operators'; @Component({ From 8277450013a1e6eeb9c485f727c3c71f932ecc96 Mon Sep 17 00:00:00 2001 From: FrancescoMolinaro Date: Wed, 7 Feb 2024 17:37:39 +0100 Subject: [PATCH 24/25] DSC-1522 align PR, refactor --- .../item-search-result-list-element.component.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.ts b/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.ts index 53a3a52fa2f..4af92a0bb5a 100644 --- a/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.ts +++ b/src/app/shared/object-list/search-result-list-element/item-search-result/item-types/item/item-search-result-list-element.component.ts @@ -9,7 +9,6 @@ import { Item } from '../../../../../../core/shared/item.model'; import { getItemPageRoute } from '../../../../../../item-page/item-page-routing-paths'; import { Context } from '../../../../../../core/shared/context.model'; import { environment } from '../../../../../../../environments/environment'; -import { BrowserKlaroService } from '../../../../../cookies/browser-klaro.service'; import { KlaroService } from '../../../../../cookies/klaro.service'; import { combineLatest, Observable } from 'rxjs'; import { TruncatableService } from '../../../../../truncatable/truncatable.service'; @@ -45,9 +44,8 @@ export class ItemSearchResultListElementComponent extends SearchResultListElemen hasLoadedThirdPartyMetrics$: Observable; - private thirdPartyMetrics = environment.metricsConsents.filter(metric => metric.enabled).map(metric => metric.key); + private thirdPartyMetrics = environment.info.metricsConsents.filter(metric => metric.enabled).map(metric => metric.key); - private browserKlaroService: BrowserKlaroService; constructor( protected truncatableService: TruncatableService, @@ -56,7 +54,6 @@ export class ItemSearchResultListElementComponent extends SearchResultListElemen @Optional() private klaroService?: KlaroService, ) { super(truncatableService, dsoNameService); - this.browserKlaroService = (this.klaroService as BrowserKlaroService); } ngOnInit(): void { @@ -69,11 +66,11 @@ export class ItemSearchResultListElementComponent extends SearchResultListElemen * Check if item has Third-party metrics blocked by consents */ ngAfterViewInit() { - if (this.showMetrics && this.browserKlaroService) { - this.browserKlaroService.watchConsentUpdates(); + if (this.showMetrics && this.klaroService) { + this.klaroService.watchConsentUpdates(); this.hasLoadedThirdPartyMetrics$ = combineLatest([ - this.browserKlaroService.consentsUpdates$, + this.klaroService.consentsUpdates$, this.dso.metrics?.pipe( getFirstSucceededRemoteListPayload(), map(metrics => { @@ -94,6 +91,6 @@ export class ItemSearchResultListElementComponent extends SearchResultListElemen * Prompt user for consents settings */ showSettings() { - this.browserKlaroService.showSettings(); + this.klaroService.showSettings(); } } From 6dd31538c638dfe4e69bc1e53654f03c6da62681 Mon Sep 17 00:00:00 2001 From: Daniele Ninfo Date: Thu, 8 Feb 2024 19:21:12 +0100 Subject: [PATCH 25/25] [DSC-1433] Modified the method name setSocialImageTag in setPrimaryBitstreamInBundleTag --- src/app/core/metadata/metadata.service.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/core/metadata/metadata.service.ts b/src/app/core/metadata/metadata.service.ts index 579517c6f50..9e27ce5cf00 100644 --- a/src/app/core/metadata/metadata.service.ts +++ b/src/app/core/metadata/metadata.service.ts @@ -408,7 +408,7 @@ export class MetadataService { * Add to the */ private setCitationPdfUrlTag(): void { - this.setSocialImageTag('ORIGINAL', 'citation_pdf_url'); + this.setPrimaryBitstreamInBundleTag('ORIGINAL', 'citation_pdf_url'); } /** @@ -432,7 +432,7 @@ export class MetadataService { * Add to the */ private setOpenGraphImageTag(): void { - this.setSocialImageTag('THUMBNAIL', 'og:image'); + this.setPrimaryBitstreamInBundleTag('THUMBNAIL', 'og:image'); } @@ -457,10 +457,10 @@ export class MetadataService { * Add to the */ private setTwitterImageTag(): void { - this.setSocialImageTag('THUMBNAIL', 'twitter:image'); + this.setPrimaryBitstreamInBundleTag('THUMBNAIL', 'twitter:image'); } - private setSocialImageTag(bundleName: string, tag: string): void { + private setPrimaryBitstreamInBundleTag(bundleName: string, tag: string): void { if (this.currentObject.value instanceof Item) { const item = this.currentObject.value as Item;