Skip to content

Commit

Permalink
Merged in gdpr-metrics-DSC-1522 (pull request DSpace#1326)
Browse files Browse the repository at this point in the history
Gdpr metrics DSC-1522

Approved-by: Andrea Barbasso
Approved-by: Giuseppe Digilio
  • Loading branch information
FrancescoMolinaro authored and atarix83 committed Feb 9, 2024
2 parents 359ddec + 850554f commit 0d2b3d4
Show file tree
Hide file tree
Showing 26 changed files with 440 additions and 82 deletions.
1 change: 1 addition & 0 deletions src/app/core/eperson/group-data.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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');
});

});
Expand All @@ -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');
});

});
Expand Down Expand Up @@ -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');
});

});
Expand All @@ -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');
});

});
Expand All @@ -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');
});

});
Expand All @@ -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');
});

});
Expand Down
48 changes: 45 additions & 3 deletions src/app/shared/cookies/browser-klaro.service.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
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';
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';
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';

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

private readonly GOOGLE_ANALYTICS_SERVICE_NAME = 'google-analytics';

private lastCookiesConsents: CookieConsents;

/**
* Initial Klaro configuration
*/
klaroConfig = cloneDeep(klaroConfiguration);

/**
* Subject to emit updates in the consents
*/
consentsUpdates$: BehaviorSubject<CookieConsents> = new BehaviorSubject<CookieConsents>(null);


