From d70c3ab4c39e642d41138ab693af77dd6c258e8c Mon Sep 17 00:00:00 2001 From: ThibG Date: Tue, 11 Aug 2020 23:09:13 +0200 Subject: [PATCH] Add HTML form validation for the registration form (#14560) * Add HTML-level validation of username in sign-up form * Make required fields with incorrect values more visible * Enable HTML form validation for the registration form * Mark agreement checkbox as required client-side * Add minimum length to password * Add client-side password confirmation validation --- app/javascript/packs/public.js | 10 ++++++++++ app/javascript/styles/mastodon/forms.scss | 3 ++- app/views/about/_registration.html.haml | 8 ++++---- 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/app/javascript/packs/public.js b/app/javascript/packs/public.js index 08cc662e60..777182b7f5 100644 --- a/app/javascript/packs/public.js +++ b/app/javascript/packs/public.js @@ -116,6 +116,16 @@ function main() { new Rellax('.parallax', { speed: -1 }); } + delegate(document, '#registration_user_password_confirmation,#registration_user_password', 'input', () => { + const password = document.getElementById('registration_user_password'); + const confirmation = document.getElementById('registration_user_password_confirmation'); + if (password.value && password.value !== confirmation.value) { + confirmation.setCustomValidity((new IntlMessageFormat(messages['password_confirmation.mismatching'] || 'Password confirmation does not match', locale)).format()); + } else { + confirmation.setCustomValidity(''); + } + }); + delegate(document, '.custom-emoji', 'mouseover', getEmojiAnimationHandler('data-original')); delegate(document, '.custom-emoji', 'mouseout', getEmojiAnimationHandler('data-static')); diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss index 7a0b2f9a39..a6df51f954 100644 --- a/app/javascript/styles/mastodon/forms.scss +++ b/app/javascript/styles/mastodon/forms.scss @@ -364,7 +364,8 @@ code { box-shadow: none; } - &:focus:invalid:not(:placeholder-shown) { + &:focus:invalid:not(:placeholder-shown), + &:required:invalid:not(:placeholder-shown) { border-color: lighten($error-red, 12%); } diff --git a/app/views/about/_registration.html.haml b/app/views/about/_registration.html.haml index af28e21749..f65e674279 100644 --- a/app/views/about/_registration.html.haml +++ b/app/views/about/_registration.html.haml @@ -1,13 +1,13 @@ .simple_form__overlay-area{ class: (closed_registrations? && @instance_presenter.closed_registrations_message.present?) ? 'simple_form__overlay-area__blurred' : '' } - = simple_form_for(new_user, url: user_registration_path, namespace: 'registration') do |f| + = simple_form_for(new_user, url: user_registration_path, namespace: 'registration', html: { novalidate: false }) do |f| %p.lead= t('about.federation_hint_html', instance: content_tag(:strong, site_hostname)) .fields-group = f.simple_fields_for :account do |account_fields| - = account_fields.input :username, wrapper: :with_label, label: false, required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.username'), :autocomplete => 'off', placeholder: t('simple_form.labels.defaults.username') }, append: "@#{site_hostname}", hint: false, disabled: closed_registrations? + = account_fields.input :username, wrapper: :with_label, label: false, required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.username'), :autocomplete => 'off', placeholder: t('simple_form.labels.defaults.username'), pattern: Account::USERNAME_RE.source }, append: "@#{site_hostname}", hint: false, disabled: closed_registrations? = f.input :email, placeholder: t('simple_form.labels.defaults.email'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.email'), :autocomplete => 'off' }, hint: false, disabled: closed_registrations? - = f.input :password, placeholder: t('simple_form.labels.defaults.password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.password'), :autocomplete => 'off' }, hint: false, disabled: closed_registrations? + = f.input :password, placeholder: t('simple_form.labels.defaults.password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.password'), :autocomplete => 'off', :minlength => User.password_length.first, :maxlength => User.password_length.last }, hint: false, disabled: closed_registrations? = f.input :password_confirmation, placeholder: t('simple_form.labels.defaults.confirm_password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_password'), :autocomplete => 'off' }, hint: false, disabled: closed_registrations? - if approved_registrations? @@ -16,7 +16,7 @@ = invite_request_fields.input :text, as: :text, wrapper: :with_block_label, required: false .fields-group - = f.input :agreement, as: :boolean, wrapper: :with_label, label: t('auth.checkbox_agreement_html', rules_path: about_more_path, terms_path: terms_path), disabled: closed_registrations? + = f.input :agreement, as: :boolean, wrapper: :with_label, label: t('auth.checkbox_agreement_html', rules_path: about_more_path, terms_path: terms_path), required: true, disabled: closed_registrations? .actions = f.button :button, sign_up_message, type: :submit, class: 'button button-primary', disabled: closed_registrations?