From 3d328d7b5a1a37594a86ba41f6a308da83d16cb2 Mon Sep 17 00:00:00 2001 From: tischsoic Date: Thu, 3 Sep 2026 12:48:30 +0200 Subject: [PATCH 01/11] IBX-11973: Added validation to login and forgot password screens MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Sign in button is no longer disabled until both fields are filled; empty or malformed fields are now reported inline on submit, the same way the rest of the back office does it, instead of native browser bubbles. Backend errors on the forgot password screens now also mark the input invalid, following the pattern IBX-11959 introduced on Set new password — its error-row macro is extracted to a shared account partial. Co-Authored-By: Claude --- .../Resources/encore/ibexa.js.config.js | 3 +- .../scripts/admin.account.form.validation.js | 64 +++++++++++++++++++ .../Resources/public/js/scripts/login.js | 31 --------- .../account/forgot_password/index.html.twig | 40 +++++++++++- .../index_with_login.html.twig | 35 +++++++++- .../admin/account/login/index.html.twig | 26 +++++++- .../themes/admin/account/macros.html.twig | 20 ++++++ .../account/reset_password/index.html.twig | 19 +----- 8 files changed, 183 insertions(+), 55 deletions(-) create mode 100644 src/bundle/Resources/public/js/scripts/admin.account.form.validation.js delete mode 100644 src/bundle/Resources/public/js/scripts/login.js create mode 100644 src/bundle/Resources/views/themes/admin/account/macros.html.twig diff --git a/src/bundle/Resources/encore/ibexa.js.config.js b/src/bundle/Resources/encore/ibexa.js.config.js index f20be70610..22797d3824 100644 --- a/src/bundle/Resources/encore/ibexa.js.config.js +++ b/src/bundle/Resources/encore/ibexa.js.config.js @@ -250,11 +250,12 @@ module.exports = (Encore) => { .addEntry('ibexa-admin-ui-login-js', [ path.resolve(__dirname, '../public/js/scripts/admin.input.text.js'), path.resolve('./vendor/ibexa/design-system-twig/src/bundle/Resources/public/ts/init_components.ts'), - path.resolve(__dirname, '../public/js/scripts/login.js'), + path.resolve(__dirname, '../public/js/scripts/admin.account.form.validation.js'), ]) .addEntry('ibexa-admin-ui-reset-password-js', [ path.resolve(__dirname, '../public/js/scripts/admin.input.text.js'), path.resolve('./vendor/ibexa/design-system-twig/src/bundle/Resources/public/ts/init_components.ts'), + path.resolve(__dirname, '../public/js/scripts/admin.account.form.validation.js'), ]) .addEntry('ibexa-admin-ui-user-invitation-modal', [path.resolve(__dirname, '../public/js/scripts/user.invitation.modal.js')]) .addEntry('ibexa-admin-ui-tabs-js', [ diff --git a/src/bundle/Resources/public/js/scripts/admin.account.form.validation.js b/src/bundle/Resources/public/js/scripts/admin.account.form.validation.js new file mode 100644 index 0000000000..3b2d886f1e --- /dev/null +++ b/src/bundle/Resources/public/js/scripts/admin.account.form.validation.js @@ -0,0 +1,64 @@ +(function (global, doc) { + const EMAIL_REGEXP = + /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; + + const findErrorType = (input) => { + const value = input.value.trim(); + + if (!value) { + return 'empty'; + } + + if (input.type === 'email' && !EMAIL_REGEXP.test(value)) { + return 'invalid-email'; + } + + return null; + }; + const toggleFieldState = (input, errorType) => { + const field = input.closest('.form-group'); + const label = field.querySelector('.ids-label'); + const hasError = errorType !== null; + + field.classList.toggle('has-error', hasError); + input.classList.toggle('ids-input--error', hasError); + label?.classList.toggle('ids-label--error', hasError); + + field.querySelectorAll('.ibexa-form-error__row').forEach((errorRow) => { + errorRow.hidden = errorRow.dataset.errorType !== errorType; + }); + }; + const checkIsInputValid = (input) => { + const errorType = findErrorType(input); + + toggleFieldState(input, errorType); + + return errorType === null; + }; + const handleSubmit = (event) => { + const form = event.currentTarget; + const inputs = [...form.querySelectorAll('input[required]')]; + const invalidInputs = inputs.filter((input) => !checkIsInputValid(input)); + + if (invalidInputs.length) { + event.preventDefault(); + + invalidInputs[0].focus(); + } + }; + const handleInput = (event) => { + const input = event.currentTarget; + + if (input.closest('.form-group').classList.contains('has-error')) { + checkIsInputValid(input); + } + }; + + doc.querySelectorAll('form[data-account-validate]').forEach((form) => { + form.addEventListener('submit', handleSubmit, false); + + form.querySelectorAll('input[required]').forEach((input) => { + input.addEventListener('input', handleInput, false); + }); + }); +})(window, window.document); diff --git a/src/bundle/Resources/public/js/scripts/login.js b/src/bundle/Resources/public/js/scripts/login.js deleted file mode 100644 index 4f59528359..0000000000 --- a/src/bundle/Resources/public/js/scripts/login.js +++ /dev/null @@ -1,31 +0,0 @@ -(function (global, doc) { - const AUTOFILL_TIMEOUT = 500; - const loginBtn = doc.querySelector('.ibexa-login__btn--sign-in'); - const nameInput = doc.querySelector('.ibexa-login__input--name'); - const passwordInput = doc.querySelector('.ibexa-login__input--password'); - const toggleLoginBtnState = () => { - const shouldBeDisabled = !nameInput.value || !passwordInput.value; - - loginBtn.toggleAttribute('disabled', shouldBeDisabled); - }; - const handleAutofill = () => { - const isNameInputAutofilled = nameInput.matches(':-webkit-autofill'); - const isPasswordInputAutofilled = nameInput.matches(':-webkit-autofill'); - const isAutofilled = isNameInputAutofilled && isPasswordInputAutofilled; - - if (isAutofilled) { - loginBtn.removeAttribute('disabled'); - } - }; - - if (loginBtn) { - nameInput.addEventListener('keyup', toggleLoginBtnState, false); - nameInput.addEventListener('change', toggleLoginBtnState, false); - passwordInput.addEventListener('keyup', toggleLoginBtnState, false); - passwordInput.addEventListener('change', toggleLoginBtnState, false); - - toggleLoginBtnState(); - - global.setTimeout(handleAutofill, AUTOFILL_TIMEOUT); - } -})(window, window.document); diff --git a/src/bundle/Resources/views/themes/admin/account/forgot_password/index.html.twig b/src/bundle/Resources/views/themes/admin/account/forgot_password/index.html.twig index e7ffa3b29c..c0bf3f14e4 100644 --- a/src/bundle/Resources/views/themes/admin/account/forgot_password/index.html.twig +++ b/src/bundle/Resources/views/themes/admin/account/forgot_password/index.html.twig @@ -3,14 +3,50 @@ {% form_theme form_forgot_user_password '@ibexadesign/ui/form_fields.html.twig' %} {%- block content -%} + {% from '@ibexadesign/account/macros.html.twig' import error_row %} + {% if reason == userForgotPasswordReasonMigration %}

{{ 'ezplatform.forgot_password.reset_your_password.reason.migration'|trans|desc('Your password has expired, change it.') }}

{% endif %} {% if form_forgot_user_password is defined %} - {{ form_start(form_forgot_user_password, {'attr': {'class': 'ibexa-form-validate ibexa-login__forgot-password-form'}}) }} + {% set email_form = form_forgot_user_password.email %} + {% set email_errors = email_form.vars.errors %} + {% set email_label = email_form.vars.label|trans({}, email_form.vars.translation_domain) %} + + {{ form_start(form_forgot_user_password, { + attr: { + novalidate: true, + class: 'ibexa-login__forgot-password-form', + 'data-account-validate': true, + }, + }) }}
- {{ form_row(form_forgot_user_password.email, {'attr': {'class': 'ibexa-login__input'}}) }} +
{{ form_widget(form_forgot_user_password.reset, {'attr': {'class': 'ids-btn ids-btn--primary ids-btn--medium ibexa-login__btn ibexa-login__btn--reset-password'}}) }} diff --git a/src/bundle/Resources/views/themes/admin/account/forgot_password/index_with_login.html.twig b/src/bundle/Resources/views/themes/admin/account/forgot_password/index_with_login.html.twig index 12a513896e..ae07111a3d 100644 --- a/src/bundle/Resources/views/themes/admin/account/forgot_password/index_with_login.html.twig +++ b/src/bundle/Resources/views/themes/admin/account/forgot_password/index_with_login.html.twig @@ -3,14 +3,43 @@ {% form_theme form_forgot_user_password_with_login '@ibexadesign/ui/form_fields.html.twig' %} {%- block content -%} + {% from '@ibexadesign/account/macros.html.twig' import error_row %} +

{{ 'ezplatform.forgot_user_password.login'|trans|desc('This email is connected with several accounts. Enter your login instead.') }}

{% if form_forgot_user_password_with_login is defined %} - {{ form_start(form_forgot_user_password_with_login, {'attr': {'class': 'ibexa-form-validate'} }) }} + {% set login_form = form_forgot_user_password_with_login.login %} + {% set login_errors = login_form.vars.errors %} + + {{ form_start(form_forgot_user_password_with_login, { + attr: { + novalidate: true, + 'data-account-validate': true, + }, + }) }}
- {{ form_row(form_forgot_user_password_with_login.login) }} +
{{ form_widget(form_forgot_user_password_with_login.reset, {'attr': {'class': 'ids-btn ids-btn--primary ids-btn--medium ibexa-login__btn ibexa-login__btn--reset-password'}}) }} @@ -19,4 +48,6 @@

{{ 'ezplatform.forgot_user_password.contact_administrator'|trans|desc('If you do not remember your login, contact your Administrator.') }}

+ + {{ encore_entry_script_tags('ibexa-admin-ui-reset-password-js', null, 'ibexa') }} {%- endblock content -%} diff --git a/src/bundle/Resources/views/themes/admin/account/login/index.html.twig b/src/bundle/Resources/views/themes/admin/account/login/index.html.twig index 446fa64c64..f24225374f 100644 --- a/src/bundle/Resources/views/themes/admin/account/login/index.html.twig +++ b/src/bundle/Resources/views/themes/admin/account/login/index.html.twig @@ -9,7 +9,13 @@ {% block login_form %} {{ ibexa_twig_component_group('admin-ui-login-form-before') }} -
+
{% block login_form_errors %} {% if error %} @@ -23,6 +29,8 @@ {% endblock %} {% block login_form_fields %} + {% from '@ibexadesign/account/macros.html.twig' import error_row %} +
@@ -68,7 +90,6 @@ type="primary" htmlType="submit" class="ibexa-login__btn ibexa-login__btn--sign-in" - :disabled="true" > {{ 'authentication.sign_in'|trans|desc('Sign in') }} @@ -80,7 +101,6 @@ type="secondary" htmlType="submit" class="ibexa-login__btn" - :disabled="true" > {{ 'authentication.sso'|trans|desc('Sign in with SSO') }} diff --git a/src/bundle/Resources/views/themes/admin/account/macros.html.twig b/src/bundle/Resources/views/themes/admin/account/macros.html.twig new file mode 100644 index 0000000000..9bf90b4fda --- /dev/null +++ b/src/bundle/Resources/views/themes/admin/account/macros.html.twig @@ -0,0 +1,20 @@ +{% macro error_row(message, error_type = null, is_hidden = false) %} + + + + + {{ message }} + +{% endmacro %} + +{% macro field_errors(errors) %} +
+ {% for error in errors %} + {{ _self.error_row(error.message) }} + {% endfor %} +
+{% endmacro %} diff --git a/src/bundle/Resources/views/themes/admin/account/reset_password/index.html.twig b/src/bundle/Resources/views/themes/admin/account/reset_password/index.html.twig index 49acba07e1..eb4c4597c8 100644 --- a/src/bundle/Resources/views/themes/admin/account/reset_password/index.html.twig +++ b/src/bundle/Resources/views/themes/admin/account/reset_password/index.html.twig @@ -4,21 +4,8 @@ {% form_theme form_reset_user_password '@ibexadesign/ui/form_fields.html.twig' %} {% endif %} -{% macro password_field_errors(errors) %} -
- {% for error in errors %} - - - - - {{ error.message }} - - {% endfor %} -
-{% endmacro %} - {%- block content -%} - {% import _self as reset_password_macros %} + {% from '@ibexadesign/account/macros.html.twig' import field_errors %} {% if form_reset_user_password is defined %} @@ -72,7 +59,7 @@ : {} ) }} {% if policy_errors is not empty %} - {{ reset_password_macros.password_field_errors(policy_errors) }} + {{ field_errors(policy_errors) }} {% endif %}
@@ -82,7 +69,7 @@ : {} ) }} {% if other_errors is not empty %} - {{ reset_password_macros.password_field_errors(other_errors) }} + {{ field_errors(other_errors) }} {% endif %}
From c0164be564918e33ca6dc9bc9c7c41190099816d Mon Sep 17 00:00:00 2001 From: tischsoic Date: Thu, 3 Sep 2026 13:02:30 +0200 Subject: [PATCH 02/11] IBX-11973: Dropped the client-side validation from the account screens Validation goes back to the backend: the forgot password screens post their empty value, and the constraint violation now also marks the input invalid instead of only printing the message. Native browser validation stays as the safety net on the login form. This leaves the Set new password screen untouched, so its error-row macro no longer needs extracting. Co-Authored-By: Claude --- .../scripts/admin.account.form.validation.js | 64 ------------------- .../account/forgot_password/index.html.twig | 45 ++----------- .../index_with_login.html.twig | 39 ++--------- .../admin/account/login/index.html.twig | 24 +------ .../themes/admin/account/macros.html.twig | 20 ------ .../account/reset_password/index.html.twig | 19 +++++- 6 files changed, 30 insertions(+), 181 deletions(-) delete mode 100644 src/bundle/Resources/public/js/scripts/admin.account.form.validation.js delete mode 100644 src/bundle/Resources/views/themes/admin/account/macros.html.twig diff --git a/src/bundle/Resources/public/js/scripts/admin.account.form.validation.js b/src/bundle/Resources/public/js/scripts/admin.account.form.validation.js deleted file mode 100644 index 3b2d886f1e..0000000000 --- a/src/bundle/Resources/public/js/scripts/admin.account.form.validation.js +++ /dev/null @@ -1,64 +0,0 @@ -(function (global, doc) { - const EMAIL_REGEXP = - /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; - - const findErrorType = (input) => { - const value = input.value.trim(); - - if (!value) { - return 'empty'; - } - - if (input.type === 'email' && !EMAIL_REGEXP.test(value)) { - return 'invalid-email'; - } - - return null; - }; - const toggleFieldState = (input, errorType) => { - const field = input.closest('.form-group'); - const label = field.querySelector('.ids-label'); - const hasError = errorType !== null; - - field.classList.toggle('has-error', hasError); - input.classList.toggle('ids-input--error', hasError); - label?.classList.toggle('ids-label--error', hasError); - - field.querySelectorAll('.ibexa-form-error__row').forEach((errorRow) => { - errorRow.hidden = errorRow.dataset.errorType !== errorType; - }); - }; - const checkIsInputValid = (input) => { - const errorType = findErrorType(input); - - toggleFieldState(input, errorType); - - return errorType === null; - }; - const handleSubmit = (event) => { - const form = event.currentTarget; - const inputs = [...form.querySelectorAll('input[required]')]; - const invalidInputs = inputs.filter((input) => !checkIsInputValid(input)); - - if (invalidInputs.length) { - event.preventDefault(); - - invalidInputs[0].focus(); - } - }; - const handleInput = (event) => { - const input = event.currentTarget; - - if (input.closest('.form-group').classList.contains('has-error')) { - checkIsInputValid(input); - } - }; - - doc.querySelectorAll('form[data-account-validate]').forEach((form) => { - form.addEventListener('submit', handleSubmit, false); - - form.querySelectorAll('input[required]').forEach((input) => { - input.addEventListener('input', handleInput, false); - }); - }); -})(window, window.document); diff --git a/src/bundle/Resources/views/themes/admin/account/forgot_password/index.html.twig b/src/bundle/Resources/views/themes/admin/account/forgot_password/index.html.twig index c0bf3f14e4..097c66f8dd 100644 --- a/src/bundle/Resources/views/themes/admin/account/forgot_password/index.html.twig +++ b/src/bundle/Resources/views/themes/admin/account/forgot_password/index.html.twig @@ -3,50 +3,19 @@ {% form_theme form_forgot_user_password '@ibexadesign/ui/form_fields.html.twig' %} {%- block content -%} - {% from '@ibexadesign/account/macros.html.twig' import error_row %} - {% if reason == userForgotPasswordReasonMigration %}

