diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 68c1bce6f3..e7033f51ba 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -15,7 +15,8 @@ import { ActivationEnd, NavigationCancel, NavigationEnd, - NavigationStart, ResolveEnd, + NavigationStart, + ResolveEnd, Router, } from '@angular/router'; @@ -48,8 +49,7 @@ import { BASE_THEME_NAME } from './shared/theme-support/theme.constants'; import { BreadcrumbsService } from './breadcrumbs/breadcrumbs.service'; import { IdleModalComponent } from './shared/idle-modal/idle-modal.component'; import { getDefaultThemeConfig } from '../config/config.util'; -import { AppConfig, APP_CONFIG } from '../config/app-config.interface'; -import { GoogleRecaptchaService } from './core/google-recaptcha/google-recaptcha.service'; +import { APP_CONFIG, AppConfig } from '../config/app-config.interface'; @Component({ selector: 'ds-app', @@ -110,7 +110,6 @@ export class AppComponent implements OnInit, AfterViewInit { private modalConfig: NgbModalConfig, @Optional() private cookiesService: KlaroService, @Optional() private googleAnalyticsService: GoogleAnalyticsService, - @Optional() private googleRecaptchaService: GoogleRecaptchaService, ) { if (!isEqual(environment, this.appConfig)) { diff --git a/src/app/core/google-recaptcha/google-recaptcha.service.ts b/src/app/core/google-recaptcha/google-recaptcha.service.ts index 4f602ef575..080ddfc19f 100644 --- a/src/app/core/google-recaptcha/google-recaptcha.service.ts +++ b/src/app/core/google-recaptcha/google-recaptcha.service.ts @@ -5,8 +5,8 @@ import { isNotEmpty } from '../../shared/empty.util'; import { DOCUMENT } from '@angular/common'; import { ConfigurationDataService } from '../data/configuration-data.service'; import { RemoteData } from '../data/remote-data'; -import { map, take } from 'rxjs/operators'; -import { combineLatest, Observable, of } from 'rxjs'; +import { map, switchMap, take } from 'rxjs/operators'; +import { BehaviorSubject, combineLatest, Observable, of } from 'rxjs'; import { CookieService } from '../services/cookie.service'; import { NativeWindowRef, NativeWindowService } from '../services/window.service'; @@ -20,25 +20,33 @@ export const CAPTCHA_NAME = 'google-recaptcha'; export class GoogleRecaptchaService { private renderer: Renderer2; - /** - * A Google Recaptcha site key - */ - captchaSiteKeyStr: string; - - /** - * A Google Recaptcha site key - */ - captchaSiteKey$: Observable; - - /** - * A Google Recaptcha mode - */ - captchaMode$: Observable = of('invisible'); /** * A Google Recaptcha version */ - captchaVersion$: Observable = of(''); + private captchaVersionSubject$ = new BehaviorSubject(null); + + /** + * The Google Recaptcha Key + */ + private captchaKeySubject$ = new BehaviorSubject(null); + + /** + * The Google Recaptcha mode + */ + private captchaModeSubject$ = new BehaviorSubject(null); + + captchaKey(): Observable { + return this.captchaKeySubject$.asObservable(); + } + + captchaMode(): Observable { + return this.captchaModeSubject$.asObservable(); + } + + captchaVersion(): Observable { + return this.captchaVersionSubject$.asObservable(); + } constructor( private cookieService: CookieService, @@ -66,36 +74,46 @@ export class GoogleRecaptchaService { } loadRecaptchaProperties() { - const recaptchaKey$ = this.configService.findByPropertyName('google.recaptcha.key.site').pipe( - take(1), + const recaptchaKeyRD$ = this.configService.findByPropertyName('google.recaptcha.key.site').pipe( getFirstCompletedRemoteData(), ); - const recaptchaVersion$ = this.configService.findByPropertyName('google.recaptcha.version').pipe( - take(1), + const recaptchaVersionRD$ = this.configService.findByPropertyName('google.recaptcha.version').pipe( getFirstCompletedRemoteData(), ); - const recaptchaMode$ = this.configService.findByPropertyName('google.recaptcha.mode').pipe( - take(1), + const recaptchaModeRD$ = this.configService.findByPropertyName('google.recaptcha.mode').pipe( getFirstCompletedRemoteData(), ); - combineLatest(recaptchaVersion$, recaptchaMode$, recaptchaKey$).subscribe(([recaptchaVersion, recaptchaMode, recaptchaKey]) => { - if (this.cookieService.get('klaro-anonymous') && this.cookieService.get('klaro-anonymous')[CAPTCHA_NAME]) { - if (recaptchaKey.hasSucceeded && isNotEmpty(recaptchaKey?.payload?.values[0])) { - this.captchaSiteKeyStr = recaptchaKey?.payload?.values[0]; - this.captchaSiteKey$ = of(recaptchaKey?.payload?.values[0]); + combineLatest([recaptchaVersionRD$, recaptchaModeRD$, recaptchaKeyRD$]).subscribe(([recaptchaVersionRD, recaptchaModeRD, recaptchaKeyRD]) => { + + if ( + this.cookieService.get('klaro-anonymous') && this.cookieService.get('klaro-anonymous')[CAPTCHA_NAME] && + recaptchaKeyRD.hasSucceeded && recaptchaVersionRD.hasSucceeded && + isNotEmpty(recaptchaVersionRD.payload?.values) && isNotEmpty(recaptchaKeyRD.payload?.values) + ) { + const key = recaptchaKeyRD.payload?.values[0]; + const version = recaptchaVersionRD.payload?.values[0]; + this.captchaKeySubject$.next(key); + this.captchaVersionSubject$.next(version); + + let captchaUrl; + switch (version) { + case 'v3': + if (recaptchaKeyRD.hasSucceeded && isNotEmpty(recaptchaKeyRD.payload?.values)) { + captchaUrl = this.buildCaptchaUrl(key); + this.captchaModeSubject$.next('invisible'); + } + break; + case 'v2': + if (recaptchaModeRD.hasSucceeded && isNotEmpty(recaptchaModeRD.payload?.values)) { + captchaUrl = 'https://www.google.com/recaptcha/api.js'; + this.captchaModeSubject$.next(recaptchaModeRD.payload?.values[0]); + } + break; + default: + // TODO handle error } - if (recaptchaVersion.hasSucceeded && isNotEmpty(recaptchaVersion?.payload?.values[0]) && recaptchaVersion?.payload?.values[0] === 'v3') { - this.captchaVersion$ = of('v3'); - if (recaptchaKey.hasSucceeded && isNotEmpty(recaptchaKey?.payload?.values[0])) { - this.loadScript(this.buildCaptchaUrl(recaptchaKey?.payload?.values[0])); - } - } else { - this.captchaVersion$ = of('v2'); - const captchaUrl = 'https://www.google.com/recaptcha/api.js'; - if (recaptchaMode.hasSucceeded && isNotEmpty(recaptchaMode?.payload?.values[0])) { - this.captchaMode$ = of(recaptchaMode?.payload?.values[0]); - this.loadScript(captchaUrl); - } + if (captchaUrl) { + this.loadScript(captchaUrl); } } }); @@ -105,23 +123,21 @@ export class GoogleRecaptchaService { * Returns an observable of string * @param action action is the process type in which used to protect multiple spam REST calls */ - public async getRecaptchaToken (action) { - return await grecaptcha.execute(this.captchaSiteKeyStr, {action: action}); + public getRecaptchaToken(action) { + return this.captchaKey().pipe( + switchMap((key) => grecaptcha.execute(key, {action: action})) + ); } /** * Returns an observable of string */ - public async executeRecaptcha () { - return await grecaptcha.execute(); + public executeRecaptcha() { + return of(grecaptcha.execute()); } - /** - * Returns an observable of string - * @param action action is the process type in which used to protect multiple spam REST calls - */ - public async getRecaptchaTokenResponse () { - return await grecaptcha.getResponse(); + public getRecaptchaTokenResponse () { + return grecaptcha.getResponse(); } /** diff --git a/src/app/register-email-form/register-email-form.component.html b/src/app/register-email-form/register-email-form.component.html index b10833c097..19912ec0a9 100644 --- a/src/app/register-email-form/register-email-form.component.html +++ b/src/app/register-email-form/register-email-form.component.html @@ -24,23 +24,23 @@
{{MESSAGE_PREFIX + '.email.hint' |translate}}
-
- +
+
- - - + {{ MESSAGE_PREFIX + '.submit' | translate }} diff --git a/src/app/register-email-form/register-email-form.component.ts b/src/app/register-email-form/register-email-form.component.ts index 08e172d5f1..084446eced 100644 --- a/src/app/register-email-form/register-email-form.component.ts +++ b/src/app/register-email-form/register-email-form.component.ts @@ -7,12 +7,12 @@ import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms' import { Registration } from '../core/shared/registration.model'; import { RemoteData } from '../core/data/remote-data'; import { ConfigurationDataService } from '../core/data/configuration-data.service'; -import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from '../core/shared/operators'; +import { getFirstSucceededRemoteDataPayload } from '../core/shared/operators'; import { ConfigurationProperty } from '../core/shared/configuration-property.model'; import { isNotEmpty } from '../shared/empty.util'; +import { combineLatest, Observable, of, switchMap } from 'rxjs'; import { map } from 'rxjs/operators'; import { GoogleRecaptchaService } from '../core/google-recaptcha/google-recaptcha.service'; -import { Observable } from 'rxjs'; @Component({ selector: 'ds-register-email-form', @@ -39,9 +39,16 @@ export class RegisterEmailFormComponent implements OnInit { */ registrationVerification = false; - recaptchaKey$: Observable; + captchaVersion(): Observable { + return this.googleRecaptchaService.captchaVersion(); + } - constructor( + captchaMode(): Observable { + return this.googleRecaptchaService.captchaMode(); + } + + +constructor( private epersonRegistrationService: EpersonRegistrationService, private notificationService: NotificationsService, private translateService: TranslateService, @@ -61,14 +68,9 @@ export class RegisterEmailFormComponent implements OnInit { ], }) }); - this.recaptchaKey$ = this.configService.findByPropertyName('google.recaptcha.key.site').pipe( - getFirstSucceededRemoteDataPayload(), - ); this.configService.findByPropertyName('registration.verification.enabled').pipe( - getFirstCompletedRemoteData(), - map((res: RemoteData) => { - return res.hasSucceeded && res.payload && isNotEmpty(res.payload.values) && res.payload.values[0].toLowerCase() === 'true'; - }) + getFirstSucceededRemoteDataPayload(), + map((res: ConfigurationProperty) => res?.values[0].toLowerCase() === 'true') ).subscribe((res: boolean) => { this.registrationVerification = res; }); @@ -77,38 +79,36 @@ export class RegisterEmailFormComponent implements OnInit { /** * execute the captcha function for v2 invisible */ - async executeRecaptcha() { - await this.googleRecaptchaService.executeRecaptcha(); + executeRecaptcha() { + console.log('executeRecaptcha'); + this.googleRecaptchaService.executeRecaptcha(); } /** * Register an email address */ - async register(tokenV2 = null) { + register(tokenV2 = null) { if (!this.form.invalid) { if (this.registrationVerification) { - let token; - let captchaVersion; - let captchaMode; - this.googleRecaptchaService.captchaVersion$.subscribe(res => { - captchaVersion = res; - }); - this.googleRecaptchaService.captchaMode$.subscribe(res => { - captchaMode = res; - }); - if (captchaVersion === 'v3') { - token = await this.googleRecaptchaService.getRecaptchaToken('register_email'); - } else if (captchaMode === 'checkbox') { - token = await this.googleRecaptchaService.getRecaptchaTokenResponse(); - } else { - token = tokenV2; - } - if (isNotEmpty(token)) { - this.registration(token); - } else { - this.notificationService.error(this.translateService.get(`${this.MESSAGE_PREFIX}.error.head`), - this.translateService.get(`${this.MESSAGE_PREFIX}.error.recaptcha`)); - } + combineLatest([this.captchaVersion(), this.captchaMode()]).pipe( + switchMap(([captchaVersion, captchaMode]) => { + if (captchaVersion === 'v3') { + return this.googleRecaptchaService.getRecaptchaToken('register_email'); + } else if (captchaMode === 'checkbox') { + return this.googleRecaptchaService.getRecaptchaTokenResponse(); + } else { + return of(tokenV2); + } + }), + ).subscribe((token) => { + if (isNotEmpty(token)) { + this.registration(token); + } else { + this.notificationService.error(this.translateService.get(`${this.MESSAGE_PREFIX}.error.head`), + this.translateService.get(`${this.MESSAGE_PREFIX}.error.recaptcha`)); + } + } + ); } else { this.registration(); } @@ -119,12 +119,9 @@ export class RegisterEmailFormComponent implements OnInit { * Registration of an email address */ registration(captchaToken = null) { - let registerEmail$; - if (captchaToken) { - registerEmail$ = this.epersonRegistrationService.registerEmail(this.email.value, captchaToken); - } else { - registerEmail$ = this.epersonRegistrationService.registerEmail(this.email.value); - } + let registerEmail$ = captchaToken ? + this.epersonRegistrationService.registerEmail(this.email.value, captchaToken) : + this.epersonRegistrationService.registerEmail(this.email.value); registerEmail$.subscribe((response: RemoteData) => { if (response.hasSucceeded) { this.notificationService.success(this.translateService.get(`${this.MESSAGE_PREFIX}.success.head`),