Skip to content

Commit 76da08b

Browse files
authored
Merge pull request #3942 from dpalou/MOBILE-4400
MOBILE-4400 signup: Check extendedusernamechars setting
2 parents 5ef95b6 + ae79dc9 commit 76da08b

File tree

7 files changed

+124
-16
lines changed

7 files changed

+124
-16
lines changed

scripts/langindex.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2092,6 +2092,7 @@
20922092
"core.login.invalidsite": "local_moodlemobileapp",
20932093
"core.login.invalidtime": "local_moodlemobileapp",
20942094
"core.login.invalidurl": "scorm",
2095+
"core.login.invalidusername": "moodle",
20952096
"core.login.invalidvaluemax": "local_moodlemobileapp",
20962097
"core.login.invalidvaluemin": "local_moodlemobileapp",
20972098
"core.login.login": "moodle",
@@ -2155,6 +2156,7 @@
21552156
"core.login.supplyinfo": "moodle",
21562157
"core.login.toggleremove": "local_moodlemobileapp",
21572158
"core.login.username": "moodle",
2159+
"core.login.usernamelowercase": "moodle",
21582160
"core.login.usernameoremail": "moodle",
21592161
"core.login.usernamerequired": "local_moodlemobileapp",
21602162
"core.login.usernotaddederror": "error",

src/core/components/input-errors/core-input-errors.html

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
<ng-container *ngIf="control && control.dirty && !control.valid">
22
<ng-container *ngFor="let error of errorKeys">
33
<div *ngIf="control.hasError(error)" class="core-input-error">
4-
<span *ngIf="errorMessages && errorMessages[error]">{{ errorMessages[error] | translate }}</span>
5-
<span *ngIf="(!errorMessages || !errorMessages[error]) && error === 'max' && control.errors?.max">
6-
{{ 'core.login.invalidvaluemax' | translate:{$a: control.errors!.max.max} }}
7-
</span>
8-
<span *ngIf="(!errorMessages || !errorMessages[error]) && error === 'min' && control.errors?.min">
9-
{{ 'core.login.invalidvaluemin' | translate:{$a: control.errors!.min.min} }}
10-
</span>
4+
<ng-container *ngIf="error !== 'pattern'">
5+
<span *ngIf="errorMessages && errorMessages[error]">{{ errorMessages[error] | translate }}</span>
6+
<span *ngIf="(!errorMessages || !errorMessages[error]) && error === 'max' && control.errors?.max">
7+
{{ 'core.login.invalidvaluemax' | translate:{$a: control.errors!.max.max} }}
8+
</span>
9+
<span *ngIf="(!errorMessages || !errorMessages[error]) && error === 'min' && control.errors?.min">
10+
{{ 'core.login.invalidvaluemin' | translate:{$a: control.errors!.min.min} }}
11+
</span>
12+
</ng-container>
13+
<ng-container *ngIf="error === 'pattern' && getPatternErrorMessage() as errorMessage">
14+
<span>{{ errorMessage | translate }}</span>
15+
</ng-container>
1116
</div>
1217
</ng-container>
1318
</ng-container>

src/core/components/input-errors/input-errors.ts

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ import { FormControl } from '@angular/forms';
4141
export class CoreInputErrorsComponent implements OnInit, OnChanges {
4242

4343
@Input() control?: FormControl<unknown>; // Needed to be able to check the validity of the input.
44-
@Input() errorMessages: Record<string, string> = {}; // Error messages to show. Keys must be the name of the error.
44+
@Input() errorMessages: CoreInputErrorsMessages = {}; // Error messages to show. Keys must be the name of the error.
4545
@Input() errorText = ''; // Set other non automatic errors.
4646
errorKeys: string[] = [];
4747

@@ -124,4 +124,44 @@ export class CoreInputErrorsComponent implements OnInit, OnChanges {
124124
}
125125
}
126126