constructor(
private translateService: TranslateService,
private authService: AuthService,
Expand All @@ -94,6 +103,20 @@ export class BrowserKlaroService extends KlaroService {
this.klaroConfig.translations.zz.consentNotice.description = 'cookies.consent.content-notice.description.no-privacy';
}

if (hasValue(environment.info.metricsConsents)) {
environment.info.metricsConsents.forEach((metric) => {
if (metric.enabled) {
this.klaroConfig.services.push(
{
name: metric.key,
purposes: ['thirdPartyJs'],
required: false,
}
);
}
});
}

const hideGoogleAnalytics$ = this.configService.findByPropertyName(this.GOOGLE_ANALYTICS_KEY).pipe(
getFirstCompletedRemoteData(),
map(remoteData => !remoteData.hasSucceeded || !remoteData.payload || isEmpty(remoteData.payload.values)),
Expand Down Expand Up @@ -331,6 +354,25 @@ export class BrowserKlaroService extends KlaroService {
return 'klaro-' + identifier;
}

watchConsentUpdates(): void {
this.lazyKlaro.then(({getManager}) => {
const manager = getManager(this.klaroConfig);
const consentsSubject$ = this.consentsUpdates$;
let lastCookiesConsents = this.lastCookiesConsents;

consentsSubject$.next(manager.consents);
manager.watch({
update(_, eventName, consents) {

if (eventName === 'consents' && !isEqual(consents, lastCookiesConsents)) {
lastCookiesConsents = deepClone(consents);
consentsSubject$.next(consents);
}
}
});
});
}

/**
* remove the google analytics from the services
*/
Expand Down
20 changes: 16 additions & 4 deletions src/app/shared/cookies/klaro.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { Injectable } from '@angular/core';

import { Observable } from 'rxjs';

import { BehaviorSubject, Observable } from 'rxjs';
export interface CookieConsents {
[key: string]: boolean;
}
/**
* Abstract class representing a service for handling Klaro consent preferences and UI
*/
Expand All @@ -10,15 +12,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<any>;

/**
* Watch for changes in consents
*/
abstract watchConsentUpdates(): void;

/**
* Subject to emit updates in the consents
*/
abstract consentsUpdates$: BehaviorSubject<CookieConsents>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,33 @@
<div class="d-flex flex-nowrap flex-row gapx-3 align-items-center pr-3"
*ngIf="!failed && !(isHidden$ | async) && (remark | dsListMetricProps: 'data-badge-enabled':isListElement == true)">
<div #metricChild>
<div
class="altmetric-embed"
[attr.data-hide-no-mentions]="remark | dsListMetricProps : 'data-hide-no-mentions' : isListElement"
[attr.data-hide-less-than]="remark | dsListMetricProps : 'data-hide-less-than' : isListElement"
[attr.data-badge-details]="remark | dsListMetricProps : 'data-badge-details' : isListElement"
[attr.data-badge-type]="remark | dsListMetricProps : 'badgeType' : isListElement"
[attr.data-badge-popover]="remark | dsListMetricProps : 'popover' : isListElement"
[attr.data-doi]="remark | dsListMetricProps : 'doiAttr' : isListElement"
[attr.data-pmid]="remark | dsListMetricProps : 'pmidAttr' : isListElement"
[attr.data-link-target]="remark | dsListMetricProps : 'data-link-target' : isListElement"
></div>
<div class="row d-flex align-items-center"
*ngIf="(!failed &&
canLoadScript &&
!(isHidden$ | async) &&
(remark | dsListMetricProps: 'data-badge-enabled':isListElement == true))"
>
<div class="col-5 text-left">
<div #metricChild>
<div
class="altmetric-embed"
[attr.data-hide-no-mentions]="visibleWithoutData ? false : (remark | dsListMetricProps : 'data-hide-no-mentions' : isListElement)"
[attr.data-hide-less-than]="remark | dsListMetricProps : 'data-hide-less-than' : isListElement"
[attr.data-badge-details]="remark | dsListMetricProps : 'data-badge-details' : isListElement"
[attr.data-badge-type]="remark | dsListMetricProps : 'badgeType' : isListElement"
[attr.data-badge-popover]="remark | dsListMetricProps : 'popover' : isListElement"
[attr.data-doi]="remark | dsListMetricProps : 'doiAttr' : isListElement"
[attr.data-pmid]="remark | dsListMetricProps : 'pmidAttr' : isListElement"
[attr.data-link-target]="remark | dsListMetricProps : 'data-link-target' : isListElement"
></div>
</div>
</div>
<div class="font-weight-bold" *ngIf="!hideLabel">
{{ "item.page.metric.label." + metric.metricType | translate }}
<div class="col-7" *ngIf="!hideLabel">
<div class="font-weight-bold text-capitalize">
{{ metric.metricType | translate }}
</div>
</div>
</div>
<div class="row d-flex align-items-center justify-content-center m-2" *ngIf="!canLoadScript && !isListElement">
<div>
{{ "third-party-metrics-cookies.message" | translate: {metricType: metric.metricType | titlecase} }}
<div role="button" class="btn-link" (click)="requestSettingsConsent.emit(true)">{{"third-party-metrics-cookies.consent-settings" | translate}}</div>
</div>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ describe('MetricAltmetricComponent', () => {
component = fixture.componentInstance;
component.metric = metricMock;
component.success = true;
component.canLoadScript = true;
component.maxRetry = 0;
fixture.detectChanges();
});
Expand All @@ -50,7 +51,7 @@ describe('MetricAltmetricComponent', () => {
expect(component).toBeTruthy();
});
it('should render badge div', () => {
const div = fixture.debugElement.queryAll(By.css('div'))[2];
const div = fixture.debugElement.queryAll(By.css('div'))[3];
expect(div.nativeElement.className).toEqual('altmetric-embed');
expect(div.nativeElement.dataset.badgePopover).toEqual('bottom');
expect(div.nativeElement.dataset.doi).toEqual('10.1056/Test');
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,33 @@
<div class="d-flex flex-nowrap flex-row gapx-3 align-items-center pr-3"
*ngIf="!(isHidden$ | async) && !failed && (remark | dsListMetricProps: 'data-badge-enabled':isListElement == true)">
<div
#metricChild
class="__dimensions_badge_embed__"
[attr.data-hide-zero-citations]="remark | dsListMetricProps: 'data-hide-zero-citations':isListElement"
[attr.data-pmid]="
<div class="row d-flex align-items-center"
*ngIf="!(isHidden$ | async) &&
canLoadScript &&
!failed &&
(remark | dsListMetricProps: 'data-badge-enabled':isListElement == true)"
>
<div class="col-5 text-left">
<div
#metricChild
class="__dimensions_badge_embed__"
[attr.data-hide-zero-citations]="visibleWithoutData ? false : (remark | dsListMetricProps: 'data-hide-zero-citations':isListElement)"
[attr.data-pmid]="
(remark | dsListMetricProps: 'data-doi':isListElement)
? null
: (remark | dsListMetricProps: 'data-pmid':isListElement)
"
[attr.data-doi]="remark | dsListMetricProps: 'data-doi':isListElement"
[attr.data-style]="remark | dsListMetricProps: 'data-style':isListElement"
[attr.data-legend]="remark | dsListMetricProps: 'data-legend':isListElement"
></div>
<div class="font-weight-bold" *ngIf="!hideLabel">
{{ "item.page.metric.label." + metric.metricType | translate }}
[attr.data-doi]="remark | dsListMetricProps: 'data-doi':isListElement"
[attr.data-style]="remark | dsListMetricProps: 'data-style':isListElement"
[attr.data-legend]="remark | dsListMetricProps: 'data-legend':isListElement"
></div>
</div>
<div class="col-7" *ngIf="!hideLabel">
<div class="font-weight-bold text-capitalize">
{{ metric.metricType | translate }}
</div>
</div>
</div>
<div class="row d-flex align-items-center justify-content-center m-2" *ngIf="!canLoadScript && !isListElement">
<div>
{{ "third-party-metrics-cookies.message" | translate: {metricType: metric.metricType | titlecase} }}
<div role="button" class="btn-link" (click)="requestSettingsConsent.emit(true)">{{"third-party-metrics-cookies.consent-settings" | translate}}</div>
</div>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ describe('MetricDimensionsComponent', () => {
component = fixture.componentInstance;
component.metric = metricMock;
component.success = true;
component.canLoadScript = true;
component.maxRetry = 0;
fixture.detectChanges();
});
Expand All @@ -49,7 +50,7 @@ describe('MetricDimensionsComponent', () => {
expect(component).toBeTruthy();
});
it('should render badge div', () => {
const div = fixture.debugElement.queryAll(By.css('div'))[1];
const div = fixture.debugElement.queryAll(By.css('div'))[2];
expect(div.nativeElement.className).toEqual('__dimensions_badge_embed__');
expect(div.nativeElement.dataset.doi).toEqual('10.1056/Test');
expect(div.nativeElement.dataset.style).toEqual('small_rectangle');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
Expand Down
15 changes: 15 additions & 0 deletions src/app/shared/metric/metric-loader/base-metric.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,26 @@ export abstract class BaseMetricComponent {

@Output() hide: EventEmitter<boolean> = new EventEmitter();

/**
* Emitter to trigger a new prompt of the cookies modal
*/
@Output() requestSettingsConsent: EventEmitter<boolean> = new EventEmitter();

/**
* A boolean representing if the metric content is hidden or not
*/
isHidden$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);

/**
* A boolean to check if the component can load the associated script
*/
canLoadScript = true;

/**
* A boolean to force rendering without data
*/
visibleWithoutData = false;

/**
* Get the detail url form metric remark if present.
*/
Expand Down
Loading

0 comments on commit 0d2b3d4

Please sign in to comment.