mirror of
https://github.com/DSpace/dspace-angular.git
synced 2025-10-07 10:04:11 +00:00
[UXP-10] dynamic recaptcha versions and modes
This commit is contained in:
@@ -48,8 +48,8 @@ import { BASE_THEME_NAME } from './shared/theme-support/theme.constants';
|
|||||||
import { BreadcrumbsService } from './breadcrumbs/breadcrumbs.service';
|
import { BreadcrumbsService } from './breadcrumbs/breadcrumbs.service';
|
||||||
import { IdleModalComponent } from './shared/idle-modal/idle-modal.component';
|
import { IdleModalComponent } from './shared/idle-modal/idle-modal.component';
|
||||||
import { getDefaultThemeConfig } from '../config/config.util';
|
import { getDefaultThemeConfig } from '../config/config.util';
|
||||||
import { AppConfig, APP_CONFIG } from 'src/config/app-config.interface';
|
import { AppConfig, APP_CONFIG } from '../config/app-config.interface';
|
||||||
import { ModalBeforeDismiss } from './shared/interfaces/modal-before-dismiss.interface';
|
import { GoogleRecaptchaService } from './core/google-recaptcha/google-recaptcha.service';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-app',
|
selector: 'ds-app',
|
||||||
@@ -110,6 +110,7 @@ export class AppComponent implements OnInit, AfterViewInit {
|
|||||||
private modalConfig: NgbModalConfig,
|
private modalConfig: NgbModalConfig,
|
||||||
@Optional() private cookiesService: KlaroService,
|
@Optional() private cookiesService: KlaroService,
|
||||||
@Optional() private googleAnalyticsService: GoogleAnalyticsService,
|
@Optional() private googleAnalyticsService: GoogleAnalyticsService,
|
||||||
|
@Optional() private googleRecaptchaService: GoogleRecaptchaService,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
if (!isEqual(environment, this.appConfig)) {
|
if (!isEqual(environment, this.appConfig)) {
|
||||||
|
@@ -1,4 +1,6 @@
|
|||||||
|
import { CommonModule } from '@angular/common';
|
||||||
import { NgModule } from '@angular/core';
|
import { NgModule } from '@angular/core';
|
||||||
|
import { GoogleRecaptchaComponent } from '../../shared/google-recaptcha/google-recaptcha.component';
|
||||||
import { SharedModule } from '../../shared/shared.module';
|
import { SharedModule } from '../../shared/shared.module';
|
||||||
|
|
||||||
import { GoogleRecaptchaService } from './google-recaptcha.service';
|
import { GoogleRecaptchaService } from './google-recaptcha.service';
|
||||||
@@ -7,11 +9,15 @@ const PROVIDERS = [
|
|||||||
GoogleRecaptchaService
|
GoogleRecaptchaService
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const COMPONENTS = [
|
||||||
|
GoogleRecaptchaComponent
|
||||||
|
];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [ SharedModule ],
|
imports: [ CommonModule ],
|
||||||
providers: [
|
providers: [...PROVIDERS],
|
||||||
...PROVIDERS
|
declarations: [...COMPONENTS],
|
||||||
]
|
exports: [...COMPONENTS]
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -5,8 +5,10 @@ import { isNotEmpty } from '../../shared/empty.util';
|
|||||||
import { DOCUMENT } from '@angular/common';
|
import { DOCUMENT } from '@angular/common';
|
||||||
import { ConfigurationDataService } from '../data/configuration-data.service';
|
import { ConfigurationDataService } from '../data/configuration-data.service';
|
||||||
import { RemoteData } from '../data/remote-data';
|
import { RemoteData } from '../data/remote-data';
|
||||||
import { map } from 'rxjs/operators';
|
import { map, take } from 'rxjs/operators';
|
||||||
import { combineLatest } from 'rxjs';
|
import { combineLatest, Observable, of } from 'rxjs';
|
||||||
|
|
||||||
|
export const CAPTCHA_COOKIE = '_GRECAPTCHA';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A GoogleRecaptchaService used to send action and get a token from REST
|
* A GoogleRecaptchaService used to send action and get a token from REST
|
||||||
@@ -18,7 +20,22 @@ export class GoogleRecaptchaService {
|
|||||||
/**
|
/**
|
||||||
* A Google Recaptcha site key
|
* A Google Recaptcha site key
|
||||||
*/
|
*/
|
||||||
captchaSiteKey: string;
|
captchaSiteKeyStr: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Google Recaptcha site key
|
||||||
|
*/
|
||||||
|
captchaSiteKey$: Observable<string>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Google Recaptcha mode
|
||||||
|
*/
|
||||||
|
captchaMode$: Observable<string> = of('invisible');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A Google Recaptcha version
|
||||||
|
*/
|
||||||
|
captchaVersion$: Observable<string>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(DOCUMENT) private _document: Document,
|
@Inject(DOCUMENT) private _document: Document,
|
||||||
@@ -27,20 +44,44 @@ export class GoogleRecaptchaService {
|
|||||||
) {
|
) {
|
||||||
this.renderer = rendererFactory.createRenderer(null, null);
|
this.renderer = rendererFactory.createRenderer(null, null);
|
||||||
const registrationVerification$ = this.configService.findByPropertyName('registration.verification.enabled').pipe(
|
const registrationVerification$ = this.configService.findByPropertyName('registration.verification.enabled').pipe(
|
||||||
|
take(1),
|
||||||
getFirstCompletedRemoteData(),
|
getFirstCompletedRemoteData(),
|
||||||
map((res: RemoteData<ConfigurationProperty>) => {
|
map((res: RemoteData<ConfigurationProperty>) => {
|
||||||
return res.hasSucceeded && res.payload && isNotEmpty(res.payload.values) && res.payload.values[0].toLowerCase() === 'true';
|
return res.hasSucceeded && res.payload && isNotEmpty(res.payload.values) && res.payload.values[0].toLowerCase() === 'true';
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
const recaptchaKey$ = this.configService.findByPropertyName('google.recaptcha.key.site').pipe(
|
const recaptchaKey$ = this.configService.findByPropertyName('google.recaptcha.key.site').pipe(
|
||||||
|
take(1),
|
||||||
getFirstCompletedRemoteData(),
|
getFirstCompletedRemoteData(),
|
||||||
);
|
);
|
||||||
combineLatest(registrationVerification$, recaptchaKey$).subscribe(([registrationVerification, recaptchaKey]) => {
|
const recaptchaVersion$ = this.configService.findByPropertyName('google.recaptcha.version').pipe(
|
||||||
|
take(1),
|
||||||
|
getFirstCompletedRemoteData(),
|
||||||
|
);
|
||||||
|
const recaptchaMode$ = this.configService.findByPropertyName('google.recaptcha.mode').pipe(
|
||||||
|
take(1),
|
||||||
|
getFirstCompletedRemoteData(),
|
||||||
|
);
|
||||||
|
combineLatest(registrationVerification$, recaptchaVersion$, recaptchaMode$, recaptchaKey$).subscribe(([registrationVerification, recaptchaVersion, recaptchaMode, recaptchaKey]) => {
|
||||||
if (registrationVerification) {
|
if (registrationVerification) {
|
||||||
if (recaptchaKey.hasSucceeded && isNotEmpty(recaptchaKey?.payload?.values[0])) {
|
if (recaptchaKey.hasSucceeded && isNotEmpty(recaptchaKey?.payload?.values[0])) {
|
||||||
this.captchaSiteKey = recaptchaKey?.payload?.values[0];
|
this.captchaSiteKeyStr = recaptchaKey?.payload?.values[0];
|
||||||
|
this.captchaSiteKey$ = of(recaptchaKey?.payload?.values[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
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]));
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -50,7 +91,22 @@ export class GoogleRecaptchaService {
|
|||||||
* @param action action is the process type in which used to protect multiple spam REST calls
|
* @param action action is the process type in which used to protect multiple spam REST calls
|
||||||
*/
|
*/
|
||||||
public async getRecaptchaToken (action) {
|
public async getRecaptchaToken (action) {
|
||||||
return await grecaptcha.execute(this.captchaSiteKey, {action: action});
|
return await grecaptcha.execute(this.captchaSiteKeyStr, {action: action});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an observable of string
|
||||||
|
*/
|
||||||
|
public async executeRecaptcha () {
|
||||||
|
return await 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();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@@ -24,6 +24,9 @@
|
|||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
{{MESSAGE_PREFIX + '.email.hint' |translate}}
|
{{MESSAGE_PREFIX + '.email.hint' |translate}}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-12" *ngIf="captchaVersion === 'v2'">
|
||||||
|
<ds-google-recaptcha [captchaMode]="captchaMode" (executeRecaptcha)="register($event)"></ds-google-recaptcha>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -32,5 +35,6 @@
|
|||||||
|
|
||||||
<button class="btn btn-primary"
|
<button class="btn btn-primary"
|
||||||
[disabled]="form.invalid"
|
[disabled]="form.invalid"
|
||||||
(click)="register()">{{MESSAGE_PREFIX + '.submit'| translate}}</button>
|
(click)="(captchaVersion === 'v2' && captchaMode === 'invisible') ? executeRecaptcha() : register()">
|
||||||
|
{{MESSAGE_PREFIX + '.submit'| translate}}</button>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
import { waitForAsync, ComponentFixture, TestBed, tick, fakeAsync } from '@angular/core/testing';
|
import { waitForAsync, ComponentFixture, TestBed, tick, fakeAsync } from '@angular/core/testing';
|
||||||
import { of as observableOf } from 'rxjs';
|
import { of as observableOf, of } from 'rxjs';
|
||||||
import { RestResponse } from '../core/cache/response.models';
|
import { RestResponse } from '../core/cache/response.models';
|
||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
@@ -31,9 +31,13 @@ describe('RegisterEmailComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const googleRecaptchaService = jasmine.createSpyObj('googleRecaptchaService', {
|
const googleRecaptchaService = jasmine.createSpyObj('googleRecaptchaService', {
|
||||||
getRecaptchaToken: Promise.resolve('googleRecaptchaToken')
|
getRecaptchaToken: Promise.resolve('googleRecaptchaToken'),
|
||||||
|
executeRecaptcha: Promise.resolve('googleRecaptchaToken'),
|
||||||
|
getRecaptchaTokenResponse: Promise.resolve('googleRecaptchaToken')
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const captchaVersion$ = of('v3');
|
||||||
|
const captchaMode$ = of('invisible');
|
||||||
const confResponse$ = createSuccessfulRemoteDataObject$({ values: ['true'] });
|
const confResponse$ = createSuccessfulRemoteDataObject$({ values: ['true'] });
|
||||||
const confResponseDisabled$ = createSuccessfulRemoteDataObject$({ values: ['false'] });
|
const confResponseDisabled$ = createSuccessfulRemoteDataObject$({ values: ['false'] });
|
||||||
|
|
||||||
@@ -63,6 +67,8 @@ describe('RegisterEmailComponent', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
fixture = TestBed.createComponent(RegisterEmailFormComponent);
|
fixture = TestBed.createComponent(RegisterEmailFormComponent);
|
||||||
comp = fixture.componentInstance;
|
comp = fixture.componentInstance;
|
||||||
|
googleRecaptchaService.captchaVersion$ = captchaVersion$;
|
||||||
|
googleRecaptchaService.captchaMode$ = captchaMode$;
|
||||||
configurationDataService.findByPropertyName.and.returnValues(confResponseDisabled$, confResponseDisabled$, confResponseDisabled$, confResponseDisabled$, confResponseDisabled$, confResponseDisabled$, confResponseDisabled$, confResponseDisabled$, confResponseDisabled$, confResponseDisabled$);
|
configurationDataService.findByPropertyName.and.returnValues(confResponseDisabled$, confResponseDisabled$, confResponseDisabled$, confResponseDisabled$, confResponseDisabled$, confResponseDisabled$, confResponseDisabled$, confResponseDisabled$, confResponseDisabled$, confResponseDisabled$);
|
||||||
|
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
@@ -109,6 +115,8 @@ describe('RegisterEmailComponent', () => {
|
|||||||
describe('register with google recaptcha', () => {
|
describe('register with google recaptcha', () => {
|
||||||
beforeEach(fakeAsync(() => {
|
beforeEach(fakeAsync(() => {
|
||||||
configurationDataService.findByPropertyName.and.returnValues(confResponse$, confResponse$, confResponse$, confResponse$, confResponse$, confResponse$, confResponse$, confResponse$, confResponse$, confResponse$);
|
configurationDataService.findByPropertyName.and.returnValues(confResponse$, confResponse$, confResponse$, confResponse$, confResponse$, confResponse$, confResponse$, confResponse$, confResponse$, confResponse$);
|
||||||
|
googleRecaptchaService.captchaVersion$ = captchaVersion$;
|
||||||
|
googleRecaptchaService.captchaMode$ = captchaMode$;
|
||||||
comp.ngOnInit();
|
comp.ngOnInit();
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
}));
|
}));
|
||||||
|
@@ -7,11 +7,12 @@ import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'
|
|||||||
import { Registration } from '../core/shared/registration.model';
|
import { Registration } from '../core/shared/registration.model';
|
||||||
import { RemoteData } from '../core/data/remote-data';
|
import { RemoteData } from '../core/data/remote-data';
|
||||||
import { ConfigurationDataService } from '../core/data/configuration-data.service';
|
import { ConfigurationDataService } from '../core/data/configuration-data.service';
|
||||||
import { getFirstCompletedRemoteData } from '../core/shared/operators';
|
import { getFirstCompletedRemoteData, getFirstSucceededRemoteDataPayload } from '../core/shared/operators';
|
||||||
import { ConfigurationProperty } from '../core/shared/configuration-property.model';
|
import { ConfigurationProperty } from '../core/shared/configuration-property.model';
|
||||||
import { isNotEmpty } from '../shared/empty.util';
|
import { isNotEmpty } from '../shared/empty.util';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
import { GoogleRecaptchaService } from '../core/google-recaptcha/google-recaptcha.service';
|
import { GoogleRecaptchaService } from '../core/google-recaptcha/google-recaptcha.service';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'ds-register-email-form',
|
selector: 'ds-register-email-form',
|
||||||
@@ -38,6 +39,18 @@ export class RegisterEmailFormComponent implements OnInit {
|
|||||||
*/
|
*/
|
||||||
registrationVerification = false;
|
registrationVerification = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* captcha version
|
||||||
|
*/
|
||||||
|
captchaVersion = 'v2';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* captcha mode
|
||||||
|
*/
|
||||||
|
captchaMode = 'checkbox';
|
||||||
|
|
||||||
|
recaptchaKey$: Observable<any>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private epersonRegistrationService: EpersonRegistrationService,
|
private epersonRegistrationService: EpersonRegistrationService,
|
||||||
private notificationService: NotificationsService,
|
private notificationService: NotificationsService,
|
||||||
@@ -45,7 +58,7 @@ export class RegisterEmailFormComponent implements OnInit {
|
|||||||
private router: Router,
|
private router: Router,
|
||||||
private formBuilder: FormBuilder,
|
private formBuilder: FormBuilder,
|
||||||
private configService: ConfigurationDataService,
|
private configService: ConfigurationDataService,
|
||||||
private googleRecaptchaService: GoogleRecaptchaService
|
public googleRecaptchaService: GoogleRecaptchaService
|
||||||
) {
|
) {
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -58,6 +71,15 @@ export class RegisterEmailFormComponent implements OnInit {
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
this.recaptchaKey$ = this.configService.findByPropertyName('google.recaptcha.key.site').pipe(
|
||||||
|
getFirstSucceededRemoteDataPayload(),
|
||||||
|
);
|
||||||
|
this.googleRecaptchaService.captchaVersion$.subscribe(res => {
|
||||||
|
this.captchaVersion = res;
|
||||||
|
});
|
||||||
|
this.googleRecaptchaService.captchaMode$.subscribe(res => {
|
||||||
|
this.captchaMode = res;
|
||||||
|
});
|
||||||
this.configService.findByPropertyName('registration.verification.enabled').pipe(
|
this.configService.findByPropertyName('registration.verification.enabled').pipe(
|
||||||
getFirstCompletedRemoteData(),
|
getFirstCompletedRemoteData(),
|
||||||
map((res: RemoteData<ConfigurationProperty>) => {
|
map((res: RemoteData<ConfigurationProperty>) => {
|
||||||
@@ -68,13 +90,27 @@ export class RegisterEmailFormComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* execute the captcha function for v2 invisible
|
||||||
|
*/
|
||||||
|
async executeRecaptcha() {
|
||||||
|
await this.googleRecaptchaService.executeRecaptcha();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register an email address
|
* Register an email address
|
||||||
*/
|
*/
|
||||||
async register() {
|
async register(tokenV2 = null) {
|
||||||
if (!this.form.invalid) {
|
if (!this.form.invalid) {
|
||||||
if (this.registrationVerification) {
|
if (this.registrationVerification) {
|
||||||
const token = await this.googleRecaptchaService.getRecaptchaToken('register_email');
|
let token;
|
||||||
|
if (this.captchaVersion === 'v3') {
|
||||||
|
token = await this.googleRecaptchaService.getRecaptchaToken('register_email');
|
||||||
|
} else if (this.captchaMode === 'checkbox') {
|
||||||
|
token = await this.googleRecaptchaService.getRecaptchaTokenResponse();
|
||||||
|
} else {
|
||||||
|
token = tokenV2;
|
||||||
|
}
|
||||||
if (isNotEmpty(token)) {
|
if (isNotEmpty(token)) {
|
||||||
this.registration(token);
|
this.registration(token);
|
||||||
} else {
|
} else {
|
||||||
|
@@ -2,13 +2,11 @@ import { NgModule } from '@angular/core';
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { SharedModule } from '../shared/shared.module';
|
import { SharedModule } from '../shared/shared.module';
|
||||||
import { RegisterEmailFormComponent } from './register-email-form.component';
|
import { RegisterEmailFormComponent } from './register-email-form.component';
|
||||||
import { GoogleRecaptchaModule } from '../core/google-recaptcha/google-recaptcha.module';
|
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
SharedModule,
|
SharedModule
|
||||||
GoogleRecaptchaModule
|
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
RegisterEmailFormComponent,
|
RegisterEmailFormComponent,
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
import { TOKENITEM } from '../../core/auth/models/auth-token-info.model';
|
import { TOKENITEM } from '../../core/auth/models/auth-token-info.model';
|
||||||
import { IMPERSONATING_COOKIE, REDIRECT_COOKIE } from '../../core/auth/auth.service';
|
import { IMPERSONATING_COOKIE, REDIRECT_COOKIE } from '../../core/auth/auth.service';
|
||||||
import { LANG_COOKIE } from '../../core/locale/locale.service';
|
import { LANG_COOKIE } from '../../core/locale/locale.service';
|
||||||
|
import { CAPTCHA_COOKIE } from 'src/app/core/google-recaptcha/google-recaptcha.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cookie for has_agreed_end_user
|
* Cookie for has_agreed_end_user
|
||||||
@@ -155,5 +156,14 @@ export const klaroConfiguration: any = {
|
|||||||
*/
|
*/
|
||||||
onlyOnce: true,
|
onlyOnce: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'google-recaptcha',
|
||||||
|
purposes: ['registration-password-recovery'],
|
||||||
|
required: true,
|
||||||
|
cookies: [
|
||||||
|
CAPTCHA_COOKIE
|
||||||
|
],
|
||||||
|
onlyOnce: true,
|
||||||
|
}
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
@@ -0,0 +1,4 @@
|
|||||||
|
<div class="g-recaptcha"
|
||||||
|
[attr.data-callback]="captchaMode === 'invisible' ? 'executeRecaptcha' : null"
|
||||||
|
[attr.data-sitekey]="(recaptchaKey$ | async)?.values[0]"
|
||||||
|
[attr.data-size]="captchaMode === 'invisible' ? 'invisible' : null"></div>
|
@@ -0,0 +1,50 @@
|
|||||||
|
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||||
|
import { By } from '@angular/platform-browser';
|
||||||
|
import { NativeWindowService } from '../../core/services/window.service';
|
||||||
|
|
||||||
|
import { ConfigurationDataService } from '../../core/data/configuration-data.service';
|
||||||
|
import { NativeWindowMockFactory } from '../mocks/mock-native-window-ref';
|
||||||
|
import { createSuccessfulRemoteDataObject$ } from '../remote-data.utils';
|
||||||
|
import { GoogleRecaptchaComponent } from './google-recaptcha.component';
|
||||||
|
|
||||||
|
describe('GoogleRecaptchaComponent', () => {
|
||||||
|
|
||||||
|
let component: GoogleRecaptchaComponent;
|
||||||
|
|
||||||
|
let fixture: ComponentFixture<GoogleRecaptchaComponent>;
|
||||||
|
|
||||||
|
|
||||||
|
const configurationDataService = jasmine.createSpyObj('configurationDataService', {
|
||||||
|
findByPropertyName: jasmine.createSpy('findByPropertyName')
|
||||||
|
});
|
||||||
|
|
||||||
|
const confResponse$ = createSuccessfulRemoteDataObject$({ values: ['valid-google-recaptcha-key'] });
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [ GoogleRecaptchaComponent ],
|
||||||
|
providers: [
|
||||||
|
{ provide: ConfigurationDataService, useValue: configurationDataService },
|
||||||
|
{ provide: NativeWindowService, useFactory: NativeWindowMockFactory },
|
||||||
|
]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(GoogleRecaptchaComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
configurationDataService.findByPropertyName.and.returnValues(confResponse$);
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should rendered google recaptcha.',() => {
|
||||||
|
const container = fixture.debugElement.query(By.css('.g-recaptcha'));
|
||||||
|
expect(container).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
@@ -0,0 +1,45 @@
|
|||||||
|
import { Component, EventEmitter, Inject, Input, OnInit, Output } from '@angular/core';
|
||||||
|
|
||||||
|
import { ConfigurationDataService } from '../../core/data/configuration-data.service';
|
||||||
|
import { getFirstSucceededRemoteDataPayload } from '../../core/shared/operators';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { NativeWindowRef, NativeWindowService } from 'src/app/core/services/window.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'ds-google-recaptcha',
|
||||||
|
templateUrl: './google-recaptcha.component.html',
|
||||||
|
styleUrls: ['./google-recaptcha.component.scss'],
|
||||||
|
})
|
||||||
|
export class GoogleRecaptchaComponent implements OnInit {
|
||||||
|
|
||||||
|
@Input() captchaMode: string;
|
||||||
|
/**
|
||||||
|
* An EventEmitter that's fired whenever the form is being submitted
|
||||||
|
*/
|
||||||
|
@Output() executeRecaptcha: EventEmitter<any> = new EventEmitter();
|
||||||
|
|
||||||
|
recaptchaKey$: Observable<any>;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
@Inject(NativeWindowService) private _window: NativeWindowRef,
|
||||||
|
private configService: ConfigurationDataService,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the google recaptcha site key
|
||||||
|
*/
|
||||||
|
ngOnInit() {
|
||||||
|
this.recaptchaKey$ = this.configService.findByPropertyName('google.recaptcha.key.site').pipe(
|
||||||
|
getFirstSucceededRemoteDataPayload(),
|
||||||
|
);
|
||||||
|
if (this.captchaMode === 'invisible') {
|
||||||
|
this._window.nativeWindow.executeRecaptcha = this.execute;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
execute = (event) => {
|
||||||
|
this.executeRecaptcha.emit(event);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@@ -293,6 +293,7 @@ import { BrowserOnlyPipe } from './utils/browser-only.pipe';
|
|||||||
import { ThemedLoadingComponent } from './loading/themed-loading.component';
|
import { ThemedLoadingComponent } from './loading/themed-loading.component';
|
||||||
import { PersonPageClaimButtonComponent } from './dso-page/person-page-claim-button/person-page-claim-button.component';
|
import { PersonPageClaimButtonComponent } from './dso-page/person-page-claim-button/person-page-claim-button.component';
|
||||||
import { SearchExportCsvComponent } from './search/search-export-csv/search-export-csv.component';
|
import { SearchExportCsvComponent } from './search/search-export-csv/search-export-csv.component';
|
||||||
|
import { GoogleRecaptchaModule } from '../core/google-recaptcha/google-recaptcha.module';
|
||||||
|
|
||||||
const MODULES = [
|
const MODULES = [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
@@ -313,7 +314,8 @@ const MODULES = [
|
|||||||
NouisliderModule,
|
NouisliderModule,
|
||||||
MomentModule,
|
MomentModule,
|
||||||
DragDropModule,
|
DragDropModule,
|
||||||
CdkTreeModule
|
CdkTreeModule,
|
||||||
|
GoogleRecaptchaModule
|
||||||
];
|
];
|
||||||
|
|
||||||
const ROOT_MODULES = [
|
const ROOT_MODULES = [
|
||||||
|
@@ -1262,10 +1262,27 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"cookies.consent.app.title.google-recaptcha": "Google reCaptcha",
|
||||||
|
|
||||||
|
"cookies.consent.app.description.google-recaptcha": "Allows us to track registration and password recovery data",
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"cookies.consent.purpose.functional": "Functional",
|
"cookies.consent.purpose.functional": "Functional",
|
||||||
|
|
||||||
"cookies.consent.purpose.statistical": "Statistical",
|
"cookies.consent.purpose.statistical": "Statistical",
|
||||||
|
|
||||||
|
"cookies.consent.purpose.registration-password-recovery": "Registration and Password recovery",
|
||||||
|
|
||||||
|
"cookies.consent.purpose.sharing": "Sharing",
|
||||||
|
|
||||||
|
"cris-layout.toggle.open": "Open section",
|
||||||
|
|
||||||
|
"cris-layout.toggle.close": "Close section",
|
||||||
|
|
||||||
|
"cris-layout.toggle.aria.open": "Expand {{sectionHeader}} section",
|
||||||
|
|
||||||
|
"cris-layout.toggle.aria.close": "Collapse {{sectionHeader}} section",
|
||||||
|
|
||||||
"curation-task.task.checklinks.label": "Check Links in Metadata",
|
"curation-task.task.checklinks.label": "Check Links in Metadata",
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user