diff --git a/src/core/components/components.module.ts b/src/core/components/components.module.ts index 76c6ec45a..416cd3999 100644 --- a/src/core/components/components.module.ts +++ b/src/core/components/components.module.ts @@ -36,6 +36,7 @@ import { CoreContextMenuComponent } from './context-menu/context-menu'; import { CoreContextMenuItemComponent } from './context-menu/context-menu-item'; import { CoreContextMenuPopoverComponent } from './context-menu/context-menu-popover'; import { CoreUserAvatarComponent } from './user-avatar/user-avatar'; +import { CoreDynamicComponent } from './dynamic-component/dynamic-component'; import { CoreDirectivesModule } from '@directives/directives.module'; import { CorePipesModule } from '@pipes/pipes.module'; @@ -63,6 +64,7 @@ import { CoreNavBarButtonsComponent } from './navbar-buttons/navbar-buttons'; CoreContextMenuPopoverComponent, CoreNavBarButtonsComponent, CoreUserAvatarComponent, + CoreDynamicComponent, ], imports: [ CommonModule, @@ -92,6 +94,7 @@ import { CoreNavBarButtonsComponent } from './navbar-buttons/navbar-buttons'; CoreContextMenuPopoverComponent, CoreNavBarButtonsComponent, CoreUserAvatarComponent, + CoreDynamicComponent, ], }) export class CoreComponentsModule {} diff --git a/src/core/components/dynamic-component/core-dynamic-component.html b/src/core/components/dynamic-component/core-dynamic-component.html new file mode 100644 index 000000000..99c89fec9 --- /dev/null +++ b/src/core/components/dynamic-component/core-dynamic-component.html @@ -0,0 +1,5 @@ + + + + + diff --git a/src/core/components/dynamic-component/dynamic-component.ts b/src/core/components/dynamic-component/dynamic-component.ts new file mode 100644 index 000000000..5ae5512ab --- /dev/null +++ b/src/core/components/dynamic-component/dynamic-component.ts @@ -0,0 +1,198 @@ +// (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, + Input, + ViewChild, + OnChanges, + DoCheck, + ViewContainerRef, + ComponentFactoryResolver, + ComponentRef, + KeyValueDiffers, + SimpleChange, + ChangeDetectorRef, + Optional, + ElementRef, + KeyValueDiffer, + Type, +} from '@angular/core'; +import { NavController } from '@ionic/angular'; + +import { CoreDomUtils } from '@services/utils/dom'; +import { CoreLogger } from '@singletons/logger'; + +/** + * Component to create another component dynamically. + * + * You need to pass the class of the component to this component (the class, not the name), along with the input data. + * + * So you should do something like: + * + * import { MyComponent } from './component'; + * + * ... + * + * this.component = MyComponent; + * + * And in the template: + * + * + *

Cannot render the data.