{{ 'ezplatform.forgot_password.reset_your_password.reason.migration'|trans|desc('Your password has expired, change it.') }}

{% endif %} {% if form_forgot_user_password is defined %} - {% set email_form = form_forgot_user_password.email %} - {% set email_errors = email_form.vars.errors %} - {% set email_label = email_form.vars.label|trans({}, email_form.vars.translation_domain) %} - - {{ form_start(form_forgot_user_password, { - attr: { - novalidate: true, - class: 'ibexa-login__forgot-password-form', - 'data-account-validate': true, - }, - }) }} + {{ form_start(form_forgot_user_password, {'attr': {'class': 'ibexa-form-validate ibexa-login__forgot-password-form'}}) }}
- + {{ form_row(form_forgot_user_password.email, { + 'attr': { + 'class': 'ibexa-login__input', + 'error': form_forgot_user_password.email.vars.errors is not empty, + }, + }) }}
{{ form_widget(form_forgot_user_password.reset, {'attr': {'class': 'ids-btn ids-btn--primary ids-btn--medium ibexa-login__btn ibexa-login__btn--reset-password'}}) }} diff --git a/src/bundle/Resources/views/themes/admin/account/forgot_password/index_with_login.html.twig b/src/bundle/Resources/views/themes/admin/account/forgot_password/index_with_login.html.twig index ae07111a3d..8e011fc272 100644 --- a/src/bundle/Resources/views/themes/admin/account/forgot_password/index_with_login.html.twig +++ b/src/bundle/Resources/views/themes/admin/account/forgot_password/index_with_login.html.twig @@ -3,43 +3,18 @@ {% form_theme form_forgot_user_password_with_login '@ibexadesign/ui/form_fields.html.twig' %} {%- block content -%} - {% from '@ibexadesign/account/macros.html.twig' import error_row %} -

