From d9a696d82ee37e96c228eb6894a6175e2a6e152b Mon Sep 17 00:00:00 2001 From: Raz Date: Sat, 29 Mar 2025 18:45:44 +0100 Subject: [PATCH 1/2] fix crash --- tournaments/forms.py | 49 +++++++++++++++++++++------ tournaments/services/email_service.py | 2 +- tournaments/templates/profile.html | 11 ------ 3 files changed, 40 insertions(+), 22 deletions(-) diff --git a/tournaments/forms.py b/tournaments/forms.py index cc4d314..872514b 100644 --- a/tournaments/forms.py +++ b/tournaments/forms.py @@ -12,6 +12,11 @@ from django.utils.encoding import force_bytes class CustomUserCreationForm(UserCreationForm): usable_password = None + def clean_licence_id(self): + licence_id = self.cleaned_data.get('licence_id') + if licence_id: + return licence_id.replace(' ', '').strip().upper() + return licence_id class Meta: model = CustomUser @@ -33,6 +38,22 @@ class CustomUserCreationForm(UserCreationForm): class SimpleCustomUserCreationForm(UserCreationForm): usable_password = None + def clean_licence_id(self): + licence_id = self.cleaned_data.get('licence_id') + if licence_id: + return licence_id.replace(' ', '').strip().upper() + return licence_id + + def clean_phone(self): + phone = self.cleaned_data.get('phone') + if phone: + # Remove all spaces + phone = phone.replace(' ', '') + # Basic regex for phone numbers, matching common formats + if not re.match(r"^\+?\d{10,15}$", phone): + raise forms.ValidationError("Entrer un numéro de téléphone valide.") + return phone + class Meta: model = CustomUser fields = UserCreationForm.Meta.fields + ('email', 'phone', 'first_name', 'last_name', 'licence_id', 'country') @@ -125,16 +146,8 @@ class AddPlayerForm(forms.Form): def clean_licence_id(self): licence_id = self.cleaned_data.get('licence_id') - - # Convert to uppercase - licence_id = licence_id.upper() - - # Update the cleaned_data with the modified licence_id - self.cleaned_data['licence_id'] = licence_id - - # Optionally, print the cleaned license ID for debugging - print(f"Cleaned Licence ID (inside clean_licence_id): {licence_id}") - + if licence_id: + licence_id = licence_id.replace(' ', '').strip().upper() return licence_id def clean_last_name(self): @@ -193,6 +206,22 @@ class ProfileUpdateForm(forms.ModelForm): # Remove autofocus from the 'username' field self.fields['username'].widget.attrs.pop("autofocus", None) + def clean_licence_id(self): + licence_id = self.cleaned_data.get('licence_id') + if licence_id: + return licence_id.replace(' ', '').upper() + return licence_id + + def clean_phone(self): + phone = self.cleaned_data.get('phone') + if phone: + # Remove all spaces + phone = phone.replace(' ', '') + # Basic regex for phone numbers, matching common formats + if not re.match(r"^\+?\d{10,15}$", phone): + raise forms.ValidationError("Entrer un numéro de téléphone valide.") + return phone + class Meta: model = CustomUser fields = ['first_name', 'last_name', 'licence_id', 'username', 'email', 'phone'] diff --git a/tournaments/services/email_service.py b/tournaments/services/email_service.py index 33fe93f..f8f904c 100644 --- a/tournaments/services/email_service.py +++ b/tournaments/services/email_service.py @@ -75,7 +75,7 @@ class TournamentEmailService: body_parts.append(f"Votre inscription en liste d'attente du tournoi {tournament_details_str} est confirmée.") else: body_parts.append(f"Votre inscription au tournoi {tournament_details_str} est confirmée.") - if tournament.team_sort == TeamSortingType.RANK: + if tournament.team_sorting == TeamSortingType.RANK: cloture_date = tournament.local_registration_federal_limit().strftime("%d/%m/%Y à %H:%M") loc = "" if cloture_date is not None: diff --git a/tournaments/templates/profile.html b/tournaments/templates/profile.html index 89fd5f4..9216b92 100644 --- a/tournaments/templates/profile.html +++ b/tournaments/templates/profile.html @@ -23,17 +23,6 @@
- {% 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 %} From 9c456a9357af27a558d802f910d76f015505716f Mon Sep 17 00:00:00 2001 From: Raz Date: Sat, 29 Mar 2025 20:18:56 +0100 Subject: [PATCH 2/2] fix lot of stuff around forms and password reset --- tournaments/custom_views.py | 20 +++++-- .../services/tournament_registration.py | 13 +++++ tournaments/templates/profile.html | 52 +++++++++--------- .../templates/register_tournament.html | 51 ++++++++---------- tournaments/templates/registration/login.html | 8 +-- .../registration/password_reset_complete.html | 4 +- .../registration/password_reset_confirm.html | 18 +------ .../registration/password_reset_done.html | 4 +- .../registration/password_reset_form.html | 17 +----- .../templates/registration/signup.html | 53 ++++++++++--------- .../tournaments/tournament_info.html | 9 ---- tournaments/urls.py | 12 ++--- tournaments/views.py | 53 +++++++++++++++++++ 13 files changed, 178 insertions(+), 136 deletions(-) diff --git a/tournaments/custom_views.py b/tournaments/custom_views.py index 01d54bb..4e3d2f1 100644 --- a/tournaments/custom_views.py +++ b/tournaments/custom_views.py @@ -10,19 +10,33 @@ class CustomLoginView(auth_views.LoginView): def get_success_url(self): # First check the 'next' parameter which has higher priority next_url = self.request.POST.get('next') or self.request.GET.get('next') + + # Check if the next URL is a password reset page and avoid that redirect if next_url and next_url.strip(): + # Avoid redirecting to password reset pages after login + if 'reset' in next_url or 'password_reset' in next_url: + # Redirect to profile or index instead + return reverse('profile') return next_url # Then check if we have a stored referrer URL referrer = self.request.session.get('login_referrer') if referrer: - # Clear the stored referrer to prevent reuse - del self.request.session['login_referrer'] - return referrer + # Avoid redirecting to password reset pages from stored referrer + if 'reset' not in referrer and 'password_reset' not in referrer: + # Clear the stored referrer to prevent reuse + del self.request.session['login_referrer'] + return referrer # Fall back to default return reverse('index') def get(self, request, *args, **kwargs): + # Clear any potential password reset session data + keys_to_clear = [key for key in request.session.keys() + if 'reset' in key or 'password' in key] + for key in keys_to_clear: + del request.session[key] + messages.get_messages(request).used = True return super().get(request, *args, **kwargs) diff --git a/tournaments/services/tournament_registration.py b/tournaments/services/tournament_registration.py index 0233edb..d42ae9e 100644 --- a/tournaments/services/tournament_registration.py +++ b/tournaments/services/tournament_registration.py @@ -48,6 +48,13 @@ class TournamentRegistrationService: if not self.context['add_player_form'].is_valid(): return + # Clear existing messages if the form is valid + from django.contrib.messages import get_messages + storage = get_messages(self.request) + # Iterate through the storage to clear it + for _ in storage: + pass + player_data = self.context['add_player_form'].cleaned_data licence_id = player_data.get('licence_id', '').upper() @@ -112,6 +119,12 @@ class TournamentRegistrationService: self.context['registration_successful'] = True def handle_get_request(self): + from django.contrib.messages import get_messages + storage = get_messages(self.request) + # Iterate through the storage to clear it + for _ in storage: + pass + self.context['add_player_form'] = AddPlayerForm() self.context['team_form'] = self.initialize_team_form() self.initialize_session_data() diff --git a/tournaments/templates/profile.html b/tournaments/templates/profile.html index 9216b92..2e6a1a7 100644 --- a/tournaments/templates/profile.html +++ b/tournaments/templates/profile.html @@ -20,16 +20,34 @@ {% load static %} {% load tz %} +{% if form.errors or password_change_form.errors %} +
+
+
+ {% for field in form %} + {% if field.errors %} + {% for error in field.errors %} +
{{ field.label }} : {{ error }}
+ {% endfor %} + {% endif %} + {% endfor %} +
+
+ {% for field in password_change_form %} + {% if field.errors %} + {% for error in field.errors %} +
{{ field.label }} : {{ error }}
+ {% endfor %} + {% endif %} + {% endfor %} +
+
+
+{% endif %} +
- {% if form.non_field_errors %} -
- {% for error in form.non_field_errors %} -