127+
/**
128+
* Get error message for pattern error.
129+
*
130+
* @returns Error message, undefined if not found.
131+
*/
132+
getPatternErrorMessage(): string | undefined {
133+
const patternError = this.control?.errors?.pattern;
134+
if (!this.errorMessages?.pattern || !patternError) {
135+
return;
136+
}
137+
138+
if (typeof this.errorMessages.pattern === 'string') {
139+
return this.errorMessages.pattern;
140+
}
141+
142+
return this.errorMessages.pattern[patternError.requiredPattern];
143+
}
144+
127145
}
146+
147+
/**
148+
* Error messages for each type of error.
149+
* Error messages will be translated in the template, they don't need to be translated already.
150+
*/
151+
export type CoreInputErrorsMessages = {
152+
required?: string;
153+
requiredTrue?: string;
154+
email?: string;
155+
date?: string;
156+
datetime?: string;
157+
datetimelocal?: string;
158+
time?: string;
159+
url?: string;
160+
max?: string;
161+
min?: string;
162+
maxlength?: string;
163+
minlength?: string;
164+
// For pattern errors you can define an error for all patterns (string), or one error per pattern.
165+
// In the latter case, the key of the object is the pattern and the value is the error message identifier.
166+
pattern?: string | Record<string,string>;
167+
};

src/core/features/login/lang.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
"invalidsite": "The site URL is not valid.",
6363
"invalidtime": "Time not valid",
6464
"invalidurl": "Invalid URL specified",
65+
"invalidusername": "The username can only contain alphanumeric lowercase characters (letters and numbers), underscore (_), hyphen (-), period (.) or at symbol (@).",
6566
"invalidvaluemax": "The maximum value is {{$a}}",
6667
"invalidvaluemin": "The minimum value is {{$a}}",
6768
"login": "Log in",
@@ -125,6 +126,7 @@
125126
"supplyinfo": "More details",
126127
"toggleremove": "Edit accounts list",
127128
"username": "Username",
129+
"usernamelowercase": "Only lowercase letters allowed",
128130
"usernameoremail": "Enter either username or email address",
129131
"usernamerequired": "Username required",
130132
"usernotaddederror": "User not added - error",

src/core/features/login/pages/email-signup/email-signup.ts

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import { CorePath } from '@singletons/path';
3535
import { CoreDom } from '@singletons/dom';
3636
import { CoreSitesFactory } from '@services/sites-factory';
3737
import { EMAIL_SIGNUP_FEATURE_NAME } from '@features/login/constants';
38+
import { CoreInputErrorsMessages } from '@components/input-errors/input-errors';
3839

