forked from EVOgeek/Vmeda.Online
		
	MOBILE-3565 login: Implement signup page
This commit is contained in:
		
							parent
							
								
									232669855a
								
							
						
					
					
						commit
						a6467f5073
					
				| @ -51,6 +51,10 @@ const routes: Routes = [ | ||||
|         path: 'sitepolicy', | ||||
|         loadChildren: () => import('./pages/site-policy/site-policy.module').then( m => m.CoreLoginSitePolicyPageModule), | ||||
|     }, | ||||
|     { | ||||
|         path: 'emailsignup', | ||||
|         loadChildren: () => import('./pages/email-signup/email-signup.module').then( m => m.CoreLoginEmailSignupPageModule), | ||||
|     }, | ||||
| ]; | ||||
| 
 | ||||
| @NgModule({ | ||||
|  | ||||
| @ -33,7 +33,10 @@ | ||||
|             </ion-item> | ||||
|             <ion-item *ngIf="siteChecked && !isBrowserSSO" class="ion-margin-bottom"> | ||||
|                 <core-show-password [name]="'password'"> | ||||
|                     <ion-input class="core-ioninput-password" name="password" type="password" placeholder="{{ 'core.login.password' | translate }}" formControlName="password" core-show-password [clearOnEdit]="false"></ion-input> | ||||
|                     <ion-input name="password" type="password" placeholder="{{ 'core.login.password' | translate }}" | ||||
|                         formControlName="password" [clearOnEdit]="false" autocomplete="current-password" enterkeyhint="go" | ||||
|                         required="true"> | ||||
|                     </ion-input> | ||||
|                 </core-show-password> | ||||
|             </ion-item> | ||||
|             <ion-button expand="block" type="submit" [disabled]="siteChecked && !isBrowserSSO && !credForm.valid" class="ion-margin core-login-login-button">{{ 'core.login.loginbutton' | translate }}</ion-button> | ||||
| @ -72,7 +75,7 @@ | ||||
|             <ion-item class="ion-text-wrap" lines="none" *ngIf="authInstructions"> | ||||
|                 <ion-label><p><core-format-text [text]="authInstructions" [filter]="false"></core-format-text></p></ion-label> | ||||
|             </ion-item> | ||||
|             <ion-button expand="block" class="ion-margin" color="light" (onClick)="signup()"> | ||||
|             <ion-button expand="block" class="ion-margin" color="light" router-direction="forward" routerLink="/login/emailsignup" [queryParams]="{siteUrl: siteUrl}"> | ||||
|                 {{ 'core.login.startsignup' | translate }} | ||||
|             </ion-button> | ||||
|         </ion-list> | ||||
|  | ||||
| @ -279,13 +279,6 @@ export class CoreLoginCredentialsPage implements OnInit, OnDestroy { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Signup button was clicked. | ||||
|      */ | ||||
|     signup(): void { | ||||
|         // @todo Go to signup.
 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Show instructions and scan QR code. | ||||
|      */ | ||||
|  | ||||
							
								
								
									
										238
									
								
								src/app/core/login/pages/email-signup/email-signup.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										238
									
								
								src/app/core/login/pages/email-signup/email-signup.html
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,238 @@ | ||||
| <ion-header> | ||||
|     <ion-toolbar> | ||||
|         <ion-buttons slot="start"> | ||||
|             <ion-back-button [attr.aria-label]="'core.back' | translate"></ion-back-button> | ||||
|         </ion-buttons> | ||||
| 
 | ||||
|         <ion-title>{{ 'core.login.newaccount' | translate }}</ion-title> | ||||
| 
 | ||||
|         <ion-buttons slot="end"> | ||||
|             <ion-button *ngIf="authInstructions" (click)="showAuthInstructions()" | ||||
|                 [attr.aria-label]="'core.login.instructions' | translate"> | ||||
| 
 | ||||
|                 <ion-icon slot="icon-only" name="far-question-circle"></ion-icon> | ||||
|             </ion-button> | ||||
|         </ion-buttons> | ||||
|     </ion-toolbar> | ||||
| </ion-header> | ||||
| <ion-content> | ||||
|     <ion-refresher slot="fixed" [disabled]="!settingsLoaded || isMinor" (ionRefresh)="refreshSettings($event)"> | ||||
|         <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content> | ||||
|     </ion-refresher> | ||||
| 
 | ||||
|     <core-loading [hideUntil]="settingsLoaded" *ngIf="!isMinor"> | ||||
| 
 | ||||
|         <!-- Site has an unsupported required field. --> | ||||
|         <ion-list *ngIf="!allRequiredSupported"> | ||||
|             <ion-item class="ion-text-wrap"> | ||||
|                 <ion-label> | ||||
|                     {{ 'core.login.signuprequiredfieldnotsupported' | translate }} | ||||
|                 </ion-label> | ||||
|             </ion-item> | ||||
|             <ion-button expand="block" class="ion-margin" [href]="signupUrl" core-link autoLogin="no"> | ||||
|                 {{ 'core.openinbrowser' | translate }} | ||||
|             </ion-button> | ||||
|         </ion-list> | ||||
| 
 | ||||
|         <!-- Age verification. --> | ||||
|         <form ion-list *ngIf="allRequiredSupported && settingsLoaded && settings && ageDigitalConsentVerification" | ||||
|             [formGroup]="ageVerificationForm" (ngSubmit)="verifyAge($event)" #ageForm> | ||||
| 
 | ||||
|             <ion-item-divider class="ion-text-wrap"> | ||||
|                 <ion-label><h3>{{ 'core.agelocationverification' | translate }}</h3></ion-label> | ||||
|             </ion-item-divider> | ||||
| 
 | ||||
|             <ion-item class="ion-text-wrap"> | ||||
|                 <ion-label position="stacked"> | ||||
|                     <span core-mark-required="true">{{ 'core.whatisyourage' | translate }}</span> | ||||
|                 </ion-label> | ||||
|                 <ion-input type="number" name="age" placeholder="0" formControlName="age" autocapitalize="none" autocorrect="off"> | ||||
|                 </ion-input> | ||||
|             </ion-item> | ||||
| 
 | ||||
|             <ion-item class="ion-text-wrap"> | ||||
|                 <ion-label position="stacked"> | ||||
|                     <span core-mark-required="true">{{ 'core.wheredoyoulive' | translate }}</span> | ||||
|                 </ion-label> | ||||
|                 <ion-select name="country" formControlName="country" [placeholder]="'core.login.selectacountry' | translate"> | ||||
|                     <ion-select-option value="">{{ 'core.login.selectacountry' | translate }}</ion-select-option> | ||||
|                     <ion-select-option *ngFor="let country of countries" [value]="country.code">{{country.name}}</ion-select-option> | ||||
|                 </ion-select> | ||||
|             </ion-item> | ||||
| 
 | ||||
|             <!-- Submit button. --> | ||||
|             <ion-button expand="block" class="ion-margin" type="submit" [disabled]="!ageVerificationForm.valid"> | ||||
|                 {{ 'core.proceed' | translate }} | ||||
|             </ion-button> | ||||
| 
 | ||||
|             <ion-item class="ion-text-wrap"> | ||||
|                 <ion-label> | ||||
|                     <p class="item-heading">{{ 'core.whyisthisrequired' | translate }}</p> | ||||
|                     <p>{{ 'core.explanationdigitalminor' | translate }}</p> | ||||
|                 </ion-label> | ||||
|             </ion-item> | ||||
|         </form> | ||||
| 
 | ||||
|         <!-- Signup form. --> | ||||
|         <form ion-list *ngIf="allRequiredSupported && settingsLoaded && settings && !ageDigitalConsentVerification" | ||||
|             [formGroup]="signupForm" (ngSubmit)="create($event)"  #signupFormEl> | ||||
| 
 | ||||
|             <ion-item class="ion-text-wrap ion-text-center"> | ||||
|                 <ion-label> | ||||
|                     <!-- If no sitename show big siteurl. --> | ||||
|                     <p *ngIf="!siteName" class="ion-padding item-heading">{{siteUrl}}</p> | ||||
|                     <!-- If sitename, show big sitename and small siteurl. --> | ||||
|                     <p *ngIf="siteName" class="ion-padding item-heading"> | ||||
|                         <core-format-text [text]="siteName" [filter]="false"></core-format-text> | ||||
|                     </p> | ||||
|                     <p *ngIf="siteName">{{siteUrl}}</p> | ||||
|                 </ion-label> | ||||
|             </ion-item> | ||||
| 
 | ||||
|             <!-- Username and password. --> | ||||
|             <ion-item-divider class="ion-text-wrap"> | ||||
|                 <ion-label>{{ 'core.login.createuserandpass' | translate }}</ion-label> | ||||
|             </ion-item-divider> | ||||
|             <ion-item class="ion-text-wrap"> | ||||
|                 <ion-label position="stacked"> | ||||
|                     <span core-mark-required="true">{{ 'core.login.username' | translate }}</span> | ||||
|                 </ion-label> | ||||
|                 <ion-input type="text" name="username" placeholder="{{ 'core.login.username' | translate }}" | ||||
|                     formControlName="username" autocapitalize="none" autocorrect="off"> | ||||
|                 </ion-input> | ||||
|                 <core-input-errors [control]="signupForm.controls.username" [errorMessages]="usernameErrors"></core-input-errors> | ||||
|             </ion-item> | ||||
|             <ion-item class="ion-text-wrap"> | ||||
|                 <ion-label position="stacked"> | ||||
|                     <span core-mark-required="true">{{ 'core.login.password' | translate }}</span> | ||||
|                 </ion-label> | ||||
|                 <core-show-password [name]="'password'"> | ||||
|                     <ion-input name="password" type="password" placeholder="{{ 'core.login.password' | translate }}" | ||||
|                         formControlName="password" [clearOnEdit]="false" autocomplete="new-password" required="true"> | ||||
|                     </ion-input> | ||||
|                 </core-show-password> | ||||
|                 <p *ngIf="settings.passwordpolicy" class="core-input-footnote"> | ||||
|                     {{settings.passwordpolicy}} | ||||
|                 </p> | ||||
|                 <core-input-errors [control]="signupForm.controls.password" [errorMessages]="passwordErrors"></core-input-errors> | ||||
|             </ion-item> | ||||
| 
 | ||||
|             <!-- More details. --> | ||||
|             <ion-item-divider class="ion-text-wrap"> | ||||
|                 <ion-label> | ||||
|                     {{ 'core.login.supplyinfo' | translate }} | ||||
|                 </ion-label> | ||||
|             </ion-item-divider> | ||||
|             <ion-item class="ion-text-wrap"> | ||||
|                 <ion-label position="stacked"> | ||||
|                     <span core-mark-required="true">{{ 'core.user.email' | translate }}</span> | ||||
|                 </ion-label> | ||||
|                 <ion-input type="email" name="email" placeholder="{{ 'core.user.email' | translate }}" formControlName="email" | ||||
|                     autocapitalize="none" autocorrect="off"> | ||||
|                 </ion-input> | ||||
|                 <core-input-errors [control]="signupForm.controls.email" [errorMessages]="emailErrors"></core-input-errors> | ||||
|             </ion-item> | ||||
|             <ion-item class="ion-text-wrap"> | ||||
|                 <ion-label position="stacked"> | ||||
|                     <span core-mark-required="true">{{ 'core.user.emailagain' | translate }}</span> | ||||
|                 </ion-label> | ||||
|                 <ion-input type="email" name="email2" placeholder="{{ 'core.user.emailagain' | translate }}" autocapitalize="none" | ||||
|                     formControlName="email2" autocorrect="off" [pattern]="escapeMail(signupForm.controls.email.value)"> | ||||
|                 </ion-input> | ||||
|                 <core-input-errors [control]="signupForm.controls.email2" [errorMessages]="email2Errors"></core-input-errors> | ||||
|             </ion-item> | ||||
|             <ion-item *ngFor="let nameField of settings.namefields" class="ion-text-wrap"> | ||||
|                 <ion-label position="stacked"> | ||||
|                     <span core-mark-required="true">{{ 'core.user.' + nameField | translate }}</span> | ||||
|                 </ion-label> | ||||
|                 <ion-input type="text" name="nameField" placeholder="{{ 'core.user.' + nameField | translate }}" | ||||
|                     formControlName="{{nameField}}" autocorrect="off"> | ||||
|                 </ion-input> | ||||
|                 <core-input-errors [control]="signupForm.controls[nameField]" [errorMessages]="namefieldsErrors[nameField]"> | ||||
|                 </core-input-errors> | ||||
|             </ion-item> | ||||
|             <ion-item class="ion-text-wrap"> | ||||
|                 <ion-label position="stacked">{{ 'core.user.city' | translate }}</ion-label> | ||||
|                 <ion-input type="text" name="city" placeholder="{{ 'core.user.city' | translate }}" formControlName="city" | ||||
|                     autocorrect="off"> | ||||
|                 </ion-input> | ||||
|             </ion-item> | ||||
|             <ion-item class="ion-text-wrap"> | ||||
|                 <ion-label position="stacked" id="core-login-signup-country">{{ 'core.user.country' | translate }}</ion-label> | ||||
|                 <ion-select name="country" formControlName="country" aria-labelledby="core-login-signup-country" | ||||
|                     [placeholder]="'core.login.selectacountry' | translate"> | ||||
| 
 | ||||
|                     <ion-select-option value="">{{ 'core.login.selectacountry' | translate }}</ion-select-option> | ||||
|                     <ion-select-option *ngFor="let country of countries" [value]="country.code">{{country.name}}</ion-select-option> | ||||
|                 </ion-select> | ||||
|             </ion-item> | ||||
| 
 | ||||
|             <!-- Other categories. --> | ||||
|             <ng-container *ngFor="let category of categories"> | ||||
|                 <ion-item-divider class="ion-text-wrap"> | ||||
|                     <ion-label>{{ category.name }}</ion-label> | ||||
|                 </ion-item-divider> | ||||
|                 <!-- @todo <core-user-profile-field *ngFor="let field of category.fields" [field]="field" edit="true" signup="true" | ||||
|                     registerAuth="email" [form]="signupForm"></core-user-profile-field> --> | ||||
|             </ng-container> | ||||
| 
 | ||||
|             <!-- ReCAPTCHA --> | ||||
|             <ng-container *ngIf="settings.recaptchapublickey"> | ||||
|                 <ion-item-divider class="ion-text-wrap"> | ||||
|                     <ion-label> | ||||
|                         <span [core-mark-required]="true">{{ 'core.login.security_question' | translate }}</span> | ||||
|                     </ion-label> | ||||
|                 </ion-item-divider> | ||||
|                 <core-recaptcha [publicKey]="settings.recaptchapublickey" [model]="captcha" [siteUrl]="siteUrl"></core-recaptcha> | ||||
|             </ng-container> | ||||
| 
 | ||||
|             <!-- Site policy (if any). --> | ||||
|             <ng-container *ngIf="settings.sitepolicy"> | ||||
|                 <ion-item-divider class="ion-text-wrap"> | ||||
|                     <ion-label>{{ 'core.login.policyagreement' | translate }}</ion-label> | ||||
|                 </ion-item-divider> | ||||
|                 <ion-item class="ion-text-wrap"> | ||||
|                     <ion-label> | ||||
|                         <a [href]="settings.sitepolicy" core-link capture="false"> | ||||
|                             {{ 'core.login.policyagreementclick' | translate }} | ||||
|                         </a> | ||||
|                     </ion-label> | ||||
|                 </ion-item> | ||||
|                 <ion-item class="ion-text-wrap"> | ||||
|                     <ion-label> | ||||
|                         <span [core-mark-required]="true">{{ 'core.login.policyaccept' | translate }}</span> | ||||
|                         <core-input-errors [control]="signupForm.controls.policyagreed" [errorMessages]="policyErrors"> | ||||
|                         </core-input-errors> | ||||
|                     </ion-label> | ||||
|                     <ion-checkbox slot="end" name="policyagreed" formControlName="policyagreed"></ion-checkbox> | ||||
|                 </ion-item> | ||||
|             </ng-container> | ||||
| 
 | ||||
|             <!-- Submit button. --> | ||||
|             <ion-button expand="block" class="ion-margin" type="submit">{{ 'core.login.createaccount' | translate }}</ion-button> | ||||
|             <!-- Remove this once Ionic fixes this bug: https://github.com/ionic-team/ionic-framework/issues/19368 --> | ||||
|             <input type="submit" class="core-submit-hidden-enter" /> | ||||
|         </form> | ||||
|     </core-loading> | ||||
| 
 | ||||
|     <ion-list *ngIf="allRequiredSupported && isMinor"> | ||||
|         <ion-item-divider class="ion-text-wrap"> | ||||
|             <ion-label> | ||||
|                 <p *ngIf="siteName" class="item-heading ion-padding"> | ||||
|                     <core-format-text [text]="siteName" [filter]="false"></core-format-text> | ||||
|                 </p> | ||||
|             </ion-label> | ||||
|         </ion-item-divider> | ||||
|         <ion-item class="ion-text-wrap" lines="none"> | ||||
|             <ion-label> | ||||
|                 <p class="item-heading">{{ 'core.considereddigitalminor' | translate }}</p> | ||||
|                 <p>{{ 'core.digitalminor_desc' | translate }}</p> | ||||
|                 <p *ngIf="supportName">{{ supportName }}</p> | ||||
|                 <p *ngIf="supportEmail">{{ supportEmail }}</p> | ||||
|             </ion-label> | ||||
|         </ion-item> | ||||
|         <ion-button *ngIf="!supportName && !supportEmail" expand="block" class="ion-margin" (click)="showContactOnSite()"> | ||||
|             {{ 'core.openinbrowser' | translate }} | ||||
|         </ion-button> | ||||
|     </ion-list> | ||||
| </ion-content> | ||||
							
								
								
									
										50
									
								
								src/app/core/login/pages/email-signup/email-signup.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								src/app/core/login/pages/email-signup/email-signup.module.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,50 @@ | ||||
| // (C) Copyright 2015 Moodle Pty Ltd.
 | ||||
| //
 | ||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||
| // you may not use this file except in compliance with the License.
 | ||||
| // You may obtain a copy of the License at
 | ||||
| //
 | ||||
| //     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| //
 | ||||
| // Unless required by applicable law or agreed to in writing, software
 | ||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { NgModule } from '@angular/core'; | ||||
| import { CommonModule } from '@angular/common'; | ||||
| import { FormsModule, ReactiveFormsModule } from '@angular/forms'; | ||||
| import { RouterModule, Routes } from '@angular/router'; | ||||
| import { IonicModule } from '@ionic/angular'; | ||||
| import { TranslateModule } from '@ngx-translate/core'; | ||||
| 
 | ||||
| import { CoreComponentsModule } from '@components/components.module'; | ||||
| import { CoreDirectivesModule } from '@directives/directives.module'; | ||||
| 
 | ||||
| import { CoreLoginEmailSignupPage } from './email-signup.page'; | ||||
| 
 | ||||
| const routes: Routes = [ | ||||
|     { | ||||
|         path: '', | ||||
|         component: CoreLoginEmailSignupPage, | ||||
|     }, | ||||
| ]; | ||||
| 
 | ||||
| @NgModule({ | ||||
|     imports: [ | ||||
|         RouterModule.forChild(routes), | ||||
|         CommonModule, | ||||
|         IonicModule, | ||||
|         TranslateModule.forChild(), | ||||
|         FormsModule, | ||||
|         ReactiveFormsModule, | ||||
|         CoreComponentsModule, | ||||
|         CoreDirectivesModule, | ||||
|     ], | ||||
|     declarations: [ | ||||
|         CoreLoginEmailSignupPage, | ||||
|     ], | ||||
|     exports: [RouterModule], | ||||
| }) | ||||
| export class CoreLoginEmailSignupPageModule {} | ||||
							
								
								
									
										419
									
								
								src/app/core/login/pages/email-signup/email-signup.page.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										419
									
								
								src/app/core/login/pages/email-signup/email-signup.page.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,419 @@ | ||||
| // (C) Copyright 2015 Moodle Pty Ltd.
 | ||||
| //
 | ||||
| // Licensed under the Apache License, Version 2.0 (the "License");
 | ||||
| // you may not use this file except in compliance with the License.
 | ||||
| // You may obtain a copy of the License at
 | ||||
| //
 | ||||
| //     http://www.apache.org/licenses/LICENSE-2.0
 | ||||
| //
 | ||||
| // Unless required by applicable law or agreed to in writing, software
 | ||||
| // distributed under the License is distributed on an "AS IS" BASIS,
 | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | ||||
| // See the License for the specific language governing permissions and
 | ||||
| // limitations under the License.
 | ||||
| 
 | ||||
| import { Component, ViewChild, ElementRef, OnInit } from '@angular/core'; | ||||
| import { FormBuilder, FormGroup, Validators, FormControl } from '@angular/forms'; | ||||
| import { ActivatedRoute } from '@angular/router'; | ||||
| import { NavController, IonContent, IonRefresher } from '@ionic/angular'; | ||||
| 
 | ||||
| import { CoreSites } from '@services/sites'; | ||||
| import { CoreDomUtils } from '@services/utils/dom'; | ||||
| import { CoreTextUtils } from '@services/utils/text'; | ||||
| import { CoreCountry, CoreUtils } from '@services/utils/utils'; | ||||
| import { CoreWS, CoreWSExternalWarning } from '@services/ws'; | ||||
| import { AuthEmailSignupProfileFieldsCategory, AuthEmailSignupSettings, CoreLoginHelper } from '@core/login/services/helper'; | ||||
| import { CoreConstants } from '@core/constants'; | ||||
| import { Translate } from '@singletons/core.singletons'; | ||||
| import { CoreSitePublicConfigResponse } from '@classes/site'; | ||||
| 
 | ||||
| /** | ||||
|  * Page to signup using email. | ||||
|  */ | ||||
| @Component({ | ||||
|     selector: 'page-core-login-email-signup', | ||||
|     templateUrl: 'email-signup.html', | ||||
|     styleUrls: ['../../login.scss'], | ||||
| }) | ||||
| export class CoreLoginEmailSignupPage implements OnInit { | ||||
| 
 | ||||
|     @ViewChild(IonContent) content?: IonContent; | ||||
|     @ViewChild('ageForm') ageFormElement?: ElementRef; | ||||
|     @ViewChild('signupFormEl') signupFormElement?: ElementRef; | ||||
| 
 | ||||
|     signupForm: FormGroup; | ||||
|     siteUrl!: string; | ||||
|     siteConfig?: CoreSitePublicConfigResponse; | ||||
|     siteName?: string; | ||||
|     authInstructions?: string; | ||||
|     settings?: AuthEmailSignupSettings; | ||||
|     countries?: CoreCountry[]; | ||||
|     categories?: AuthEmailSignupProfileFieldsCategory[]; | ||||
|     settingsLoaded = false; | ||||
|     allRequiredSupported = true; | ||||
|     signupUrl?: string; | ||||
|     captcha = { | ||||
|         recaptcharesponse: '', | ||||
|     }; | ||||
| 
 | ||||
|     // Data for age verification.
 | ||||
|     ageVerificationForm: FormGroup; | ||||
|     countryControl: FormControl; | ||||
|     signUpCountryControl?: FormControl; | ||||
|     isMinor = false; // Whether the user is minor age.
 | ||||
|     ageDigitalConsentVerification?: boolean; // Whether the age verification is enabled.
 | ||||
|     supportName?: string; | ||||
|     supportEmail?: string; | ||||
| 
 | ||||
|     // Validation errors.
 | ||||
|     usernameErrors: Record<string, string>; | ||||
|     passwordErrors: Record<string, string>; | ||||
|     emailErrors: Record<string, string>; | ||||
|     email2Errors: Record<string, string>; | ||||
|     policyErrors: Record<string, string>; | ||||
|     namefieldsErrors?: Record<string, Record<string, string>>; | ||||
| 
 | ||||
|     constructor( | ||||
|         protected navCtrl: NavController, | ||||
|         protected fb: FormBuilder, | ||||
|         protected route: ActivatedRoute, | ||||
|     ) { | ||||
|         // Create the ageVerificationForm.
 | ||||
|         this.ageVerificationForm = this.fb.group({ | ||||
|             age: ['', Validators.required], | ||||
|         }); | ||||
|         this.countryControl = this.fb.control('', Validators.required); | ||||
|         this.ageVerificationForm.addControl('country', this.countryControl); | ||||
| 
 | ||||
|         // Create the signupForm with the basic controls. More controls will be added later.
 | ||||
|         this.signupForm = this.fb.group({ | ||||
|             username: ['', Validators.required], | ||||
|             password: ['', Validators.required], | ||||
|             email: ['', Validators.compose([Validators.required, Validators.email])], | ||||
|             email2: ['', Validators.compose([Validators.required, Validators.email])], | ||||
|         }); | ||||
| 
 | ||||
|         // Setup validation errors.
 | ||||
|         this.usernameErrors = CoreLoginHelper.instance.getErrorMessages('core.login.usernamerequired'); | ||||
|         this.passwordErrors = CoreLoginHelper.instance.getErrorMessages('core.login.passwordrequired'); | ||||
|         this.emailErrors = CoreLoginHelper.instance.getErrorMessages('core.login.missingemail'); | ||||
|         this.policyErrors = CoreLoginHelper.instance.getErrorMessages('core.login.policyagree'); | ||||
|         this.email2Errors = CoreLoginHelper.instance.getErrorMessages( | ||||
|             'core.login.missingemail', | ||||
|             undefined, | ||||
|             'core.login.emailnotmatch', | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Component initialized. | ||||
|      */ | ||||
|     ngOnInit(): void { | ||||
|         this.siteUrl = this.route.snapshot.queryParams['siteUrl']; | ||||
| 
 | ||||
|         // Fetch the data.
 | ||||
|         this.fetchData().finally(() => { | ||||
|             this.settingsLoaded = true; | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Complete the FormGroup using the settings received from server. | ||||
|      */ | ||||
|     protected completeFormGroup(): void { | ||||
|         this.signupForm.addControl('city', this.fb.control(this.settings?.defaultcity || '')); | ||||
|         this.signUpCountryControl = this.fb.control(this.settings?.country || ''); | ||||
|         this.signupForm.addControl('country', this.signUpCountryControl); | ||||
| 
 | ||||
|         // Add the name fields.
 | ||||
|         for (const i in this.settings?.namefields) { | ||||
|             this.signupForm.addControl(this.settings?.namefields[i], this.fb.control('', Validators.required)); | ||||
|         } | ||||
| 
 | ||||
|         if (this.settings?.sitepolicy) { | ||||
|             this.signupForm.addControl('policyagreed', this.fb.control(false, Validators.requiredTrue)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Fetch the required data from the server. | ||||
|      * | ||||
|      * @return Promise resolved when done. | ||||
|      */ | ||||
|     protected async fetchData(): Promise<void> { | ||||
|         try { | ||||
|             // Get site config.
 | ||||
|             this.siteConfig = await CoreSites.instance.getSitePublicConfig(this.siteUrl); | ||||
|             this.signupUrl = CoreTextUtils.instance.concatenatePaths(this.siteConfig.httpswwwroot, 'login/signup.php'); | ||||
| 
 | ||||
|             if (this.treatSiteConfig()) { | ||||
|                 // Check content verification.
 | ||||
|                 if (typeof this.ageDigitalConsentVerification == 'undefined') { | ||||
| 
 | ||||
|                     const result = await CoreUtils.instance.ignoreErrors( | ||||
|                         CoreWS.instance.callAjax<IsAgeVerificationEnabledResponse>( | ||||
|                             'core_auth_is_age_digital_consent_verification_enabled', | ||||
|                             {}, | ||||
|                             { siteUrl: this.siteUrl }, | ||||
|                         ), | ||||
|                     ); | ||||
| 
 | ||||
|                     this.ageDigitalConsentVerification = !!result?.status; | ||||
|                 } | ||||
| 
 | ||||
|                 await this.getSignupSettings(); | ||||
|             } | ||||
| 
 | ||||
|             this.completeFormGroup(); | ||||
|         } catch (error) { | ||||
|             if (this.allRequiredSupported) { | ||||
|                 CoreDomUtils.instance.showErrorModal(error); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Get signup settings from server. | ||||
|      * | ||||
|      * @return Promise resolved when done. | ||||
|      */ | ||||
|     protected async getSignupSettings(): Promise<void> { | ||||
|         this.settings = await CoreWS.instance.callAjax<AuthEmailSignupSettings>( | ||||
|             'auth_email_get_signup_settings', | ||||
|             {}, | ||||
|             { siteUrl: this.siteUrl }, | ||||
|         ); | ||||
| 
 | ||||
|         // @todo userProfileFieldDelegate
 | ||||
| 
 | ||||
|         this.categories = CoreLoginHelper.instance.formatProfileFieldsForSignup(this.settings.profilefields); | ||||
| 
 | ||||
|         if (this.settings.recaptchapublickey) { | ||||
|             this.captcha.recaptcharesponse = ''; // Reset captcha.
 | ||||
|         } | ||||
| 
 | ||||
|         if (!this.countryControl.value) { | ||||
|             this.countryControl.setValue(this.settings.country || ''); | ||||
|         } | ||||
| 
 | ||||
|         this.namefieldsErrors = {}; | ||||
|         if (this.settings.namefields) { | ||||
|             this.settings.namefields.forEach((field) => { | ||||
|                 this.namefieldsErrors![field] = CoreLoginHelper.instance.getErrorMessages('core.login.missing' + field); | ||||
|             }); | ||||
|         } | ||||
| 
 | ||||
|         this.countries = await CoreUtils.instance.getCountryListSorted(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Treat the site config, checking if it's valid and extracting the data we're interested in. | ||||
|      * | ||||
|      * @return True if success. | ||||
|      */ | ||||
|     protected treatSiteConfig(): boolean { | ||||
|         if (this.siteConfig?.registerauth == 'email' && !CoreLoginHelper.instance.isEmailSignupDisabled(this.siteConfig)) { | ||||
|             this.siteName = CoreConstants.CONFIG.sitename ? CoreConstants.CONFIG.sitename : this.siteConfig.sitename; | ||||
|             this.authInstructions = this.siteConfig.authinstructions; | ||||
|             this.ageDigitalConsentVerification = this.siteConfig.agedigitalconsentverification; | ||||
|             this.supportName = this.siteConfig.supportname; | ||||
|             this.supportEmail = this.siteConfig.supportemail; | ||||
|             this.countryControl.setValue(this.siteConfig.country || ''); | ||||
| 
 | ||||
|             return true; | ||||
|         } else { | ||||
|             CoreDomUtils.instance.showErrorModal( | ||||
|                 Translate.instance.instant( | ||||
|                     'core.login.signupplugindisabled', | ||||
|                     { $a: Translate.instance.instant('core.login.auth_email') }, | ||||
|                 ), | ||||
|             ); | ||||
|             this.navCtrl.pop(); | ||||
| 
 | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Pull to refresh. | ||||
|      * | ||||
|      * @param event Event. | ||||
|      */ | ||||
|     refreshSettings(event?: CustomEvent<IonRefresher>): void { | ||||
|         this.fetchData().finally(() => { | ||||
|             event?.detail.complete(); | ||||
|         }); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Create account. | ||||
|      * | ||||
|      * @param e Event. | ||||
|      * @return Promise resolved when done. | ||||
|      */ | ||||
|     async create(e: Event): Promise<void> { | ||||
|         e.preventDefault(); | ||||
|         e.stopPropagation(); | ||||
| 
 | ||||
|         if (!this.signupForm.valid || (this.settings?.recaptchapublickey && !this.captcha.recaptcharesponse)) { | ||||
|             // Form not valid. Scroll to the first element with errors.
 | ||||
|             const errorFound = await CoreDomUtils.instance.scrollToInputError(this.content); | ||||
| 
 | ||||
|             if (!errorFound) { | ||||
|                 // Input not found, show an error modal.
 | ||||
|                 CoreDomUtils.instance.showErrorModal('core.errorinvalidform', true); | ||||
|             } | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         const modal = await CoreDomUtils.instance.showModalLoading('core.sending', true); | ||||
| 
 | ||||
|         const params: Record<string, unknown> = { | ||||
|             username: this.signupForm.value.username.trim().toLowerCase(), | ||||
|             password: this.signupForm.value.password, | ||||
|             firstname: CoreTextUtils.instance.cleanTags(this.signupForm.value.firstname), | ||||
|             lastname: CoreTextUtils.instance.cleanTags(this.signupForm.value.lastname), | ||||
|             email: this.signupForm.value.email.trim(), | ||||
|             city: CoreTextUtils.instance.cleanTags(this.signupForm.value.city), | ||||
|             country: this.signupForm.value.country, | ||||
|         }; | ||||
| 
 | ||||
|         if (this.siteConfig?.launchurl) { | ||||
|             const service = CoreSites.instance.determineService(this.siteUrl); | ||||
|             params.redirect = CoreLoginHelper.instance.prepareForSSOLogin(this.siteUrl, service, this.siteConfig.launchurl); | ||||
|         } | ||||
| 
 | ||||
|         // Get the recaptcha response (if needed).
 | ||||
|         if (this.settings?.recaptchapublickey && this.captcha.recaptcharesponse) { | ||||
|             params.recaptcharesponse = this.captcha.recaptcharesponse; | ||||
|         } | ||||
| 
 | ||||
|         try { | ||||
|             // @todo Get the data for the custom profile fields.
 | ||||
|             const result = await CoreWS.instance.callAjax<SignupUserResult>( | ||||
|                 'auth_email_signup_user', | ||||
|                 params, | ||||
|                 { siteUrl: this.siteUrl }, | ||||
|             ); | ||||
| 
 | ||||
|             if (result.success) { | ||||
| 
 | ||||
|                 CoreDomUtils.instance.triggerFormSubmittedEvent(this.signupFormElement, true); | ||||
| 
 | ||||
|                 // Show alert and ho back.
 | ||||
|                 const message = Translate.instance.instant('core.login.emailconfirmsent', { $a: params.email }); | ||||
|                 CoreDomUtils.instance.showAlert(Translate.instance.instant('core.success'), message); | ||||
|                 this.navCtrl.pop(); | ||||
|             } else { | ||||
|                 if (result.warnings && result.warnings.length) { | ||||
|                     let error = result.warnings[0].message; | ||||
|                     if (error == 'incorrect-captcha-sol') { | ||||
|                         error = Translate.instance.instant('core.login.recaptchaincorrect'); | ||||
|                     } | ||||
| 
 | ||||
|                     CoreDomUtils.instance.showErrorModal(error); | ||||
|                 } else { | ||||
|                     CoreDomUtils.instance.showErrorModal('core.login.usernotaddederror', true); | ||||
|                 } | ||||
|             } | ||||
|         } catch (error) { | ||||
|             CoreDomUtils.instance.showErrorModalDefault(error, 'core.login.usernotaddederror', true); | ||||
|         } finally { | ||||
|             modal.dismiss(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Escape mail to avoid special characters to be treated as a RegExp. | ||||
|      * | ||||
|      * @param text Initial mail. | ||||
|      * @return Escaped mail. | ||||
|      */ | ||||
|     escapeMail(text: string): string { | ||||
|         return CoreTextUtils.instance.escapeForRegex(text); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Show authentication instructions. | ||||
|      */ | ||||
|     protected showAuthInstructions(): void { | ||||
|         CoreTextUtils.instance.viewText(Translate.instance.instant('core.login.instructions'), this.authInstructions!); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Show contact information on site (we have to display again the age verification form). | ||||
|      */ | ||||
|     showContactOnSite(): void { | ||||
|         CoreUtils.instance.openInBrowser(CoreTextUtils.instance.concatenatePaths(this.siteUrl, '/login/verify_age_location.php')); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Verify Age. | ||||
|      * | ||||
|      * @param e Event. | ||||
|      * @return Promise resolved when done. | ||||
|      */ | ||||
|     async verifyAge(e: Event): Promise<void> { | ||||
|         e.preventDefault(); | ||||
|         e.stopPropagation(); | ||||
| 
 | ||||
|         if (!this.ageVerificationForm.valid) { | ||||
|             CoreDomUtils.instance.showErrorModal('core.errorinvalidform', true); | ||||
| 
 | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         const modal = await CoreDomUtils.instance.showModalLoading('core.sending', true); | ||||
| 
 | ||||
|         const params = this.ageVerificationForm.value; | ||||
| 
 | ||||
|         params.age = parseInt(params.age, 10); // Use just the integer part.
 | ||||
| 
 | ||||
|         try { | ||||
|             const result = await CoreWS.instance.callAjax<IsMinorResult>('core_auth_is_minor', params, { siteUrl: this.siteUrl }); | ||||
| 
 | ||||
|             CoreDomUtils.instance.triggerFormSubmittedEvent(this.ageFormElement, true); | ||||
| 
 | ||||
|             if (!result.status) { | ||||
|                 if (this.countryControl.value) { | ||||
|                     this.signUpCountryControl!.setValue(this.countryControl.value); | ||||
|                 } | ||||
| 
 | ||||
|                 // Not a minor, go ahead.
 | ||||
|                 this.ageDigitalConsentVerification = false; | ||||
|             } else { | ||||
|                 // Is a minor.
 | ||||
|                 this.isMinor = true; | ||||
|             } | ||||
|         } catch (error) { | ||||
|             // Something wrong, redirect to the site.
 | ||||
|             CoreDomUtils.instance.showErrorModal('There was an error verifying your age, please try again using the browser.'); | ||||
|         } finally { | ||||
|             modal.dismiss(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * Result of WS core_auth_is_age_digital_consent_verification_enabled. | ||||
|  */ | ||||
| export type IsAgeVerificationEnabledResponse = { | ||||
|     status: boolean; // True if digital consent verification is enabled, false otherwise.
 | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
|  * Result of WS auth_email_signup_user. | ||||
|  */ | ||||
| export type SignupUserResult = { | ||||
|     success: boolean; // True if the user was created false otherwise.
 | ||||
|     warnings?: CoreWSExternalWarning[]; | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
|  * Result of WS core_auth_is_minor. | ||||
|  */ | ||||
| export type IsMinorResult = { | ||||
|     status: boolean; // True if the user is considered to be a digital minor, false if not.
 | ||||
| }; | ||||
| @ -204,7 +204,7 @@ export class CoreLoginHelperProvider { | ||||
|      * @param profileFields Profile fields to format. | ||||
|      * @return Categories with the fields to show in each one. | ||||
|      */ | ||||
|     formatProfileFieldsForSignup(profileFields: AuthEmailSignupProfileField[]): AuthEmailSignupProfileFieldsCategory[] { | ||||
|     formatProfileFieldsForSignup(profileFields?: AuthEmailSignupProfileField[]): AuthEmailSignupProfileFieldsCategory[] { | ||||
|         if (!profileFields) { | ||||
|             return []; | ||||
|         } | ||||
| @ -269,8 +269,8 @@ export class CoreLoginHelperProvider { | ||||
|         maxlengthMsg?: string, | ||||
|         minMsg?: string, | ||||
|         maxMsg?: string, | ||||
|     ): any { | ||||
|         const errors: any = {}; | ||||
|     ): Record<string, string> { | ||||
|         const errors: Record<string, string> = {}; | ||||
| 
 | ||||
|         if (requiredMsg) { | ||||
|             errors.required = errors.requiredTrue = Translate.instance.instant(requiredMsg); | ||||
|  | ||||
| @ -1014,7 +1014,7 @@ export class CoreDomUtilsProvider { | ||||
|      * @deprecated since 3.9.5. Use directly the IonContent class. | ||||
|      */ | ||||
|     scrollTo(content: IonContent, x: number, y: number, duration?: number): Promise<void> { | ||||
|         return content?.scrollByPoint(x, y, duration || 0); | ||||
|         return content?.scrollToPoint(x, y, duration || 0); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -1104,7 +1104,7 @@ export class CoreDomUtilsProvider { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         content?.scrollByPoint(position[0], position[1], duration || 0); | ||||
|         content?.scrollToPoint(position[0], position[1], duration || 0); | ||||
| 
 | ||||
|         return true; | ||||
|     } | ||||
| @ -1124,6 +1124,8 @@ export class CoreDomUtilsProvider { | ||||
|         scrollParentClass?: string, | ||||
|         duration?: number, | ||||
|     ): Promise<boolean> { | ||||
|         // @todo: This function is broken. Scroll element cannot be used because it uses shadow DOM so querySelector returns null.
 | ||||
|         // Also, traversing using parentElement doesn't work either, offsetParent isn't part of the parentElement tree.
 | ||||
|         try { | ||||
|             const scrollElement = await content.getScrollElement(); | ||||
| 
 | ||||
| @ -1132,7 +1134,7 @@ export class CoreDomUtilsProvider { | ||||
|                 return false; | ||||
|             } | ||||
| 
 | ||||
|             content?.scrollByPoint(position[0], position[1], duration || 0); | ||||
|             content?.scrollToPoint(position[0], position[1], duration || 0); | ||||
| 
 | ||||
|             return true; | ||||
|         } catch (error) { | ||||
| @ -1147,7 +1149,7 @@ export class CoreDomUtilsProvider { | ||||
|      * @param scrollParentClass Parent class where to stop calculating the position. Default inner-scroll. | ||||
|      * @return True if the element is found, false otherwise. | ||||
|      */ | ||||
|     async scrollToInputError(content: IonContent, scrollParentClass?: string): Promise<boolean> { | ||||
|     async scrollToInputError(content?: IonContent, scrollParentClass?: string): Promise<boolean> { | ||||
|         if (!content) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
| @ -618,7 +618,7 @@ export class CoreUtilsProvider { | ||||
|      * | ||||
|      * @return Promise resolved with the list of countries. | ||||
|      */ | ||||
|     getCountryListSorted(): Promise<{ code: string; name: string }[]> { | ||||
|     getCountryListSorted(): Promise<CoreCountry[]> { | ||||
|         // Get the keys of the countries.
 | ||||
|         return this.getCountryList().then((countries) => { | ||||
|             // Sort translations.
 | ||||
| @ -1659,3 +1659,11 @@ export type OrderedPromiseData = { | ||||
|      */ | ||||
|     blocking?: boolean; | ||||
| }; | ||||
| 
 | ||||
| /** | ||||
|  * Data about a country. | ||||
|  */ | ||||
| export type CoreCountry = { | ||||
|     code: string; | ||||
|     name: string; | ||||
| }; | ||||
|  | ||||
| @ -90,3 +90,12 @@ ion-list.list-md { | ||||
|     visibility: hidden; | ||||
|     left: -1000px; | ||||
| } | ||||
| 
 | ||||
| // Note on foot of ion-input. | ||||
| .item .core-input-footnote { | ||||
|     width: 100%; | ||||
|     font-style: italic; | ||||
|     margin-top: 0; | ||||
|     margin-bottom: 10px; | ||||
|     font-size: 14px; | ||||
| } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user