From 2f2bd02848d211ff9ef2e068a104c4de1953e951 Mon Sep 17 00:00:00 2001 From: gpascal123 Date: Wed, 25 Mar 2026 08:42:24 +0100 Subject: [PATCH] Email validation has been aligned with PHP email validation --- .../acc-settings/acc-settings.component.html | 2 +- .../acc-settings/acc-settings.component.ts | 6 +++++- src/app/core/_services/metadata.service.ts | 3 ++- src/app/core/_validators/email.validator.ts | 14 ++++++++++++++ src/app/users/new-user/new-user.form.ts | 4 +++- 5 files changed, 25 insertions(+), 4 deletions(-) create mode 100644 src/app/core/_validators/email.validator.ts diff --git a/src/app/account/settings/acc-settings/acc-settings.component.html b/src/app/account/settings/acc-settings/acc-settings.component.html index bd86b99f5..6bd3bcfff 100644 --- a/src/app/account/settings/acc-settings/acc-settings.component.html +++ b/src/app/account/settings/acc-settings/acc-settings.component.html @@ -10,7 +10,7 @@ data-testid="input-registered-since" > - + @if (isUpdatingLoading) { diff --git a/src/app/account/settings/acc-settings/acc-settings.component.ts b/src/app/account/settings/acc-settings/acc-settings.component.ts index 3a1b5ea5a..6063ff33a 100644 --- a/src/app/account/settings/acc-settings/acc-settings.component.ts +++ b/src/app/account/settings/acc-settings/acc-settings.component.ts @@ -14,6 +14,7 @@ import { SERV } from '@services/main.config'; import { changeOwnPasswordResponseSchema } from '@src/app/account/settings/acc-settings/acc-settings.schema'; import { JUserSchema } from '@src/app/core/_models/user.schema'; import { JsonAPISerializer } from '@src/app/core/_services/api/serializer-service'; +import { emailValidator } from '@src/app/core/_validators/email.validator'; import { passwordMatchValidator } from '@src/app/core/_validators/password.validator'; export interface UpdateUserPassword { @@ -85,6 +86,9 @@ export class AccountSettingsComponent implements OnInit, OnDestroy { this.createForm(); this.loadUserSettings(); this.createUpdatePassForm(); + + this.form.markAllAsTouched(); + this.form.updateValueAndValidity(); } /** @@ -102,7 +106,7 @@ export class AccountSettingsComponent implements OnInit, OnDestroy { this.form = new FormGroup({ name: new FormControl({ value: '', disabled: true }), // disabled, no validators needed registeredSince: new FormControl({ value: '', disabled: true }), // disabled, no validators needed - email: new FormControl('', [Validators.required, Validators.email]) + email: new FormControl('', [Validators.required, emailValidator]) }); } diff --git a/src/app/core/_services/metadata.service.ts b/src/app/core/_services/metadata.service.ts index 135d5aeb5..29f344c33 100644 --- a/src/app/core/_services/metadata.service.ts +++ b/src/app/core/_services/metadata.service.ts @@ -10,6 +10,7 @@ import { ConfigTooltipsLevel, TooltipService } from '@services/shared/tooltip.se import { fileFormat } from '@src/app/core/_constants/files.config'; import { ACCESS_GROUP_FIELD_MAPPING, FieldMapping } from '@src/app/core/_constants/select.config'; import { Option, Setting, dateFormats, proxytype, serverlog } from '@src/app/core/_constants/settings.config'; +import { emailValidator } from '@src/app/core/_validators/email.validator'; import { urlValidator } from '@src/app/core/_validators/url.validator'; /** @@ -1138,7 +1139,7 @@ export class MetadataService { label: 'Email', type: 'email', requiredasterisk: true, - validators: [Validators.required, Validators.email] + validators: [Validators.required, emailValidator] }, { name: 'globalPermissionGroupId', diff --git a/src/app/core/_validators/email.validator.ts b/src/app/core/_validators/email.validator.ts new file mode 100644 index 000000000..a6e23de7c --- /dev/null +++ b/src/app/core/_validators/email.validator.ts @@ -0,0 +1,14 @@ +import { AbstractControl, ValidationErrors } from '@angular/forms'; + +/** + * Validates email-adresses with a regex, which mirrors PHP's FILTER_VALIDATE_EMAIL internal implementation + */ +export const emailValidator = (control: AbstractControl): ValidationErrors | null => { + const value = control.value as string; + if (!value) return null; + + const phpEmailRegex = + /^[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-zA-Z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]*[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/; + + return phpEmailRegex.test(value) ? null : { email: true }; +}; diff --git a/src/app/users/new-user/new-user.form.ts b/src/app/users/new-user/new-user.form.ts index 1f3459898..ac93a1d49 100644 --- a/src/app/users/new-user/new-user.form.ts +++ b/src/app/users/new-user/new-user.form.ts @@ -3,6 +3,8 @@ */ import { FormControl, FormGroup, Validators } from '@angular/forms'; +import { emailValidator } from '@src/app/core/_validators/email.validator'; + /** * Interface definition for NewUserForm * @prop username Username @@ -24,7 +26,7 @@ export interface NewUserForm { export const getNewUserForm = () => { return new FormGroup({ username: new FormControl('', [Validators.required]), - email: new FormControl('', [Validators.required, Validators.email]), + email: new FormControl('', [Validators.required, emailValidator]), globalPermissionGroupId: new FormControl(undefined, [Validators.required]), isValid: new FormControl(false) });