solving SSR issue

This commit is contained in:
lotte
2020-08-27 16:40:39 +02:00
parent 33aeccdd43
commit 3e1fb243a1
8 changed files with 127 additions and 51 deletions

View File

@@ -31,7 +31,8 @@ import { Angulartics2DSpace } from './statistics/angulartics/dspace-provider';
import { environment } from '../environments/environment';
import { models } from './core/core.module';
import { LocaleService } from './core/locale/locale.service';
import { CookiesService } from './shared/cookies/cookies.service';
import { KlaroService } from './shared/cookies/klaro.service';
import { hasValue } from './shared/empty.util';
@Component({
selector: 'ds-app',
@@ -69,7 +70,7 @@ export class AppComponent implements OnInit, AfterViewInit {
private menuService: MenuService,
private windowService: HostWindowService,
private localeService: LocaleService,
@Optional() private cookiesService: CookiesService
@Optional() private cookiesService: KlaroService
) {
/* Use models object so all decorators are actually called */
@@ -83,7 +84,7 @@ export class AppComponent implements OnInit, AfterViewInit {
// set the current language code
this.localeService.setCurrentLanguageCode();
this.cookiesService.initialize();
this.initializeKlaro();
angulartics2GoogleAnalytics.startTracking();
angulartics2DSpace.startTracking();
@@ -163,4 +164,9 @@ export class AppComponent implements OnInit, AfterViewInit {
);
}
private initializeKlaro() {
if (hasValue(this.cookiesService)) {
this.cookiesService.initialize()
}
}
}

View File

@@ -10,7 +10,7 @@ import { Observable, of as observableOf, combineLatest } from 'rxjs';
import { map, take, flatMap } from 'rxjs/operators';
import { NativeWindowService, NativeWindowRef } from '../services/window.service';
export const LANG_COOKIE = 'language_cookie';
export const LANG_COOKIE = 'dsLanguage';
/**
* This enum defines the possible origin of the languages

View File

@@ -184,4 +184,25 @@ export class DSpaceObject extends ListableObject implements CacheableObject {
getRenderTypes(): Array<string | GenericConstructor<ListableObject>> {
return [this.constructor as GenericConstructor<ListableObject>];
}
setMetadata(key: string, language?: string, ...values: string[]) {
const mdValues: MetadataValue[] = values.map((value: string, index: number) => {
const md = new MetadataValue();
md.value = value;
md.authority = null;
md.confidence = -1;
md.language = language || null;
md.place = index;
return md;
});
if (hasNoValue(this.metadata)) {
this.metadata = Object.create({});
}
this.metadata[key] = mdValues;
}
removeMetadata(key: string) {
delete this.metadata[key];
}
}

View File

@@ -1,5 +1,6 @@
import { Component } from '@angular/core';
import { CookiesService } from '../shared/cookies/cookies.service';
import { Component, Optional } from '@angular/core';
import { KlaroService } from '../shared/cookies/klaro.service';
import { hasValue } from '../shared/empty.util';
@Component({
selector: 'ds-footer',
@@ -7,14 +8,15 @@ import { CookiesService } from '../shared/cookies/cookies.service';
templateUrl: 'footer.component.html'
})
export class FooterComponent {
constructor(private cookies: CookiesService) {
}
dateObj: number = Date.now();
constructor(@Optional() private cookies: KlaroService) {
}
showCookieSettings() {
if (hasValue(this.cookies)) {
this.cookies.showSettings();
}
return false;
}
}

View File

@@ -1,12 +1,12 @@
import { TestBed } from '@angular/core/testing';
import { CookiesService } from './cookies.service';
import { KlaroService } from './klaro.service';
describe('CookiesService', () => {
describe('KlaroService', () => {
beforeEach(() => TestBed.configureTestingModule({}));
it('should be created', () => {
const service: CookiesService = TestBed.get(CookiesService);
const service: KlaroService = TestBed.get(KlaroService);
expect(service).toBeTruthy();
});
});

View File

@@ -1,26 +1,23 @@
import { Injectable } from '@angular/core';
import * as Klaro from 'klaro'
import { BehaviorSubject } from 'rxjs';
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 { take } from 'rxjs/operators';
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 = 'hasAgreedEndUser';
export const KLARO = 'klaro';
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({
providedIn: 'root'
})
export class CookiesService {
@Injectable()
export class KlaroService {
message$: BehaviorSubject<string> = new BehaviorSubject<string>('');
@@ -129,7 +126,7 @@ export class CookiesService {
purposes: ['acknowledgement'],
required: true,
cookies: [
KLARO
[/^klaro-.+$/],
]
},
{
@@ -183,6 +180,7 @@ export class CookiesService {
passed as the second parameter.
*/
callback: (consent, app) => {
console.log(consent, app);
this.message$.next('User consent for app ' + app.name + ': consent=' + consent);
},
/*
@@ -193,41 +191,72 @@ export class CookiesService {
*/
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,
) {
private authService: AuthService) {
}
initialize() {
this.authService.getAuthenticatedUserFromStore()
.subscribe((user: EPerson) => {
this.klaroConfig.storageName = 'klaro-' + (user.uuid);
});
/**
* Add all message keys for apps and purposes
*/
this.addAppMessages();
/**
* 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.translateService.get('loading.default').pipe(take(1)).subscribe(() => {
this.translateConfiguration();
Klaro.renderKlaro(this.klaroConfig, false);
Klaro.initialize();
})
});
}
private getTitleTranslation(title: string) {
@@ -277,4 +306,12 @@ export class CookiesService {
});
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));
}
}

View File

@@ -21,9 +21,9 @@ import { AuthService } from '../../app/core/auth/auth.service';
import { Angulartics2RouterlessModule } from 'angulartics2/routerlessmodule';
import { SubmissionService } from '../../app/submission/submission.service';
import { StatisticsModule } from '../../app/statistics/statistics.module';
import { KlaroService } from '../../app/shared/cookies/klaro.service';
import { HardRedirectService } from '../../app/core/services/hard-redirect.service';
import { BrowserHardRedirectService } from '../../app/core/services/browser-hard-redirect.service';
import { CookiesService } from '../../app/shared/cookies/cookies.service';
export const REQ_KEY = makeStateKey<string>('req');
@@ -85,8 +85,8 @@ export function locationProvider(): Location {
useClass: ClientCookieService
},
{
provide: CookiesService,
useClass: CookiesService
provide: KlaroService,
useClass: KlaroService
},
{
provide: SubmissionService,

View File

@@ -6,5 +6,15 @@
},
"angularCompilerOptions": {
"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"
],
}