{{ error }}

- {% endfor %} -
- {% endif %}
{% csrf_token %} {{ form.as_p }} @@ -40,25 +58,7 @@
- {% 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/register_tournament.html b/tournaments/templates/register_tournament.html index c8349d1..95a1212 100644 --- a/tournaments/templates/register_tournament.html +++ b/tournaments/templates/register_tournament.html @@ -28,20 +28,27 @@ {% else %} {% if team_form.errors %} -
- {% if team_form.non_field_errors %} - {% for error in team_form.non_field_errors %} -

{{ error }}

- {% endfor %} - {% endif %} - +
{% for field in team_form %} - {% for error in field.errors %} -

{{ error }}

- {% endfor %} + {% if field.errors %} + {% for error in field.errors %} +
{{ field.label }} : {{ error }}
+ {% endfor %} + {% endif %} + {% endfor %} +
+ {% endif %} + + {% if messages %} +
+ {% for message in messages %} +
+ {{ message }} +
{% endfor %}
{% endif %} + {% csrf_token %} @@ -52,6 +59,12 @@ Informations de contact

+ {% if team_form.non_field_errors %} + {% for error in team_form.non_field_errors %} +

{{ error }}

+ {% endfor %} + {% endif %} + {{ team_form.as_p }}
@@ -124,24 +137,6 @@ {% endif %} {% endif %} -
- {% if add_player_form.errors %} -
- {% if add_player_form.non_field_errors %} - {% for error in add_player_form.non_field_errors %} -

{{ error }}

- {% endfor %} - {% endif %} - - {% for field in add_player_form %} - {% for error in field.errors %} -

{{ error }}

- {% endfor %} - {% endfor %} -
- {% endif %} -
- -

+

- {% for message in messages %} -
{{ message }}
- {% endfor %}
{% endblock %} diff --git a/tournaments/templates/registration/password_reset_done.html b/tournaments/templates/registration/password_reset_done.html index bfc7035..28f2b9f 100644 --- a/tournaments/templates/registration/password_reset_done.html +++ b/tournaments/templates/registration/password_reset_done.html @@ -11,9 +11,9 @@ Un e-mail contenant un lien pour réinitialiser votre mot de passe a été envoyé à votre adresse. Veuillez vérifier votre boîte de réception.

-

+

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

{{ error }}

- {% endfor %} - {% endfor %} -
- {% endif %} - {% if form.non_field_errors %}
@@ -31,13 +21,10 @@ -

+

- {% for message in messages %} -
{{ message }}
- {% endfor %}
{% endblock %} diff --git a/tournaments/templates/registration/signup.html b/tournaments/templates/registration/signup.html index d541d72..26b3b02 100644 --- a/tournaments/templates/registration/signup.html +++ b/tournaments/templates/registration/signup.html @@ -8,35 +8,40 @@ {% load static %} {% load tz %} -
-
-
- {% if form.errors %} -
- {% for field, errors in form.errors.items %} - {% for error in errors %} -

{{ error }}

+
+ {% if form.errors %} +
+
+
+ {% for field in form %} + {% if field.errors %} + {% for error in field.errors %} +
{{ field.label }} : {{ error }}
+ {% endfor %} + {% endif %} {% endfor %} - {% endfor %} +
- {% endif %} +
+ {% endif %} - - {% if form.non_field_errors %} -
- {% for error in form.non_field_errors %} -

{{ error }}

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

{{ error }}

+ {% endfor %} +
+ {% endif %} -
- {% csrf_token %} - {{ form.as_p }} - -
+
+ {% csrf_token %} + {{ form.as_p }} + +
+
-
{% endblock %} diff --git a/tournaments/templates/tournaments/tournament_info.html b/tournaments/templates/tournaments/tournament_info.html index 9c93a55..822bcee 100644 --- a/tournaments/templates/tournaments/tournament_info.html +++ b/tournaments/templates/tournaments/tournament_info.html @@ -47,12 +47,6 @@ {% if tournament.is_unregistration_possible %}

-

- {% for message in messages %} -
{{ message }}
- {% endfor %} -
- @@ -164,9 +158,6 @@

{% endif %} {% else %} - {% for message in messages %} -
{{ message }}
- {% endfor %}

Connectez-vous ! diff --git a/tournaments/urls.py b/tournaments/urls.py index f544567..e0a49bb 100644 --- a/tournaments/urls.py +++ b/tournaments/urls.py @@ -55,13 +55,7 @@ urlpatterns = [ 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/', CustomLoginView.as_view(), name='custom-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('custom_password_change/', views.custom_password_change, name='custom_password_change'), path('logout/', views.custom_logout, name='custom_logout'), path('signup/', views.signup, name='signup'), # URL pattern for signup # path('profile/', views.profile, name='profile'), # URL pattern for signup @@ -70,7 +64,9 @@ urlpatterns = [ path('tournaments//unregister/', views.unregister_tournament, name='unregister_tournament'), path('password_reset/', auth_views.PasswordResetView.as_view(), name='password_reset'), path('password_reset_done/', auth_views.PasswordResetDoneView.as_view(), name='password_reset_done'), - path('reset/done/', auth_views.PasswordResetCompleteView.as_view(), name='password_reset_complete'), + path('reset/done/', + views.CustomPasswordResetCompleteView.as_view(), + name='password_reset_complete'), path('profile/', views.ProfileUpdateView.as_view(), name='profile'), path('admin/tournament-import/', views.tournament_import_view, name='tournament_import'), path('admin/status/', views.status_page, name='status_page'), diff --git a/tournaments/views.py b/tournaments/views.py index a3dfa5f..14990c6 100644 --- a/tournaments/views.py +++ b/tournaments/views.py @@ -2,6 +2,11 @@ import os import csv +from django.contrib.auth import update_session_auth_hash +from django.contrib.auth.views import PasswordResetCompleteView +from django.shortcuts import redirect +from django.contrib.auth import login +from django.contrib.auth import get_user_model from django.shortcuts import render, get_object_or_404 from django.http import JsonResponse, HttpResponse from django.utils.encoding import force_str @@ -818,6 +823,54 @@ class CustomPasswordResetConfirmView(PasswordResetConfirmView): except (TypeError, ValueError, User.DoesNotExist): raise Http404("User not found") +class CustomPasswordResetCompleteView(PasswordResetCompleteView): + template_name = 'registration/password_reset_complete.html' + + def get(self, request, *args, **kwargs): + # Get the user from the session + username = request.session.get('reset_username') + + if username: + try: + # Get the user + User = get_user_model() + user = User.objects.get(username=username) + + # Log the user in + login(request, user, backend='django.contrib.auth.backends.ModelBackend') + + # Clean up the session + if 'reset_username' in request.session: + del request.session['reset_username'] + + # Redirect to the profile page + return redirect('profile') + except User.DoesNotExist: + pass + + # If no username in session or user not found, proceed with normal view + return super().get(request, *args, **kwargs) + +@login_required +def custom_password_change(request): + if request.method == 'POST': + form = CustomPasswordChangeForm(user=request.user, data=request.POST) + if form.is_valid(): + user = form.save() + update_session_auth_hash(request, user) # Important to keep user logged in + messages.success(request, 'Votre mot de passe a été mis à jour avec succès!') + return redirect('profile') + else: + # Form is invalid, show errors + profile_form = ProfileUpdateForm(instance=request.user) + return render(request, 'profile.html', { + 'form': profile_form, + 'password_change_form': form + }) + + # If not POST, redirect to profile page + return redirect('profile') + @login_required def my_tournaments(request): user = request.user