+ *
+ * + * Please notice that the component that you pass needs to be declared in entryComponents of the module to be created dynamically. + * + * Alternatively, you can also supply a ComponentRef instead of the class of the component. In this case, the component won't + * be instantiated because it already is, it will be attached to the view and the right data will be passed to it. + * Passing ComponentRef is meant for site plugins, so we'll inject a NavController instance to the component. + * + * The contents of this component will be displayed if no component is supplied or it cannot be created. In the example above, + * if no component is supplied then the template will show the message "Cannot render the data.". + */ +/* eslint-disable @angular-eslint/no-conflicting-lifecycle */ +@Component({ + selector: 'core-dynamic-component', + templateUrl: 'core-dynamic-component.html', +}) +export class CoreDynamicComponent implements OnChanges, DoCheck { + + @Input() component?: Type; + @Input() data?: Record; + + // Get the container where to put the dynamic component. + @ViewChild('dynamicComponent', { read: ViewContainerRef }) set dynamicComponent(el: ViewContainerRef) { + this.container = el; + this.createComponent(); + } + + instance?: any; // eslint-disable-line @typescript-eslint/no-explicit-any + container?: ViewContainerRef; + + protected logger: CoreLogger; + protected differ: KeyValueDiffer; // To detect changes in the data input. + protected lastComponent?: Type; + + constructor( + protected factoryResolver: ComponentFactoryResolver, + differs: KeyValueDiffers, + @Optional() protected navCtrl: NavController, + protected cdr: ChangeDetectorRef, + protected element: ElementRef, + ) { + + this.logger = CoreLogger.getInstance('CoreDynamicComponent'); + this.differ = differs.find([]).create(); + } + + /** + * Detect changes on input properties. + */ + ngOnChanges(changes: { [name: string]: SimpleChange }): void { + if (changes.component && !this.component) { + // Component not set, destroy the instance if any. + this.lastComponent = undefined; + this.instance = undefined; + this.container?.clear(); + } else if (changes.component && (!this.instance || this.component != this.lastComponent)) { + this.createComponent(); + } + } + + /** + * Detect and act upon changes that Angular can’t or won’t detect on its own (objects and arrays). + */ + ngDoCheck(): void { + if (this.instance) { + // Check if there's any change in the data object. + const changes = this.differ.diff(this.data || {}); + if (changes) { + this.setInputData(); + if (this.instance.ngOnChanges) { + this.instance.ngOnChanges(CoreDomUtils.instance.createChangesFromKeyValueDiff(changes)); + } + } + } + } + + /** + * Call a certain function on the component. + * + * @param name Name of the function to call. + * @param params List of params to send to the function. + * @return Result of the call. Undefined if no component instance or the function doesn't exist. + */ + callComponentFunction(name: string, params?: unknown[]): T | undefined { + if (this.instance && typeof this.instance[name] == 'function') { + return this.instance[name].apply(this.instance, params); + } + } + + /** + * Create a component, add it to a container and set the input data. + * + * @return Whether the component was successfully created. + */ + protected createComponent(): boolean { + this.lastComponent = this.component; + + if (!this.component || !this.container) { + // No component to instantiate or container doesn't exist right now. + return false; + } + + if (this.instance) { + // Component already instantiated. + return true; + } + + if (this.component instanceof ComponentRef) { + // A ComponentRef was supplied instead of the component class. Add it to the view. + this.container.insert(this.component.hostView); + this.instance = this.component.instance; + + // This feature is usually meant for site plugins. Inject some properties. + this.instance['ChangeDetectorRef'] = this.cdr; + this.instance['NavController'] = this.navCtrl; + this.instance['componentContainer'] = this.element.nativeElement; + } else { + try { + // Create the component and add it to the container. + const factory = this.factoryResolver.resolveComponentFactory(this.component); + const componentRef = this.container.createComponent(factory); + + this.instance = componentRef.instance; + } catch (ex) { + this.logger.error('Error creating component', ex); + + return false; + } + } + + this.setInputData(); + + return true; + } + + /** + * Set the input data for the component. + */ + protected setInputData(): void { + for (const name in this.data) { + this.instance[name] = this.data[name]; + } + } + +} diff --git a/src/core/features/login/pages/email-signup/email-signup.html b/src/core/features/login/pages/email-signup/email-signup.html index bbef473a6..95378a395 100644 --- a/src/core/features/login/pages/email-signup/email-signup.html +++ b/src/core/features/login/pages/email-signup/email-signup.html @@ -172,8 +172,8 @@ {{ category.name }} - + diff --git a/src/core/features/login/pages/email-signup/email-signup.module.ts b/src/core/features/login/pages/email-signup/email-signup.module.ts index 7956c875d..b1a454123 100644 --- a/src/core/features/login/pages/email-signup/email-signup.module.ts +++ b/src/core/features/login/pages/email-signup/email-signup.module.ts @@ -21,6 +21,7 @@ import { TranslateModule } from '@ngx-translate/core'; import { CoreComponentsModule } from '@components/components.module'; import { CoreDirectivesModule } from '@directives/directives.module'; +import { CoreUserComponentsModule } from '@features/user/components/components.module'; import { CoreLoginEmailSignupPage } from './email-signup'; @@ -41,6 +42,7 @@ const routes: Routes = [ ReactiveFormsModule, CoreComponentsModule, CoreDirectivesModule, + CoreUserComponentsModule, ], declarations: [ CoreLoginEmailSignupPage, diff --git a/src/core/features/login/pages/email-signup/email-signup.ts b/src/core/features/login/pages/email-signup/email-signup.ts index d5e080862..8935a09e3 100644 --- a/src/core/features/login/pages/email-signup/email-signup.ts +++ b/src/core/features/login/pages/email-signup/email-signup.ts @@ -25,6 +25,7 @@ import { CoreWS, CoreWSExternalWarning } from '@services/ws'; import { CoreConstants } from '@/core/constants'; import { Translate } from '@singletons'; import { CoreSitePublicConfigResponse } from '@classes/site'; +import { CoreUserProfileFieldDelegate } from '@features/user/services/user-profile-field-delegate'; import { AuthEmailSignupProfileFieldsCategory, @@ -82,6 +83,7 @@ export class CoreLoginEmailSignupPage implements OnInit { protected navCtrl: NavController, protected fb: FormBuilder, protected route: ActivatedRoute, + protected userProfileFieldDelegate: CoreUserProfileFieldDelegate, ) { // Create the ageVerificationForm. this.ageVerificationForm = this.fb.group({ @@ -156,7 +158,7 @@ export class CoreLoginEmailSignupPage implements OnInit { if (typeof this.ageDigitalConsentVerification == 'undefined') { const result = await CoreUtils.instance.ignoreErrors( - CoreWS.instance.callAjax( + CoreWS.instance.callAjax( 'core_auth_is_age_digital_consent_verification_enabled', {}, { siteUrl: this.siteUrl }, @@ -189,7 +191,11 @@ export class CoreLoginEmailSignupPage implements OnInit { { siteUrl: this.siteUrl }, ); - // @todo userProfileFieldDelegate + if (this.userProfileFieldDelegate.hasRequiredUnsupportedField(this.settings.profilefields)) { + this.allRequiredSupported = false; + + throw new Error(Translate.instance.instant('core.login.signuprequiredfieldnotsupported')); + } this.categories = CoreLoginHelper.instance.formatProfileFieldsForSignup(this.settings.profilefields); @@ -274,7 +280,7 @@ export class CoreLoginEmailSignupPage implements OnInit { const modal = await CoreDomUtils.instance.showModalLoading('core.sending', true); - const params: Record = { + const params: SignupUserWSParams = { username: this.signupForm.value.username.trim().toLowerCase(), password: this.signupForm.value.password, firstname: CoreTextUtils.instance.cleanTags(this.signupForm.value.firstname), @@ -295,8 +301,15 @@ export class CoreLoginEmailSignupPage implements OnInit { } try { - // @todo Get the data for the custom profile fields. - const result = await CoreWS.instance.callAjax( + // Get the data for the custom profile fields. + params.customprofilefields = await this.userProfileFieldDelegate.getDataForFields( + this.settings?.profilefields, + true, + 'email', + this.signupForm.value, + ); + + const result = await CoreWS.instance.callAjax( 'auth_email_signup_user', params, { siteUrl: this.siteUrl }, @@ -376,7 +389,7 @@ export class CoreLoginEmailSignupPage implements OnInit { params.age = parseInt(params.age, 10); // Use just the integer part. try { - const result = await CoreWS.instance.callAjax('core_auth_is_minor', params, { siteUrl: this.siteUrl }); + const result = await CoreWS.instance.callAjax('core_auth_is_minor', params, { siteUrl: this.siteUrl }); CoreDomUtils.instance.triggerFormSubmittedEvent(this.ageFormElement, true); @@ -404,14 +417,35 @@ export class CoreLoginEmailSignupPage implements OnInit { /** * Result of WS core_auth_is_age_digital_consent_verification_enabled. */ -export type IsAgeVerificationEnabledResponse = { +type IsAgeVerificationEnabledWSResponse = { status: boolean; // True if digital consent verification is enabled, false otherwise. }; +/** + * Params for WS auth_email_signup_user. + */ +type SignupUserWSParams = { + username: string; // Username. + password: string; // Plain text password. + firstname: string; // The first name(s) of the user. + lastname: string; // The family name of the user. + email: string; // A valid and unique email address. + city?: string; // Home city of the user. + country?: string; // Home country code. + recaptchachallengehash?: string; // Recaptcha challenge hash. + recaptcharesponse?: string; // Recaptcha response. + customprofilefields?: { // User custom fields (also known as user profile fields). + type: string; // The type of the custom field. + name: string; // The name of the custom field. + value: unknown; // Custom field value, can be an encoded json if required. + }[]; + redirect?: string; // Redirect the user to this site url after confirmation. +}; + /** * Result of WS auth_email_signup_user. */ -export type SignupUserResult = { +type SignupUserWSResult = { success: boolean; // True if the user was created false otherwise. warnings?: CoreWSExternalWarning[]; }; @@ -419,6 +453,6 @@ export type SignupUserResult = { /** * Result of WS core_auth_is_minor. */ -export type IsMinorResult = { +type IsMinorWSResult = { status: boolean; // True if the user is considered to be a digital minor, false if not. }; diff --git a/src/core/features/user/components/components.module.ts b/src/core/features/user/components/components.module.ts new file mode 100644 index 000000000..cd7b1d33f --- /dev/null +++ b/src/core/features/user/components/components.module.ts @@ -0,0 +1,43 @@ +// (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 { IonicModule } from '@ionic/angular'; +import { TranslateModule } from '@ngx-translate/core'; + +import { CoreComponentsModule } from '@components/components.module'; +import { CoreDirectivesModule } from '@directives/directives.module'; +import { CorePipesModule } from '@pipes/pipes.module'; +import { CoreUserProfileFieldComponent } from './user-profile-field/user-profile-field'; + +@NgModule({ + declarations: [ + CoreUserProfileFieldComponent, + ], + imports: [ + CommonModule, + IonicModule, + TranslateModule.forChild(), + CoreComponentsModule, + CoreDirectivesModule, + CorePipesModule, + ], + providers: [ + ], + exports: [ + CoreUserProfileFieldComponent, + ], +}) +export class CoreUserComponentsModule {} diff --git a/src/core/features/user/components/user-profile-field/core-user-profile-field.html b/src/core/features/user/components/user-profile-field/core-user-profile-field.html new file mode 100644 index 000000000..1f3a37007 --- /dev/null +++ b/src/core/features/user/components/user-profile-field/core-user-profile-field.html @@ -0,0 +1 @@ + diff --git a/src/core/features/user/components/user-profile-field/user-profile-field.ts b/src/core/features/user/components/user-profile-field/user-profile-field.ts new file mode 100644 index 000000000..6a4aae6c5 --- /dev/null +++ b/src/core/features/user/components/user-profile-field/user-profile-field.ts @@ -0,0 +1,83 @@ +// (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, Input, OnInit, Injector, Type } from '@angular/core'; +import { FormGroup } from '@angular/forms'; + +import { AuthEmailSignupProfileField } from '@features/login/services/login-helper'; +import { CoreUserProfileFieldDelegate } from '@features/user/services/user-profile-field-delegate'; +import { CoreUtils } from '@services/utils/utils'; + +/** + * Directive to render user profile field. + */ +@Component({ + selector: 'core-user-profile-field', + templateUrl: 'core-user-profile-field.html', +}) +export class CoreUserProfileFieldComponent implements OnInit { + + @Input() field?: AuthEmailSignupProfileField; // The profile field to be rendered. + @Input() signup = false; // True if editing the field in signup. Defaults to false. + @Input() edit = false; // True if editing the field. Defaults to false. + @Input() form?: FormGroup; // Form where to add the form control. Required if edit=true or signup=true. + @Input() registerAuth?: string; // Register auth method. E.g. 'email'. + @Input() contextLevel?: string; // The context level. + @Input() contextInstanceId?: number; // The instance ID related to the context. + @Input() courseId?: number; // Course ID the field belongs to (if any). It can be used to improve performance with filters. + + componentClass?: Type; // The class of the component to render. + data: CoreUserProfileFieldComponentData = {}; // Data to pass to the component. + + constructor( + protected userProfileFieldsDelegate: CoreUserProfileFieldDelegate, + protected injector: Injector, + ) { } + + /** + * Component being initialized. + */ + async ngOnInit(): Promise { + if (!this.field) { + return; + } + + this.componentClass = await this.userProfileFieldsDelegate.getComponent(this.injector, this.field, this.signup); + + this.data.field = this.field; + this.data.edit = CoreUtils.instance.isTrueOrOne(this.edit); + if (this.edit) { + this.data.signup = CoreUtils.instance.isTrueOrOne(this.signup); + this.data.disabled = CoreUtils.instance.isTrueOrOne(this.field.locked); + this.data.form = this.form; + this.data.registerAuth = this.registerAuth; + this.data.contextLevel = this.contextLevel; + this.data.contextInstanceId = this.contextInstanceId; + this.data.courseId = this.courseId; + } + } + +} + +export type CoreUserProfileFieldComponentData = { + field?: AuthEmailSignupProfileField; + edit?: boolean; + signup?: boolean; + disabled?: boolean; + form?: FormGroup; + registerAuth?: string; + contextLevel?: string; + contextInstanceId?: number; + courseId?: number; +}; diff --git a/src/core/features/user/pages/about/about.html b/src/core/features/user/pages/about/about.html index c45d302d3..633ecd788 100644 --- a/src/core/features/user/pages/about/about.html +++ b/src/core/features/user/pages/about/about.html @@ -41,7 +41,7 @@

{{ 'core.user.address' | translate}}

-

+

{{ user.address }}

diff --git a/src/core/features/user/pages/about/about.module.ts b/src/core/features/user/pages/about/about.module.ts index daa3c8811..2c2e2c340 100644 --- a/src/core/features/user/pages/about/about.module.ts +++ b/src/core/features/user/pages/about/about.module.ts @@ -20,6 +20,7 @@ import { TranslateModule } from '@ngx-translate/core'; import { CoreComponentsModule } from '@components/components.module'; import { CoreDirectivesModule } from '@directives/directives.module'; +import { CoreUserComponentsModule } from '@features/user/components/components.module'; import { CoreUserAboutPage } from './about.page'; @@ -38,6 +39,7 @@ const routes: Routes = [ TranslateModule.forChild(), CoreComponentsModule, CoreDirectivesModule, + CoreUserComponentsModule, ], declarations: [ CoreUserAboutPage, diff --git a/src/core/features/user/services/user-profile-field-delegate.ts b/src/core/features/user/services/user-profile-field-delegate.ts new file mode 100644 index 000000000..6ec0e6e3d --- /dev/null +++ b/src/core/features/user/services/user-profile-field-delegate.ts @@ -0,0 +1,210 @@ +// (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 { Injectable, Injector, Type } from '@angular/core'; + +import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate'; +import { CoreError } from '@classes/errors/error'; +import { AuthEmailSignupProfileField } from '@features/login/services/login-helper'; +import { CoreUserProfileField } from './user'; + +/** + * Interface that all user profile field handlers must implement. + */ +export interface CoreUserProfileFieldHandler extends CoreDelegateHandler { + /** + * Type of the field the handler supports. E.g. 'checkbox'. + */ + type: string; + + /** + * Return the Component to use to display the user profile field. + * It's recommended to return the class of the component, but you can also return an instance of the component. + * + * @param injector Injector. + * @return The component (or promise resolved with component) to use, undefined if not found. + */ + getComponent(injector: Injector): Type | Promise>; + + /** + * Get the data to send for the field based on the input data. + * + * @param field User field to get the data for. + * @param signup True if user is in signup page. + * @param registerAuth Register auth method. E.g. 'email'. + * @param formValues Form Values. + * @return Data to send for the field. + */ + getData?( + field: AuthEmailSignupProfileField | CoreUserProfileField, + signup: boolean, + registerAuth: string, + formValues: Record, + ): Promise; +} + +export interface CoreUserProfileFieldHandlerData { + /** + * Name of the custom field. + */ + name: string; + + /** + * The type of the custom field + */ + type: string; + + /** + * Value of the custom field. + */ + value: unknown; +} + +/** + * Service to interact with user profile fields. + */ +@Injectable({ + providedIn: 'root', +}) +export class CoreUserProfileFieldDelegate extends CoreDelegate { + + protected handlerNameProperty = 'type'; + + constructor() { + super('CoreUserProfileFieldDelegate', true); + } + + /** + * Get the type of a field. + * + * @param field The field to get its type. + * @return The field type. + */ + protected getType(field: AuthEmailSignupProfileField | CoreUserProfileField): string { + return ('type' in field ? field.type : field.datatype) || ''; + } + + /** + * Get the component to use to display an user field. + * + * @param injector Injector. + * @param field User field to get the directive for. + * @param signup True if user is in signup page. + * @return Promise resolved with component to use, undefined if not found. + */ + async getComponent( + injector: Injector, + field: AuthEmailSignupProfileField | CoreUserProfileField, + signup: boolean, + ): Promise | undefined> { + const type = this.getType(field); + + try { + if (signup) { + return await this.executeFunction(type, 'getComponent', [injector]); + } else { + return await this.executeFunctionOnEnabled(type, 'getComponent', [injector]); + } + } catch (error) { + this.logger.error('Error getting component for field', type, error); + } + } + + /** + * Get the data to send for a certain field based on the input data. + * + * @param field User field to get the data for. + * @param signup True if user is in signup page. + * @param registerAuth Register auth method. E.g. 'email'. + * @param formValues Form values. + * @return Data to send for the field. + */ + async getDataForField( + field: AuthEmailSignupProfileField | CoreUserProfileField, + signup: boolean, + registerAuth: string, + formValues: Record, + ): Promise { + const type = this.getType(field); + const handler = this.getHandler(type, !signup); + + if (handler) { + const name = 'profile_field_' + field.shortname; + + if (handler.getData) { + return await handler.getData(field, signup, registerAuth, formValues); + } else if (field.shortname && typeof formValues[name] != 'undefined') { + // Handler doesn't implement the function, but the form has data for the field. + return { + type: type, + name: name, + value: formValues[name], + }; + } + } + + throw new CoreError('User profile field handler not found.'); + } + + /** + * Get the data to send for a list of fields based on the input data. + * + * @param fields User fields to get the data for. + * @param signup True if user is in signup page. + * @param registerAuth Register auth method. E.g. 'email'. + * @param formValues Form values. + * @return Data to send. + */ + async getDataForFields( + fields: (AuthEmailSignupProfileField | CoreUserProfileField)[] | undefined, + signup: boolean = false, + registerAuth: string = '', + formValues: Record, + ): Promise { + if (!fields) { + return []; + } + + const result: CoreUserProfileFieldHandlerData[] = []; + + await Promise.all(fields.map(async (field) => { + try { + const data = await this.getDataForField(field, signup, registerAuth, formValues); + + if (data) { + result.push(data); + } + } catch (error) { + // Ignore errors. + } + })); + + return result; + } + + /** + * Check if any of the profile fields is not supported in the app. + * + * @param fields List of fields. + * @return Whether any of the profile fields is not supported in the app. + */ + hasRequiredUnsupportedField(fields?: AuthEmailSignupProfileField[]): boolean { + if (!fields || !fields.length) { + return false; + } + + return fields.some((field) => field.required && !this.hasHandler(this.getType(field))); + } + +} diff --git a/src/core/features/user/user.module.ts b/src/core/features/user/user.module.ts index 26f0e6c98..6c580f42d 100644 --- a/src/core/features/user/user.module.ts +++ b/src/core/features/user/user.module.ts @@ -18,6 +18,7 @@ import { Routes } from '@angular/router'; import { CoreMainMenuMoreRoutingModule } from '@features/mainmenu/pages/more/more-routing.module'; import { CORE_SITE_SCHEMAS } from '@services/sites'; import { SITE_SCHEMA, OFFLINE_SITE_SCHEMA } from './services/db/user'; +import { CoreUserComponentsModule } from './components/components.module'; const routes: Routes = [ { @@ -29,6 +30,7 @@ const routes: Routes = [ @NgModule({ imports: [ CoreMainMenuMoreRoutingModule.forChild({ siblings: routes }), + CoreUserComponentsModule, ], providers: [ {