From e04c308c61107b058f96170abb728a5fe3487630 Mon Sep 17 00:00:00 2001 From: Raz Date: Wed, 15 Jan 2025 10:54:09 +0100 Subject: [PATCH] fix errors in login / profile / create user, update unique var in licence_id --- padelclub_backend/settings.py | 7 +++ padelclub_backend/urls.py | 2 +- tournaments/backends.py | 4 -- tournaments/forms.py | 50 ++++++++++++++++++- ...registration_registered_online_and_more.py | 23 +++++++++ .../0106_alter_customuser_licence_id.py | 18 +++++++ tournaments/models/custom_user.py | 16 +++++- tournaments/models/player_enums.py | 1 - tournaments/models/player_registration.py | 1 + tournaments/repositories.py | 7 ++- .../services/tournament_registration.py | 4 +- tournaments/signals.py | 4 +- tournaments/templates/profile.html | 36 +++++++++++++ .../registration/password_reset_confirm.html | 18 +++++++ .../registration/password_reset_form.html | 18 +++++++ .../templates/registration/signup.html | 19 +++++++ tournaments/urls.py | 17 ++++--- tournaments/views.py | 12 ++--- 18 files changed, 232 insertions(+), 25 deletions(-) create mode 100644 tournaments/migrations/0105_playerregistration_registered_online_and_more.py create mode 100644 tournaments/migrations/0106_alter_customuser_licence_id.py diff --git a/padelclub_backend/settings.py b/padelclub_backend/settings.py index 0a7067b..8f46d66 100644 --- a/padelclub_backend/settings.py +++ b/padelclub_backend/settings.py @@ -155,3 +155,10 @@ AUTHENTICATION_BACKENDS = [ 'tournaments.backends.EmailOrUsernameModelBackend', # replace 'yourapp' with your actual app name 'django.contrib.auth.backends.ModelBackend', ] + +CSRF_COOKIE_SECURE = True # if using HTTPS +CSRF_COOKIE_HTTPONLY = False # needed for AJAX requests +CSRF_USE_SESSIONS = False # store CSRF token in cookie instead of session +CSRF_COOKIE_SAMESITE = 'Lax' # or 'Strict' for more security +LOGIN_URL = 'login' +PASSWORD_CHANGE_REDIRECT_URL = 'profile' # Redirect back to profile after password change diff --git a/padelclub_backend/urls.py b/padelclub_backend/urls.py index e5e7913..202cd71 100644 --- a/padelclub_backend/urls.py +++ b/padelclub_backend/urls.py @@ -25,6 +25,6 @@ urlpatterns = [ path('kingdom/', admin.site.urls), path('api-auth/', include('rest_framework.urls')), - path('dj-auth/', include('django.contrib.auth.urls')), + #path('dj-auth/', include('django.contrib.auth.urls')), ] diff --git a/tournaments/backends.py b/tournaments/backends.py index 04f1346..761d22b 100644 --- a/tournaments/backends.py +++ b/tournaments/backends.py @@ -1,8 +1,4 @@ # backends.py -from django.contrib.auth import get_user_model -from django.contrib.auth.backends import ModelBackend -from django.db.models import Q - from django.contrib.auth import get_user_model from django.contrib.auth.backends import ModelBackend from django.db.models import Q diff --git a/tournaments/forms.py b/tournaments/forms.py index c19bfd3..8e344ad 100644 --- a/tournaments/forms.py +++ b/tournaments/forms.py @@ -14,6 +14,19 @@ class CustomUserCreationForm(UserCreationForm): class Meta: model = CustomUser + error_messages = { + 'email': { + 'unique': "Cette adresse email est déjà utilisée.", + }, + 'username': { + 'unique': "Ce nom d'utilisateur est déjà pris.", + }, + 'licence_id': { + 'unique': "Cette licence est déjà utilisée par un autre compte.", + }, + + } + fields = UserCreationForm.Meta.fields + ('umpire_code', 'clubs', 'phone', 'first_name', 'last_name', 'licence_id', 'country') class SimpleCustomUserCreationForm(UserCreationForm): @@ -21,6 +34,18 @@ class SimpleCustomUserCreationForm(UserCreationForm): class Meta: model = CustomUser fields = UserCreationForm.Meta.fields + ('email', 'phone', 'first_name', 'last_name', 'licence_id', 'country') + error_messages = { + 'email': { + 'unique': "Cette adresse email est déjà utilisée.", + }, + 'username': { + 'unique': "Ce nom d'utilisateur est déjà pris.", + }, + 'licence_id': { + 'unique': "Cette licence est déjà utilisée par un autre compte.", + }, + + } labels = { 'username': 'Nom d’utilisateur', 'email': 'E-mail', @@ -38,6 +63,18 @@ class CustomUserChangeForm(UserChangeForm): class Meta: model = CustomUser + error_messages = { + 'email': { + 'unique': "Cette adresse email est déjà utilisée.", + }, + 'username': { + 'unique': "Ce nom d'utilisateur est déjà pris.", + }, + 'licence_id': { + 'unique': "Cette licence est déjà utilisée par un autre compte.", + }, + + } fields = UserCreationForm.Meta.fields + ('umpire_code', 'clubs', 'phone', 'first_name', 'last_name', 'licence_id', 'country') class SimpleForm(forms.Form): @@ -156,6 +193,17 @@ class ProfileUpdateForm(forms.ModelForm): 'last_name': 'Nom de famille', 'licence_id': 'Numéro de licence', } + error_messages = { + 'email': { + 'unique': "Cette adresse email est déjà utilisée.", + }, + 'username': { + 'unique': "Ce nom d'utilisateur est déjà pris.", + }, + 'licence_id': { + 'unique': "Cette licence est déjà utilisée par un autre compte.", + }, + } from django.contrib.auth.forms import PasswordChangeForm @@ -197,7 +245,7 @@ class EmailOrUsernameAuthenticationForm(AuthenticationForm): print("Authentication failed") # Debug print logger.warning("Authentication failed") raise forms.ValidationError( - "Please enter a correct username/email and password.", + "Identifiant/E-mail ou mot de passe incorrect. Les champs sont sensibles à la casse.", code='invalid_login' ) else: diff --git a/tournaments/migrations/0105_playerregistration_registered_online_and_more.py b/tournaments/migrations/0105_playerregistration_registered_online_and_more.py new file mode 100644 index 0000000..2359b38 --- /dev/null +++ b/tournaments/migrations/0105_playerregistration_registered_online_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.11 on 2025-01-15 07:49 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tournaments', '0104_remove_tournament_target_team_count'), + ] + + operations = [ + migrations.AddField( + model_name='playerregistration', + name='registered_online', + field=models.BooleanField(default=False), + ), + migrations.AlterField( + model_name='playerregistration', + name='source', + field=models.IntegerField(blank=True, choices=[(0, 'French Federation'), (1, 'Beach Padel')], null=True), + ), + ] diff --git a/tournaments/migrations/0106_alter_customuser_licence_id.py b/tournaments/migrations/0106_alter_customuser_licence_id.py new file mode 100644 index 0000000..c259dfe --- /dev/null +++ b/tournaments/migrations/0106_alter_customuser_licence_id.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.11 on 2025-01-15 07:53 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tournaments', '0105_playerregistration_registered_online_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='customuser', + name='licence_id', + field=models.CharField(blank=True, max_length=10, null=True, unique=True), + ), + ] diff --git a/tournaments/models/custom_user.py b/tournaments/models/custom_user.py index 5f6ecdf..45de189 100644 --- a/tournaments/models/custom_user.py +++ b/tournaments/models/custom_user.py @@ -6,13 +6,25 @@ import uuid class CustomUser(AbstractUser): pass id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True) - email = models.EmailField(unique=True) + email = models.EmailField( + unique=True, + error_messages={ + 'unique': "Cette adresse email est déjà utilisée.", + } + ) + username = models.CharField( + max_length=150, + unique=True, + error_messages={ + 'unique': "Ce nom d'utilisateur est déjà pris.", + } + ) umpire_code = models.CharField(max_length=50, blank=True, null=True) clubs = models.ManyToManyField(club.Club, blank=True) phone = models.CharField(max_length=15, null=True, blank=True) first_name = models.CharField(max_length=50) last_name = models.CharField(max_length=50) - licence_id = models.CharField(max_length=10, null=True, blank=True) + licence_id = models.CharField(max_length=10, unique=True, null=True, blank=True) country = models.CharField(max_length=40, null=True, blank=True) summons_message_body = models.TextField(blank=True, null=True) diff --git a/tournaments/models/player_enums.py b/tournaments/models/player_enums.py index 11ee89a..59c46a7 100644 --- a/tournaments/models/player_enums.py +++ b/tournaments/models/player_enums.py @@ -15,7 +15,6 @@ class PlayerPaymentType(models.IntegerChoices): class PlayerDataSource(models.IntegerChoices): FRENCH_FEDERATION = 0, 'French Federation' BEACH_PADEL = 1, 'Beach Padel' - ONLINE_REGISTRATION = 2, 'Online Registration' class PlayerSexType(models.IntegerChoices): FEMALE = 0, 'Female' diff --git a/tournaments/models/player_registration.py b/tournaments/models/player_registration.py index 846443a..29ebcf0 100644 --- a/tournaments/models/player_registration.py +++ b/tournaments/models/player_registration.py @@ -36,6 +36,7 @@ class PlayerRegistration(models.Model): has_arrived = models.BooleanField(default=False) captain = models.BooleanField(default=False) coach = models.BooleanField(default=False) + registered_online = models.BooleanField(default=False) def __str__(self): return self.name() diff --git a/tournaments/repositories.py b/tournaments/repositories.py index bf296e7..d33690f 100644 --- a/tournaments/repositories.py +++ b/tournaments/repositories.py @@ -31,10 +31,15 @@ class TournamentRegistrationRepository: player_data ) + data_source = None + if player_data.get('found_in_french_federation', False) == True: + data_source = PlayerDataSource.FRENCH_FEDERATION + player_registration = PlayerRegistration.objects.create( team_registration=team_registration, captain=is_captain, - source=PlayerDataSource.ONLINE_REGISTRATION, + source=data_source, + registered_online=True, first_name=player_data.get('first_name'), last_name=player_data.get('last_name'), points=player_data.get('points'), diff --git a/tournaments/services/tournament_registration.py b/tournaments/services/tournament_registration.py index 9176de6..f4df4a9 100644 --- a/tournaments/services/tournament_registration.py +++ b/tournaments/services/tournament_registration.py @@ -162,7 +162,8 @@ class TournamentRegistrationService: 'tournament_count': data.get('tournament_count'), 'ligue_name': data.get('ligue_name'), 'club_name': data.get('club_name'), - 'birth_year': data.get('birth_year') + 'birth_year': data.get('birth_year'), + 'found': True, }) return player_data @@ -252,6 +253,7 @@ class TournamentRegistrationService: 'ligue_name': csv_data.get('ligue_name'), 'club_name': csv_data.get('club_name'), 'birth_year': csv_data.get('birth_year'), + 'found': True, 'email': None, 'phone': None, }) diff --git a/tournaments/signals.py b/tournaments/signals.py index 4fdc747..9dddf3b 100644 --- a/tournaments/signals.py +++ b/tournaments/signals.py @@ -92,14 +92,14 @@ def unregister_team(sender, instance, **kwargs): else: waiting_other_player = player - if waiting_captain is not None and waiting_captain.source == PlayerDataSource.ONLINE_REGISTRATION and waiting_captain.email is not None: + if waiting_captain is not None and waiting_captain.registered_online == True and waiting_captain.email is not None: TournamentEmailService.send_out_of_waiting_list_confirmation( waiting_captain, tournament, waiting_other_player ) - if captain is not None and captain.source == PlayerDataSource.ONLINE_REGISTRATION and captain.email is not None: + if captain is not None and captain.registered_online == True and captain.email is not None: TournamentEmailService.send_unregistration_confirmation( captain, tournament, diff --git a/tournaments/templates/profile.html b/tournaments/templates/profile.html index f0a2dfa..997cc8e 100644 --- a/tournaments/templates/profile.html +++ b/tournaments/templates/profile.html @@ -23,6 +23,24 @@
+ {% if form.errors %} +
+ {% for field, errors in form.errors.items %} + {% for error in errors %} +

{{ error }}

+ {% endfor %} + {% endfor %} +
+ {% endif %} + + + {% if form.non_field_errors %} +
+ {% for error in form.non_field_errors %} +

{{ error }}

+ {% endfor %} +
+ {% endif %}
{% csrf_token %} {{ form.as_p }} @@ -33,6 +51,24 @@
+ {% if password_change_form.errors %} +
+ {% for field, errors in password_change_form.errors.items %} + {% for error in errors %} +

{{ error }}

+ {% endfor %} + {% endfor %} +
+ {% endif %} + + {% if password_change_form.non_field_errors %} +
+ {% for error in password_change_form.non_field_errors %} +

{{ error }}

+ {% endfor %} +
+ {% endif %} + {% csrf_token %} {{ password_change_form.as_p }} diff --git a/tournaments/templates/registration/password_reset_confirm.html b/tournaments/templates/registration/password_reset_confirm.html index 0d09311..9393cc8 100644 --- a/tournaments/templates/registration/password_reset_confirm.html +++ b/tournaments/templates/registration/password_reset_confirm.html @@ -7,6 +7,24 @@
+ {% if form.errors %} +
+ {% for field, errors in form.errors.items %} + {% for error in errors %} +

{{ error }}

+ {% endfor %} + {% endfor %} +
+ {% endif %} + + + {% if form.non_field_errors %} +
+ {% for error in form.non_field_errors %} +

{{ error }}

+ {% endfor %} +
+ {% endif %} {% csrf_token %} diff --git a/tournaments/templates/registration/password_reset_form.html b/tournaments/templates/registration/password_reset_form.html index 0872462..40e8858 100644 --- a/tournaments/templates/registration/password_reset_form.html +++ b/tournaments/templates/registration/password_reset_form.html @@ -7,6 +7,24 @@
+ {% if form.errors %} +
+ {% for field, errors in form.errors.items %} + {% for error in errors %} +

{{ error }}

+ {% endfor %} + {% endfor %} +
+ {% endif %} + + + {% if form.non_field_errors %} +
+ {% for error in form.non_field_errors %} +

{{ error }}

+ {% endfor %} +
+ {% endif %} {% csrf_token %} diff --git a/tournaments/templates/registration/signup.html b/tournaments/templates/registration/signup.html index 40ca09d..a6a083f 100644 --- a/tournaments/templates/registration/signup.html +++ b/tournaments/templates/registration/signup.html @@ -11,6 +11,25 @@
+ {% if form.errors %} +
+ {% for field, errors in form.errors.items %} + {% for error in errors %} +

{{ error }}

+ {% endfor %} + {% endfor %} +
+ {% endif %} + + + {% if form.non_field_errors %} +
+ {% for error in form.non_field_errors %} +

{{ error }}

+ {% endfor %} +
+ {% endif %} + {% csrf_token %} {{ form.as_p }} diff --git a/tournaments/urls.py b/tournaments/urls.py index cdec723..7affe34 100644 --- a/tournaments/urls.py +++ b/tournaments/urls.py @@ -1,8 +1,6 @@ from django.contrib.auth import views as auth_views from django.urls import include, path -from .forms import EmailOrUsernameAuthenticationForm -from django.views.decorators.csrf import ensure_csrf_cookie -from django.utils.decorators import method_decorator +from .forms import EmailOrUsernameAuthenticationForm, CustomPasswordChangeForm from . import views @@ -44,10 +42,17 @@ urlpatterns = [ path('terms-of-use/', views.terms_of_use, name='terms-of-use'), path('utils/xls-to-csv/', views.xls_to_csv, name='xls-to-csv'), path('mail-test/', views.simple_form_view, name='mail-test'), - path('login/', method_decorator(ensure_csrf_cookie)(auth_views.LoginView.as_view( + path('login/', auth_views.LoginView.as_view( template_name='registration/login.html', - form_class=EmailOrUsernameAuthenticationForm - )), name='login'), + authentication_form=EmailOrUsernameAuthenticationForm + ), name='login'), + path('password_change/', + auth_views.PasswordChangeView.as_view( + success_url='/profile/', # Redirect back to profile after success + form_class=CustomPasswordChangeForm + ), + name='password_change' + ), path('logout/', auth_views.LogoutView.as_view(), name='logout'), path('signup/', views.signup, name='signup'), # URL pattern for signup # path('profile/', views.profile, name='profile'), # URL pattern for signup diff --git a/tournaments/views.py b/tournaments/views.py index 4ffe527..1f00e1f 100644 --- a/tournaments/views.py +++ b/tournaments/views.py @@ -583,24 +583,24 @@ def send_email(mail, name): email.send() def signup(request): - next_url = request.GET.get('next', '/') # Get the 'next' parameter from the request - print('next_url', next_url) + next_url = request.GET.get('next', '/') if request.method == 'POST': form = SimpleCustomUserCreationForm(request.POST) if form.is_valid(): user = form.save(commit=False) - user.is_active = False # Deactivate account until email is verified + user.is_active = False user.save() - # Send verification email send_verification_email(request, user, next_url) messages.success(request, "Votre compte a été créé ! Veuillez vérifier votre e-mail pour confirmer votre compte.") - return redirect(next_url) # Redirect directly to the 'next' URL + return redirect(next_url) else: form = SimpleCustomUserCreationForm() - return render(request, 'registration/signup.html', {'form': form}) + + response = render(request, 'registration/signup.html', {'form': form}) + return response def send_verification_email(request, user, next_url): print('next_url', next_url)