{{ 'ezplatform.forgot_user_password.login'|trans|desc('This email is connected with several accounts. Enter your login instead.') }}

{% if form_forgot_user_password_with_login is defined %} - {% set login_form = form_forgot_user_password_with_login.login %} - {% set login_errors = login_form.vars.errors %} - - {{ form_start(form_forgot_user_password_with_login, { - attr: { - novalidate: true, - 'data-account-validate': true, - }, - }) }} + {{ form_start(form_forgot_user_password_with_login, {'attr': {'class': 'ibexa-form-validate'} }) }}
- + {{ form_row(form_forgot_user_password_with_login.login, { + 'attr': { + 'error': form_forgot_user_password_with_login.login.vars.errors is not empty, + }, + }) }}
{{ form_widget(form_forgot_user_password_with_login.reset, {'attr': {'class': 'ids-btn ids-btn--primary ids-btn--medium ibexa-login__btn ibexa-login__btn--reset-password'}}) }} @@ -48,6 +23,4 @@

{{ 'ezplatform.forgot_user_password.contact_administrator'|trans|desc('If you do not remember your login, contact your Administrator.') }}

- - {{ encore_entry_script_tags('ibexa-admin-ui-reset-password-js', null, 'ibexa') }} {%- endblock content -%} diff --git a/src/bundle/Resources/views/themes/admin/account/login/index.html.twig b/src/bundle/Resources/views/themes/admin/account/login/index.html.twig index f24225374f..5dd6c2fa77 100644 --- a/src/bundle/Resources/views/themes/admin/account/login/index.html.twig +++ b/src/bundle/Resources/views/themes/admin/account/login/index.html.twig @@ -9,13 +9,7 @@ {% block login_form %} {{ ibexa_twig_component_group('admin-ui-login-form-before') }} - +
{% block login_form_errors %} {% if error %} @@ -29,8 +23,6 @@ {% endblock %} {% block login_form_fields %} - {% from '@ibexadesign/account/macros.html.twig' import error_row %} -
diff --git a/src/bundle/Resources/views/themes/admin/account/macros.html.twig b/src/bundle/Resources/views/themes/admin/account/macros.html.twig deleted file mode 100644 index 9bf90b4fda..0000000000 --- a/src/bundle/Resources/views/themes/admin/account/macros.html.twig +++ /dev/null @@ -1,20 +0,0 @@ -{% macro error_row(message, error_type = null, is_hidden = false) %} - - - - - {{ message }} - -{% endmacro %} - -{% macro field_errors(errors) %} -
- {% for error in errors %} - {{ _self.error_row(error.message) }} - {% endfor %} -
-{% endmacro %} diff --git a/src/bundle/Resources/views/themes/admin/account/reset_password/index.html.twig b/src/bundle/Resources/views/themes/admin/account/reset_password/index.html.twig index eb4c4597c8..49acba07e1 100644 --- a/src/bundle/Resources/views/themes/admin/account/reset_password/index.html.twig +++ b/src/bundle/Resources/views/themes/admin/account/reset_password/index.html.twig @@ -4,8 +4,21 @@ {% form_theme form_reset_user_password '@ibexadesign/ui/form_fields.html.twig' %} {% endif %} +{% macro password_field_errors(errors) %} +
+ {% for error in errors %} + + + + + {{ error.message }} + + {% endfor %} +
+{% endmacro %} + {%- block content -%} - {% from '@ibexadesign/account/macros.html.twig' import field_errors %} + {% import _self as reset_password_macros %} {% if form_reset_user_password is defined %} @@ -59,7 +72,7 @@ : {} ) }} {% if policy_errors is not empty %} - {{ field_errors(policy_errors) }} + {{ reset_password_macros.password_field_errors(policy_errors) }} {% endif %}
@@ -69,7 +82,7 @@ : {} ) }} {% if other_errors is not empty %} - {{ field_errors(other_errors) }} + {{ reset_password_macros.password_field_errors(other_errors) }} {% endif %}
From 2047967b7c7a8ad9cb4547237ad65653ef74dd3a Mon Sep 17 00:00:00 2001 From: tischsoic Date: Thu, 3 Sep 2026 13:09:02 +0200 Subject: [PATCH 03/11] IBX-11973: Removed the validation script from the Encore entries The script itself was already deleted; the login and reset password entries still pointed at it. Co-Authored-By: Claude --- src/bundle/Resources/encore/ibexa.js.config.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/bundle/Resources/encore/ibexa.js.config.js b/src/bundle/Resources/encore/ibexa.js.config.js index 22797d3824..32a0b07254 100644 --- a/src/bundle/Resources/encore/ibexa.js.config.js +++ b/src/bundle/Resources/encore/ibexa.js.config.js @@ -250,12 +250,10 @@ module.exports = (Encore) => { .addEntry('ibexa-admin-ui-login-js', [ path.resolve(__dirname, '../public/js/scripts/admin.input.text.js'), path.resolve('./vendor/ibexa/design-system-twig/src/bundle/Resources/public/ts/init_components.ts'), - path.resolve(__dirname, '../public/js/scripts/admin.account.form.validation.js'), ]) .addEntry('ibexa-admin-ui-reset-password-js', [ path.resolve(__dirname, '../public/js/scripts/admin.input.text.js'), path.resolve('./vendor/ibexa/design-system-twig/src/bundle/Resources/public/ts/init_components.ts'), - path.resolve(__dirname, '../public/js/scripts/admin.account.form.validation.js'), ]) .addEntry('ibexa-admin-ui-user-invitation-modal', [path.resolve(__dirname, '../public/js/scripts/user.invitation.modal.js')]) .addEntry('ibexa-admin-ui-tabs-js', [ From 4c91ec3e6fa4ebd2b4169c4e1cb43ac4e72fba3e Mon Sep 17 00:00:00 2001 From: tischsoic Date: Thu, 3 Sep 2026 13:11:31 +0200 Subject: [PATCH 04/11] IBX-11973: Marked the label invalid alongside the field An invalid field reddens both its input and its label elsewhere in the back office; the forgot password screens only reddened the input. Co-Authored-By: Claude --- .../views/themes/admin/account/forgot_password/index.html.twig | 3 +++ .../admin/account/forgot_password/index_with_login.html.twig | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/bundle/Resources/views/themes/admin/account/forgot_password/index.html.twig b/src/bundle/Resources/views/themes/admin/account/forgot_password/index.html.twig index 097c66f8dd..00df085b4b 100644 --- a/src/bundle/Resources/views/themes/admin/account/forgot_password/index.html.twig +++ b/src/bundle/Resources/views/themes/admin/account/forgot_password/index.html.twig @@ -15,6 +15,9 @@ 'class': 'ibexa-login__input', 'error': form_forgot_user_password.email.vars.errors is not empty, }, + 'label_attr': { + 'class': form_forgot_user_password.email.vars.errors is not empty ? 'ids-label--error' : '', + }, }) }} {{ form_widget(form_forgot_user_password.reset, {'attr': {'class': 'ids-btn ids-btn--primary ids-btn--medium ibexa-login__btn ibexa-login__btn--reset-password'}}) }} diff --git a/src/bundle/Resources/views/themes/admin/account/forgot_password/index_with_login.html.twig b/src/bundle/Resources/views/themes/admin/account/forgot_password/index_with_login.html.twig index 8e011fc272..3a5ceb7c10 100644 --- a/src/bundle/Resources/views/themes/admin/account/forgot_password/index_with_login.html.twig +++ b/src/bundle/Resources/views/themes/admin/account/forgot_password/index_with_login.html.twig @@ -14,6 +14,9 @@ 'attr': { 'error': form_forgot_user_password_with_login.login.vars.errors is not empty, }, + 'label_attr': { + 'class': form_forgot_user_password_with_login.login.vars.errors is not empty ? 'ids-label--error' : '', + }, }) }} {{ form_widget(form_forgot_user_password_with_login.reset, {'attr': {'class': 'ids-btn ids-btn--primary ids-btn--medium ibexa-login__btn ibexa-login__btn--reset-password'}}) }} From 74034ce6da2d60029e6f0c4c6e3a778ed046eb5b Mon Sep 17 00:00:00 2001 From: tischsoic Date: Tue, 8 Sep 2026 07:44:42 +0200 Subject: [PATCH 05/11] IBX-11973: Reported blank login fields on the field itself MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clicking Sign in with an empty field now always submits — the browser no longer blocks it — and the field comes back marked invalid with a message beneath it, matching the forgot password screen. The banner is kept for failures that are not a blank field, so a wrong password still reads as "Bad credentials." rather than pointing at one of the fields. Requires ibexa/user to preserve which field was blank. Co-Authored-By: Claude --- .../admin/account/login/index.html.twig | 41 ++++++++++++++++--- 1 file changed, 35 insertions(+), 6 deletions(-) diff --git a/src/bundle/Resources/views/themes/admin/account/login/index.html.twig b/src/bundle/Resources/views/themes/admin/account/login/index.html.twig index 5dd6c2fa77..610d691fe9 100644 --- a/src/bundle/Resources/views/themes/admin/account/login/index.html.twig +++ b/src/bundle/Resources/views/themes/admin/account/login/index.html.twig @@ -3,16 +3,23 @@ {% set is_sso = false %} {%- block content -%} + {% set blank_fields = error is not null and error.blankFields is defined ? error.blankFields : [] %} + {% set has_username_error = 'username' in blank_fields %} + {% set has_password_error = 'password' in blank_fields %} + {% set show_error_alert = error is not null and blank_fields is empty %} + {% set username_label = 'authentication.login'|trans|desc('Login') %} + {% set password_label = 'authentication.password'|trans|desc('Password') %} + {# TODO: change to