mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
fixed SSR issue, added code for storing the users cookie consents and restoring them
This commit is contained in:
@@ -97,7 +97,7 @@
|
|||||||
"json5": "^2.1.0",
|
"json5": "^2.1.0",
|
||||||
"jsonschema": "1.2.2",
|
"jsonschema": "1.2.2",
|
||||||
"jwt-decode": "^2.2.0",
|
"jwt-decode": "^2.2.0",
|
||||||
"klaro": "^0.5.34",
|
"klaro": "^0.6.3",
|
||||||
"moment": "^2.22.1",
|
"moment": "^2.22.1",
|
||||||
"morgan": "^1.9.1",
|
"morgan": "^1.9.1",
|
||||||
"ng-mocks": "^8.1.0",
|
"ng-mocks": "^8.1.0",
|
||||||
|
@@ -31,8 +31,8 @@ import { Angulartics2DSpace } from './statistics/angulartics/dspace-provider';
|
|||||||
import { environment } from '../environments/environment';
|
import { environment } from '../environments/environment';
|
||||||
import { models } from './core/core.module';
|
import { models } from './core/core.module';
|
||||||
import { LocaleService } from './core/locale/locale.service';
|
import { LocaleService } from './core/locale/locale.service';
|
||||||
import { KlaroService } from './shared/cookies/klaro.service';
|
|
||||||
import { hasValue } from './shared/empty.util';
|
import { hasValue } from './shared/empty.util';
|
||||||
|
import { KlaroService } from './shared/cookies/klaro.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-app',
|
selector: 'ds-app',
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { Component, Optional } from '@angular/core';
|
import { Component, Optional } from '@angular/core';
|
||||||
import { KlaroService } from '../shared/cookies/klaro.service';
|
|
||||||
import { hasValue } from '../shared/empty.util';
|
import { hasValue } from '../shared/empty.util';
|
||||||
|
import { KlaroService } from '../shared/cookies/klaro.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-footer',
|
selector: 'ds-footer',
|
||||||
|
@@ -1,12 +1,12 @@
|
|||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
|
|
||||||
import { KlaroService } from './klaro.service';
|
import { BrowserKlaroService } from './browser-klaro.service';
|
||||||
|
|
||||||
describe('KlaroService', () => {
|
describe('KlaroService', () => {
|
||||||
beforeEach(() => TestBed.configureTestingModule({}));
|
beforeEach(() => TestBed.configureTestingModule({}));
|
||||||
|
|
||||||
it('should be created', () => {
|
it('should be created', () => {
|
||||||
const service: KlaroService = TestBed.get(KlaroService);
|
const service: BrowserKlaroService = TestBed.get(BrowserKlaroService);
|
||||||
expect(service).toBeTruthy();
|
expect(service).toBeTruthy();
|
||||||
});
|
});
|
||||||
});
|
});
|
324
src/app/shared/cookies/browser-klaro.service.ts
Normal file
324
src/app/shared/cookies/browser-klaro.service.ts
Normal file
@@ -0,0 +1,324 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import * as Klaro from 'klaro'
|
||||||
|
import { BehaviorSubject, Observable } from 'rxjs';
|
||||||
|
import { TOKENITEM } from '../../core/auth/models/auth-token-info.model';
|
||||||
|
import { AuthService, IMPERSONATING_COOKIE, REDIRECT_COOKIE } from '../../core/auth/auth.service';
|
||||||
|
import { LANG_COOKIE } from '../../core/locale/locale.service';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { environment } from '../../../environments/environment';
|
||||||
|
import { map, switchMap, take, tap } from 'rxjs/operators';
|
||||||
|
import { EPerson } from '../../core/eperson/models/eperson.model';
|
||||||
|
import { of as observableOf, combineLatest as observableCombineLatest } from 'rxjs';
|
||||||
|
import { KlaroService } from './klaro.service';
|
||||||
|
import { hasValue } from '../empty.util';
|
||||||
|
import { CookieService } from '../../core/services/cookie.service';
|
||||||
|
import { EPersonDataService } from '../../core/eperson/eperson-data.service';
|
||||||
|
|
||||||
|
export const HAS_AGREED_END_USER = 'dsHasAgreedEndUser';
|
||||||
|
export const COOKIE_MDFIELD = 'dspace.agreements.cookies';
|
||||||
|
const cookieNameMessagePrefix = 'cookies.consent.app.title.';
|
||||||
|
const cookieDescriptionMessagePrefix = 'cookies.consent.app.description.';
|
||||||
|
const cookiePurposeMessagePrefix = 'cookies.consent.purpose.';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class BrowserKlaroService extends KlaroService {
|
||||||
|
|
||||||
|
message$: BehaviorSubject<string> = new BehaviorSubject<string>('');
|
||||||
|
|
||||||
|
klaroConfig: any = {
|
||||||
|
storageName: this.getStorageName('anonymous'),
|
||||||
|
|
||||||
|
privacyPolicy: '/info/privacy',
|
||||||
|
|
||||||
|
/*
|
||||||
|
Setting 'hideLearnMore' to 'true' will hide the "learn more / customize" link in
|
||||||
|
the consent notice. We strongly advise against using this under most
|
||||||
|
circumstances, as it keeps the user from customizing his/her consent choices.
|
||||||
|
*/
|
||||||
|
hideLearnMore: false,
|
||||||
|
|
||||||
|
/*
|
||||||
|
Setting 'acceptAll' to 'true' will show an "accept all" button in the notice and
|
||||||
|
modal, which will enable all third-party apps if the user clicks on it. If set
|
||||||
|
to 'false', there will be an "accept" button that will only enable the apps that
|
||||||
|
are enabled in the consent modal.
|
||||||
|
*/
|
||||||
|
acceptAll: true,
|
||||||
|
|
||||||
|
/*
|
||||||
|
You can also set a custom expiration time for the Klaro cookie. By default, it
|
||||||
|
will expire after 30 days. Only relevant if 'storageMethod' is set to 'cookie'.
|
||||||
|
*/
|
||||||
|
cookieExpiresAfterDays: 365,
|
||||||
|
|
||||||
|
htmlTexts: true,
|
||||||
|
|
||||||
|
/*
|
||||||
|
You can overwrite existing translations and add translations for your app
|
||||||
|
descriptions and purposes. See `src/translations/` for a full list of
|
||||||
|
translations that can be overwritten:
|
||||||
|
https://github.com/KIProtect/klaro/tree/master/src/translations
|
||||||
|
*/
|
||||||
|
translations: {
|
||||||
|
en: {
|
||||||
|
acceptAll: 'cookies.consent.accept-all',
|
||||||
|
acceptSelected: 'cookies.consent.accept-selected',
|
||||||
|
app: {
|
||||||
|
optOut: {
|
||||||
|
description: 'cookies.consent.app.opt-out.description',
|
||||||
|
title: 'cookies.consent.app.opt-out.title'
|
||||||
|
},
|
||||||
|
purpose: 'cookies.consent.app.purpose',
|
||||||
|
purposes: 'cookies.consent.app.purposes',
|
||||||
|
required: {
|
||||||
|
description: 'cookies.consent.app.required.description',
|
||||||
|
title: 'cookies.consent.app.required.title'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
close: 'cookies.consent.close',
|
||||||
|
decline: 'cookies.consent.decline',
|
||||||
|
consentNotice: {
|
||||||
|
description: 'cookies.consent.content-notice.description',
|
||||||
|
learnMore: 'cookies.consent.content-notice.learnMore'
|
||||||
|
},
|
||||||
|
consentModal: {
|
||||||
|
description: 'cookies.consent.content-modal.description',
|
||||||
|
privacyPolicy: {
|
||||||
|
name: 'cookies.consent.content-modal.privacy-policy.name',
|
||||||
|
text: 'cookies.consent.content-modal.privacy-policy.text'
|
||||||
|
},
|
||||||
|
title: 'cookies.consent.content-modal.title'
|
||||||
|
},
|
||||||
|
purposes: {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
apps: [
|
||||||
|
{
|
||||||
|
name: 'token_item',
|
||||||
|
purposes: ['authentication'],
|
||||||
|
required: true,
|
||||||
|
cookies: [
|
||||||
|
TOKENITEM
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'impersonation',
|
||||||
|
purposes: ['authentication'],
|
||||||
|
required: true,
|
||||||
|
cookies: [
|
||||||
|
IMPERSONATING_COOKIE
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'redirect',
|
||||||
|
purposes: ['authentication'],
|
||||||
|
required: true,
|
||||||
|
cookies: [
|
||||||
|
REDIRECT_COOKIE
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'language',
|
||||||
|
purposes: ['preferences'],
|
||||||
|
required: true,
|
||||||
|
cookies: [
|
||||||
|
LANG_COOKIE
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'klaro',
|
||||||
|
purposes: ['acknowledgement'],
|
||||||
|
required: true,
|
||||||
|
cookies: [
|
||||||
|
[/^klaro-.+$/],
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'has_agreed_end_user',
|
||||||
|
purposes: ['acknowledgement'],
|
||||||
|
required: true,
|
||||||
|
cookies: [
|
||||||
|
HAS_AGREED_END_USER
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'google-analytics',
|
||||||
|
purposes: ['statistics'],
|
||||||
|
required: false,
|
||||||
|
cookies: [
|
||||||
|
// /*
|
||||||
|
// you an either only provide a cookie name or regular expression (regex) or a list
|
||||||
|
// consisting of a name or regex, a path and a cookie domain. Providing a path and
|
||||||
|
// domain is necessary if you have apps that set cookies for a path that is not
|
||||||
|
// "/", or a domain that is not the current domain. If you do not set these values
|
||||||
|
// properly, the cookie can't be deleted by Klaro, as there is no way to access the
|
||||||
|
// path or domain of a cookie in JS. Notice that it is not possible to delete
|
||||||
|
// cookies that were set on a third-party domain, or cookies that have the HTTPOnly
|
||||||
|
// attribute: https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie#new-
|
||||||
|
// cookie_domain
|
||||||
|
// */
|
||||||
|
//
|
||||||
|
// /*
|
||||||
|
// This rule will match cookies that contain the string '_pk_' and that are set on
|
||||||
|
// the path '/' and the domain 'klaro.kiprotect.com'
|
||||||
|
// */
|
||||||
|
[/^_ga.?$/],
|
||||||
|
[/^_gid$/],
|
||||||
|
//
|
||||||
|
// /*
|
||||||
|
// Same as above, only for the 'localhost' domain
|
||||||
|
// */
|
||||||
|
// [/^_pk_.*$/, '/', 'localhost'],
|
||||||
|
//
|
||||||
|
// /*
|
||||||
|
// This rule will match all cookies named 'piwik_ignore' that are set on the path
|
||||||
|
// '/' on the current domain
|
||||||
|
// */
|
||||||
|
// 'piwik_ignore',
|
||||||
|
],
|
||||||
|
|
||||||
|
/*
|
||||||
|
You can define an optional callback function that will be called each time the
|
||||||
|
consent state for the given app changes. The consent value will be passed as the
|
||||||
|
first parameter to the function (true=consented). The `app` config will be
|
||||||
|
passed as the second parameter.
|
||||||
|
*/
|
||||||
|
callback: (consent, app) => {
|
||||||
|
console.log(consent, app);
|
||||||
|
this.message$.next('User consent for app ' + app.name + ': consent=' + consent);
|
||||||
|
},
|
||||||
|
/*
|
||||||
|
If 'onlyOnce' is set to 'true', the app will only be executed once regardless
|
||||||
|
how often the user toggles it on and off. This is relevant e.g. for tracking
|
||||||
|
scripts that would generate new page view events every time Klaro disables and
|
||||||
|
re-enables them due to a consent change by the user.
|
||||||
|
*/
|
||||||
|
onlyOnce: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private translateService: TranslateService,
|
||||||
|
private authService: AuthService,
|
||||||
|
private ePersonService: EPersonDataService,
|
||||||
|
private cookieService: CookieService) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
initialize() {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make sure the fallback language is english
|
||||||
|
*/
|
||||||
|
this.translateService.setDefaultLang(environment.defaultLanguage);
|
||||||
|
|
||||||
|
const user$: Observable<EPerson> = this.authService.isAuthenticated()
|
||||||
|
.pipe(
|
||||||
|
take(1),
|
||||||
|
switchMap((loggedIn: boolean) => {
|
||||||
|
if (loggedIn) {
|
||||||
|
return this.authService.getAuthenticatedUserFromStore();
|
||||||
|
}
|
||||||
|
return observableOf(undefined);
|
||||||
|
}),
|
||||||
|
take(1)
|
||||||
|
);
|
||||||
|
|
||||||
|
const translationServiceReady$ = this.translateService.get('loading.default').pipe(take(1));
|
||||||
|
|
||||||
|
observableCombineLatest(user$, translationServiceReady$)
|
||||||
|
.subscribe(([user, translation]: [EPerson, string]) => {
|
||||||
|
if (hasValue(user)) {
|
||||||
|
this.klaroConfig.callback = (consent, app) => this.updateSettingsForUsers(user);
|
||||||
|
this.klaroConfig.storageName = this.getStorageName(user.uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add all message keys for apps and purposes
|
||||||
|
*/
|
||||||
|
this.addAppMessages();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Subscribe on a message to make sure the translation service is ready
|
||||||
|
* Translate all keys in the translation section of the configuration
|
||||||
|
* Show the configuration if the configuration has not been confirmed
|
||||||
|
*/
|
||||||
|
this.translateConfiguration();
|
||||||
|
Klaro.renderKlaro(this.klaroConfig, false);
|
||||||
|
Klaro.initialize();
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private getTitleTranslation(title: string) {
|
||||||
|
return cookieNameMessagePrefix + title;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getDescriptionTranslation(description: string) {
|
||||||
|
return cookieDescriptionMessagePrefix + description;
|
||||||
|
}
|
||||||
|
|
||||||
|
private getPurposeTranslation(purpose: string) {
|
||||||
|
return cookiePurposeMessagePrefix + purpose;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the cookie consent form
|
||||||
|
*/
|
||||||
|
showSettings() {
|
||||||
|
Klaro.show(this.klaroConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add message keys for all apps and purposes
|
||||||
|
*/
|
||||||
|
addAppMessages() {
|
||||||
|
this.klaroConfig.apps.forEach((app) => {
|
||||||
|
this.klaroConfig.translations.en[app.name] = { title: this.getTitleTranslation(app.name), description: this.getDescriptionTranslation(app.name) };
|
||||||
|
app.purposes.forEach((purpose) => {
|
||||||
|
this.klaroConfig.translations.en.purposes[purpose] = this.getPurposeTranslation(purpose);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Translate the translation section from the Klaro configuration
|
||||||
|
*/
|
||||||
|
translateConfiguration() {
|
||||||
|
this.translate(this.klaroConfig.translations.en);
|
||||||
|
}
|
||||||
|
|
||||||
|
private translate(object) {
|
||||||
|
if (typeof (object) === 'string') {
|
||||||
|
return this.translateService.instant(object);
|
||||||
|
}
|
||||||
|
Object.entries(object).forEach(([key, value]: [string, any]) => {
|
||||||
|
object[key] = this.translate(value);
|
||||||
|
});
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSettingsForUser(user: EPerson) {
|
||||||
|
return JSON.parse(user.firstMetadataValue(COOKIE_MDFIELD));
|
||||||
|
}
|
||||||
|
|
||||||
|
setSettingsForUser(user: EPerson, config: object) {
|
||||||
|
user.setMetadata(COOKIE_MDFIELD, undefined, JSON.stringify(config));
|
||||||
|
this.ePersonService.update(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
restoreSettingsForUsers(user: EPerson) {
|
||||||
|
console.log('restore klaro', user);
|
||||||
|
this.cookieService.set(this.getStorageName(user.uuid), this.getSettingsForUser(user));
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSettingsForUsers(user: EPerson) {
|
||||||
|
console.log('update klaro', user);
|
||||||
|
this.setSettingsForUser(user, this.cookieService.get(this.getStorageName(user.uuid)))
|
||||||
|
}
|
||||||
|
|
||||||
|
getStorageName(identifier: string) {
|
||||||
|
return 'klaro-' + identifier
|
||||||
|
}
|
||||||
|
}
|
3
src/app/shared/cookies/klaro.effects.ts
Normal file
3
src/app/shared/cookies/klaro.effects.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export class KlaroEffects {
|
||||||
|
|
||||||
|
}
|
@@ -1,317 +1,7 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import * as Klaro from 'klaro'
|
|
||||||
import { BehaviorSubject, Observable } from 'rxjs';
|
|
||||||
import { TOKENITEM } from '../../core/auth/models/auth-token-info.model';
|
|
||||||
import { AuthService, IMPERSONATING_COOKIE, REDIRECT_COOKIE } from '../../core/auth/auth.service';
|
|
||||||
import { LANG_COOKIE } from '../../core/locale/locale.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 { of as observableOf, combineLatest as observableCombineLatest } from 'rxjs';
|
|
||||||
|
|
||||||
export const HAS_AGREED_END_USER = 'dsHasAgreedEndUser';
|
|
||||||
export const COOKIE_MDFIELD = 'dspace.agreements.cookies';
|
|
||||||
const cookieNameMessagePrefix = 'cookies.consent.app.title.';
|
|
||||||
const cookieDescriptionMessagePrefix = 'cookies.consent.app.description.';
|
|
||||||
const cookiePurposeMessagePrefix = 'cookies.consent.purpose.';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class KlaroService {
|
export abstract class KlaroService {
|
||||||
|
abstract initialize();
|
||||||
message$: BehaviorSubject<string> = new BehaviorSubject<string>('');
|
abstract showSettings();
|
||||||
|
|
||||||
klaroConfig = {
|
|
||||||
storageName: 'klaro-anonymous',
|
|
||||||
|
|
||||||
privacyPolicy: '/info/privacy',
|
|
||||||
|
|
||||||
/*
|
|
||||||
Setting 'hideLearnMore' to 'true' will hide the "learn more / customize" link in
|
|
||||||
the consent notice. We strongly advise against using this under most
|
|
||||||
circumstances, as it keeps the user from customizing his/her consent choices.
|
|
||||||
*/
|
|
||||||
hideLearnMore: false,
|
|
||||||
|
|
||||||
/*
|
|
||||||
Setting 'acceptAll' to 'true' will show an "accept all" button in the notice and
|
|
||||||
modal, which will enable all third-party apps if the user clicks on it. If set
|
|
||||||
to 'false', there will be an "accept" button that will only enable the apps that
|
|
||||||
are enabled in the consent modal.
|
|
||||||
*/
|
|
||||||
acceptAll: true,
|
|
||||||
|
|
||||||
/*
|
|
||||||
You can also set a custom expiration time for the Klaro cookie. By default, it
|
|
||||||
will expire after 30 days. Only relevant if 'storageMethod' is set to 'cookie'.
|
|
||||||
*/
|
|
||||||
cookieExpiresAfterDays: 365,
|
|
||||||
|
|
||||||
htmlTexts: true,
|
|
||||||
|
|
||||||
/*
|
|
||||||
You can overwrite existing translations and add translations for your app
|
|
||||||
descriptions and purposes. See `src/translations/` for a full list of
|
|
||||||
translations that can be overwritten:
|
|
||||||
https://github.com/KIProtect/klaro/tree/master/src/translations
|
|
||||||
*/
|
|
||||||
translations: {
|
|
||||||
en: {
|
|
||||||
acceptAll: 'cookies.consent.accept-all',
|
|
||||||
acceptSelected: 'cookies.consent.accept-selected',
|
|
||||||
app: {
|
|
||||||
optOut: {
|
|
||||||
description: 'cookies.consent.app.opt-out.description',
|
|
||||||
title: 'cookies.consent.app.opt-out.title'
|
|
||||||
},
|
|
||||||
purpose: 'cookies.consent.app.purpose',
|
|
||||||
purposes: 'cookies.consent.app.purposes',
|
|
||||||
required: {
|
|
||||||
description: 'cookies.consent.app.required.description',
|
|
||||||
title: 'cookies.consent.app.required.title'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
close: 'cookies.consent.close',
|
|
||||||
decline: 'cookies.consent.decline',
|
|
||||||
consentNotice: {
|
|
||||||
description: 'cookies.consent.content-notice.description',
|
|
||||||
learnMore: 'cookies.consent.content-notice.learnMore'
|
|
||||||
},
|
|
||||||
consentModal: {
|
|
||||||
description: 'cookies.consent.content-modal.description',
|
|
||||||
privacyPolicy: {
|
|
||||||
name: 'cookies.consent.content-modal.privacy-policy.name',
|
|
||||||
text: 'cookies.consent.content-modal.privacy-policy.text'
|
|
||||||
},
|
|
||||||
title: 'cookies.consent.content-modal.title'
|
|
||||||
},
|
|
||||||
purposes: {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
apps: [
|
|
||||||
{
|
|
||||||
name: 'token_item',
|
|
||||||
purposes: ['authentication'],
|
|
||||||
required: true,
|
|
||||||
cookies: [
|
|
||||||
TOKENITEM
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'impersonation',
|
|
||||||
purposes: ['authentication'],
|
|
||||||
required: true,
|
|
||||||
cookies: [
|
|
||||||
IMPERSONATING_COOKIE
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'redirect',
|
|
||||||
purposes: ['authentication'],
|
|
||||||
required: true,
|
|
||||||
cookies: [
|
|
||||||
REDIRECT_COOKIE
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'language',
|
|
||||||
purposes: ['preferences'],
|
|
||||||
required: true,
|
|
||||||
cookies: [
|
|
||||||
LANG_COOKIE
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'klaro',
|
|
||||||
purposes: ['acknowledgement'],
|
|
||||||
required: true,
|
|
||||||
cookies: [
|
|
||||||
[/^klaro-.+$/],
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'has_agreed_end_user',
|
|
||||||
purposes: ['acknowledgement'],
|
|
||||||
required: true,
|
|
||||||
cookies: [
|
|
||||||
HAS_AGREED_END_USER
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'google-analytics',
|
|
||||||
purposes: ['statistics'],
|
|
||||||
required: false,
|
|
||||||
cookies: [
|
|
||||||
// /*
|
|
||||||
// you an either only provide a cookie name or regular expression (regex) or a list
|
|
||||||
// consisting of a name or regex, a path and a cookie domain. Providing a path and
|
|
||||||
// domain is necessary if you have apps that set cookies for a path that is not
|
|
||||||
// "/", or a domain that is not the current domain. If you do not set these values
|
|
||||||
// properly, the cookie can't be deleted by Klaro, as there is no way to access the
|
|
||||||
// path or domain of a cookie in JS. Notice that it is not possible to delete
|
|
||||||
// cookies that were set on a third-party domain, or cookies that have the HTTPOnly
|
|
||||||
// attribute: https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie#new-
|
|
||||||
// cookie_domain
|
|
||||||
// */
|
|
||||||
//
|
|
||||||
// /*
|
|
||||||
// This rule will match cookies that contain the string '_pk_' and that are set on
|
|
||||||
// the path '/' and the domain 'klaro.kiprotect.com'
|
|
||||||
// */
|
|
||||||
[/^_ga.?$/],
|
|
||||||
[/^_gid$/],
|
|
||||||
//
|
|
||||||
// /*
|
|
||||||
// Same as above, only for the 'localhost' domain
|
|
||||||
// */
|
|
||||||
// [/^_pk_.*$/, '/', 'localhost'],
|
|
||||||
//
|
|
||||||
// /*
|
|
||||||
// This rule will match all cookies named 'piwik_ignore' that are set on the path
|
|
||||||
// '/' on the current domain
|
|
||||||
// */
|
|
||||||
// 'piwik_ignore',
|
|
||||||
],
|
|
||||||
|
|
||||||
/*
|
|
||||||
You can define an optional callback function that will be called each time the
|
|
||||||
consent state for the given app changes. The consent value will be passed as the
|
|
||||||
first parameter to the function (true=consented). The `app` config will be
|
|
||||||
passed as the second parameter.
|
|
||||||
*/
|
|
||||||
callback: (consent, app) => {
|
|
||||||
console.log(consent, app);
|
|
||||||
this.message$.next('User consent for app ' + app.name + ': consent=' + consent);
|
|
||||||
},
|
|
||||||
/*
|
|
||||||
If 'onlyOnce' is set to 'true', the app will only be executed once regardless
|
|
||||||
how often the user toggles it on and off. This is relevant e.g. for tracking
|
|
||||||
scripts that would generate new page view events every time Klaro disables and
|
|
||||||
re-enables them due to a consent change by the user.
|
|
||||||
*/
|
|
||||||
onlyOnce: true,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
/*
|
|
||||||
You can define an optional callback function that will be called each time the
|
|
||||||
consent state for any given app changes. The consent value will be passed as the
|
|
||||||
first parameter to the function (true=consented). The `app` config will be
|
|
||||||
passed as the second parameter.
|
|
||||||
*/
|
|
||||||
callback: (consent, app) => {
|
|
||||||
|
|
||||||
/*
|
|
||||||
You can define an optional callback function that will be called each time the
|
|
||||||
consent state for any given app changes. The consent value will be passed as the
|
|
||||||
first parameter to the function (true=consented). The `app` config will be
|
|
||||||
passed as the second parameter.
|
|
||||||
*/
|
|
||||||
console.log(
|
|
||||||
'User consent for app ' + app.name + ': consent=' + consent
|
|
||||||
);
|
|
||||||
// this.updateUserCookieConsent();
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
private translateService: TranslateService,
|
|
||||||
private authService: AuthService) {
|
|
||||||
}
|
|
||||||
|
|
||||||
initialize() {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Make sure the fallback language is english
|
|
||||||
*/
|
|
||||||
this.translateService.setDefaultLang(environment.defaultLanguage);
|
|
||||||
|
|
||||||
const storageName$: Observable<string> = this.authService.isAuthenticated()
|
|
||||||
.pipe(
|
|
||||||
take(1),
|
|
||||||
switchMap((loggedIn: boolean) => {
|
|
||||||
if (loggedIn) {
|
|
||||||
return this.authService.getAuthenticatedUserFromStore().pipe(map((user: EPerson) => 'klaro-' + user.uuid), take(1))
|
|
||||||
} else {
|
|
||||||
return observableOf('klaro-anonymous')
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
const translationServiceReady$ = this.translateService.get('loading.default').pipe(take(1));
|
|
||||||
|
|
||||||
observableCombineLatest(storageName$, translationServiceReady$)
|
|
||||||
.subscribe(([name, translation]: string[]) => {
|
|
||||||
this.klaroConfig.storageName = name;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add all message keys for apps and purposes
|
|
||||||
*/
|
|
||||||
this.addAppMessages();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Subscribe on a message to make sure the translation service is ready
|
|
||||||
* Translate all keys in the translation section of the configuration
|
|
||||||
* Show the configuration if the configuration has not been confirmed
|
|
||||||
*/
|
|
||||||
this.translateConfiguration();
|
|
||||||
Klaro.renderKlaro(this.klaroConfig, false);
|
|
||||||
Klaro.initialize();
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private getTitleTranslation(title: string) {
|
|
||||||
return cookieNameMessagePrefix + title;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getDescriptionTranslation(description: string) {
|
|
||||||
return cookieDescriptionMessagePrefix + description;
|
|
||||||
}
|
|
||||||
|
|
||||||
private getPurposeTranslation(purpose: string) {
|
|
||||||
return cookiePurposeMessagePrefix + purpose;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Show the cookie consent form
|
|
||||||
*/
|
|
||||||
showSettings() {
|
|
||||||
Klaro.show(this.klaroConfig);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add message keys for all apps and purposes
|
|
||||||
*/
|
|
||||||
addAppMessages() {
|
|
||||||
this.klaroConfig.apps.forEach((app) => {
|
|
||||||
this.klaroConfig.translations.en[app.name] = { title: this.getTitleTranslation(app.name), description: this.getDescriptionTranslation(app.name) };
|
|
||||||
app.purposes.forEach((purpose) => {
|
|
||||||
this.klaroConfig.translations.en.purposes[purpose] = this.getPurposeTranslation(purpose);
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Translate the translation section from the Klaro configuration
|
|
||||||
*/
|
|
||||||
translateConfiguration() {
|
|
||||||
this.translate(this.klaroConfig.translations.en);
|
|
||||||
}
|
|
||||||
|
|
||||||
private translate(object) {
|
|
||||||
if (typeof (object) === 'string') {
|
|
||||||
return this.translateService.instant(object);
|
|
||||||
}
|
|
||||||
Object.entries(object).forEach(([key, value]: [string, any]) => {
|
|
||||||
object[key] = this.translate(value);
|
|
||||||
});
|
|
||||||
return object;
|
|
||||||
}
|
|
||||||
|
|
||||||
getSettingsForUser(user: EPerson) {
|
|
||||||
return JSON.parse(user.firstMetadataValue(COOKIE_MDFIELD));
|
|
||||||
}
|
|
||||||
|
|
||||||
setSettingsForUser(user: EPerson, config: object) {
|
|
||||||
return user.setMetadata(COOKIE_MDFIELD, undefined, JSON.stringify(config));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@@ -21,6 +21,7 @@ import { AuthService } from '../../app/core/auth/auth.service';
|
|||||||
import { Angulartics2RouterlessModule } from 'angulartics2/routerlessmodule';
|
import { Angulartics2RouterlessModule } from 'angulartics2/routerlessmodule';
|
||||||
import { SubmissionService } from '../../app/submission/submission.service';
|
import { SubmissionService } from '../../app/submission/submission.service';
|
||||||
import { StatisticsModule } from '../../app/statistics/statistics.module';
|
import { StatisticsModule } from '../../app/statistics/statistics.module';
|
||||||
|
import { BrowserKlaroService } from '../../app/shared/cookies/browser-klaro.service';
|
||||||
import { KlaroService } from '../../app/shared/cookies/klaro.service';
|
import { KlaroService } from '../../app/shared/cookies/klaro.service';
|
||||||
import { HardRedirectService } from '../../app/core/services/hard-redirect.service';
|
import { HardRedirectService } from '../../app/core/services/hard-redirect.service';
|
||||||
import { BrowserHardRedirectService } from '../../app/core/services/browser-hard-redirect.service';
|
import { BrowserHardRedirectService } from '../../app/core/services/browser-hard-redirect.service';
|
||||||
@@ -86,7 +87,7 @@ export function locationProvider(): Location {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: KlaroService,
|
provide: KlaroService,
|
||||||
useClass: KlaroService
|
useClass: BrowserKlaroService
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
provide: SubmissionService,
|
provide: SubmissionService,
|
||||||
|
@@ -6,15 +6,5 @@
|
|||||||
},
|
},
|
||||||
"angularCompilerOptions": {
|
"angularCompilerOptions": {
|
||||||
"entryModule": "./src/modules/app/server-app.module#ServerAppModule"
|
"entryModule": "./src/modules/app/server-app.module#ServerAppModule"
|
||||||
},
|
}
|
||||||
"exclude": [
|
|
||||||
"src/test.ts",
|
|
||||||
"src/**/*.spec.ts",
|
|
||||||
"src/**/*.mock.ts",
|
|
||||||
"src/**/*.test.ts",
|
|
||||||
"src/**/*.stub.ts",
|
|
||||||
"src/**/testing/*",
|
|
||||||
"src/**/mocks/*",
|
|
||||||
"node_modules/klaro/*"
|
|
||||||
],
|
|
||||||
}
|
}
|
||||||
|
@@ -25,11 +25,6 @@ module.exports = {
|
|||||||
module: {
|
module: {
|
||||||
noParse: /polyfills-.*\.js/,
|
noParse: /polyfills-.*\.js/,
|
||||||
rules: [
|
rules: [
|
||||||
{
|
|
||||||
test: /\.js$/,
|
|
||||||
exclude: [/node_modules\/klaro/],
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
{
|
||||||
test: /\.ts$/, loader: 'ts-loader',
|
test: /\.ts$/, loader: 'ts-loader',
|
||||||
options: {
|
options: {
|
||||||
|
@@ -18,15 +18,6 @@ module.exports = Object.assign({}, commonExports, {
|
|||||||
recordsOutputPath: projectRoot('webpack.records.json'),
|
recordsOutputPath: projectRoot('webpack.records.json'),
|
||||||
entry: buildRoot('./main.server.ts'),
|
entry: buildRoot('./main.server.ts'),
|
||||||
target: 'node',
|
target: 'node',
|
||||||
module: {
|
|
||||||
rules: [
|
|
||||||
...commonExports.module.rules,
|
|
||||||
{
|
|
||||||
test: /\.js$/,
|
|
||||||
exclude: [/node_modules\/klaro/],
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
externals: [nodeExternals({
|
externals: [nodeExternals({
|
||||||
whitelist: [
|
whitelist: [
|
||||||
/@angular/,
|
/@angular/,
|
||||||
|
@@ -6165,10 +6165,10 @@ kind-of@^6.0.0, kind-of@^6.0.2:
|
|||||||
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
|
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
|
||||||
integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
|
integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
|
||||||
|
|
||||||
klaro@^0.5.34:
|
klaro@^0.6.3:
|
||||||
version "0.5.34"
|
version "0.6.3"
|
||||||
resolved "https://registry.yarnpkg.com/klaro/-/klaro-0.5.34.tgz#0b524be96a1bb177fe88ff2603e1be75f494fc98"
|
resolved "https://registry.yarnpkg.com/klaro/-/klaro-0.6.3.tgz#b2aaa810d17f073c9a1b5eab618a9f13d5f40fa3"
|
||||||
integrity sha512-M6KHqlBWpMyYoxOK1icoJMeYsaPT7YhzJIAQ3wdxZWGgBc0sV7xQsf0PsgMUVnuTD0AeC58QegCGEv0qYeq4gw==
|
integrity sha512-rRP37FaJaHHSScHIe3YUdMZJ1asxOF5+C/RMrFB2RzhAUfGVMM5/GiucECM3Si1lhW2LL0xGVymE8JhYZl2Bjg==
|
||||||
|
|
||||||
last-call-webpack-plugin@^3.0.0:
|
last-call-webpack-plugin@^3.0.0:
|
||||||
version "3.0.0"
|
version "3.0.0"
|
||||||
|
Reference in New Issue
Block a user