3940
/**
4041
* Page to signup using email.
@@ -46,6 +47,10 @@ import { EMAIL_SIGNUP_FEATURE_NAME } from '@features/login/constants';
4647
})
4748
export class CoreLoginEmailSignupPage implements OnInit {
4849

50+
// Accept A-Z in strict chars pattern to be able to differentiate it from the lowercase pattern.
51+
protected static readonly USERNAME_STRICT_CHARS_PATTERN = '^[A-Z-.@_a-z0-9]*$';
52+
protected static readonly USERNAME_LOWERCASE_PATTERN = '^[^A-Z]*$';
53+
4954
@ViewChild(CoreRecaptchaComponent) recaptchaComponent?: CoreRecaptchaComponent;
5055
@ViewChild('ageForm') ageFormElement?: ElementRef;
5156
@ViewChild('signupFormEl') signupFormElement?: ElementRef;
@@ -77,12 +82,12 @@ export class CoreLoginEmailSignupPage implements OnInit {
7782
supportEmail?: string;
7883

7984
// Validation errors.
80-
usernameErrors: Record<string, string>;
81-
passwordErrors: Record<string, string>;
82-
emailErrors: Record<string, string>;
83-
email2Errors: Record<string, string>;
84-
policyErrors: Record<string, string>;
85-
namefieldsErrors?: Record<string, Record<string, string>>;
85+
usernameErrors: CoreInputErrorsMessages;
86+
passwordErrors: CoreInputErrorsMessages;
87+
emailErrors: CoreInputErrorsMessages;
88+
email2Errors: CoreInputErrorsMessages;
89+
policyErrors: CoreInputErrorsMessages;
90+
namefieldsErrors?: Record<string, CoreInputErrorsMessages>;
8691

8792
constructor(
8893
protected fb: FormBuilder,
@@ -98,14 +103,19 @@ export class CoreLoginEmailSignupPage implements OnInit {
98103

99104
// Create the signupForm with the basic controls. More controls will be added later.
100105
this.signupForm = this.fb.group({
101-
username: ['', Validators.required],
102106
password: ['', Validators.required],
103107
email: ['', Validators.compose([Validators.required, Validators.email])],
104108
email2: ['', Validators.compose([Validators.required, Validators.email])],
105109
});
106110

107111
// Setup validation errors.
108-
this.usernameErrors = { required: 'core.login.usernamerequired' };
112+
this.usernameErrors = {
113+
required: 'core.login.usernamerequired',
114+
pattern: {
115+
[CoreLoginEmailSignupPage.USERNAME_STRICT_CHARS_PATTERN]: 'core.login.invalidusername',
116+
[CoreLoginEmailSignupPage.USERNAME_LOWERCASE_PATTERN]: 'core.login.usernamelowercase',
117+
},
118+
};
109119
this.passwordErrors = { required: 'core.login.passwordrequired' };
110120
this.emailErrors = { required: 'core.login.missingemail' };
111121
this.policyErrors = { required: 'core.login.policyagree' };
@@ -140,6 +150,13 @@ export class CoreLoginEmailSignupPage implements OnInit {
140150
* Complete the FormGroup using the settings received from server.
141151
*/
142152
protected completeFormGroup(): void {
153+
const checkStrictChars = this.settings?.extendedusernamechars === false;
154+
this.signupForm.addControl('username', this.fb.control('', Validators.compose([
155+
Validators.required,
156+
Validators.pattern(CoreLoginEmailSignupPage.USERNAME_LOWERCASE_PATTERN),
157+
checkStrictChars ? Validators.pattern(CoreLoginEmailSignupPage.USERNAME_STRICT_CHARS_PATTERN) : undefined,
158+
])));
159+
143160
this.signupForm.addControl('city', this.fb.control(this.settings?.defaultcity || ''));
144161
this.signUpCountryControl = this.fb.control(this.settings?.country || '', { nonNullable: true });
145162
this.signupForm.addControl('country', this.signUpCountryControl);

src/core/features/login/services/login-helper.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1553,6 +1553,7 @@ export type AuthEmailSignupSettings = {
15531553
sitepolicyhandler?: string; // Site policy handler.
15541554
defaultcity?: string; // Default city.
15551555
country?: string; // Default country.
1556+
extendedusernamechars?: boolean; // @since 4.4. Extended characters in usernames or no.
15561557
profilefields?: AuthEmailSignupProfileField[]; // Required profile fields.
15571558
recaptchapublickey?: string; // Recaptcha public key.
15581559
recaptchachallengehash?: string; // Recaptcha challenge hash.

src/core/features/login/tests/behat/signup.feature

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,3 +184,44 @@ Feature: Test signup in app
184184
And I should find "1 January 2010, 11:45 AM" in the app
185185
And I should find "This is my description" in the app
186186
And I should find "https://moodle.com" in the app
187+
188+
@lms_from4.4
189+
Scenario: Check extended characters in usernames show error if setting is disabled
190+
Given the following config values are set as admin:
191+
| extendedusernamechars | 0 |
192+
When I launch the app
193+
And I set the field "Your site" to "$WWWROOT" in the app
194+
And I press "Connect to your site" in the app
195+
And I press "Create new account" in the app
196+
And I set the following fields to these values in the app:
197+
| Username | u1$ |
198+
Then I should find "The username can only contain alphanumeric" in the app
199+
200+
When I set the following fields to these values in the app:
201+
| Username | u1 |
202+
Then I should not find "The username can only contain alphanumeric" in the app
203+
204+
@lms_from4.4
205+
Scenario: Check can include extended characters in usernames if setting is enabled
206+
Given the following config values are set as admin:
207+
| extendedusernamechars | 1 |
208+
When I launch the app
209+
And I set the field "Your site" to "$WWWROOT" in the app
210+
And I press "Connect to your site" in the app
211+
And I press "Create new account" in the app
212+
And I set the following fields to these values in the app:
213+
| Username | u1U |
214+
Then I should find "Only lowercase letters allowed" in the app
215+
216+
When I set the following fields to these values in the app:
217+
| Username | u1$ |
218+
Then I should not find "The username can only contain alphanumeric" in the app
219+
220+
When I set the following fields to these values in the app:
221+
| Password | pu1 |
222+
| Email address | u1@u1.com |
223+
| Email (again) | u1@u1.com |
224+
| First name | User |
225+
| Last name | Test |
226+
And I press "Create my new account" in the app
227+
Then I should find "An email should have been sent to your address" in the app

0 commit comments

Comments
 (0)