Merge branch 'main' into timetoconfirm

timetoconfirm
Raz 8 months ago
commit b9e0bab3fc
  1. 14
      tournaments/custom_views.py
  2. 49
      tournaments/forms.py
  3. 2
      tournaments/services/email_service.py
  4. 13
      tournaments/services/tournament_registration.py
  5. 53
      tournaments/templates/profile.html
  6. 47
      tournaments/templates/register_tournament.html
  7. 4
      tournaments/templates/registration/login.html
  8. 4
      tournaments/templates/registration/password_reset_complete.html
  9. 18
      tournaments/templates/registration/password_reset_confirm.html
  10. 4
      tournaments/templates/registration/password_reset_done.html
  11. 17
      tournaments/templates/registration/password_reset_form.html
  12. 23
      tournaments/templates/registration/signup.html
  13. 9
      tournaments/templates/tournaments/tournament_info.html
  14. 12
      tournaments/urls.py
  15. 53
      tournaments/views.py

@ -10,12 +10,20 @@ class CustomLoginView(auth_views.LoginView):
def get_success_url(self): def get_success_url(self):
# First check the 'next' parameter which has higher priority # First check the 'next' parameter which has higher priority
next_url = self.request.POST.get('next') or self.request.GET.get('next') 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(): 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 return next_url
# Then check if we have a stored referrer URL # Then check if we have a stored referrer URL
referrer = self.request.session.get('login_referrer') referrer = self.request.session.get('login_referrer')
if referrer: if 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 # Clear the stored referrer to prevent reuse
del self.request.session['login_referrer'] del self.request.session['login_referrer']
return referrer return referrer
@ -24,5 +32,11 @@ class CustomLoginView(auth_views.LoginView):
return reverse('index') return reverse('index')
def get(self, request, *args, **kwargs): 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 messages.get_messages(request).used = True
return super().get(request, *args, **kwargs) return super().get(request, *args, **kwargs)

@ -12,6 +12,11 @@ from django.utils.encoding import force_bytes
class CustomUserCreationForm(UserCreationForm): class CustomUserCreationForm(UserCreationForm):
usable_password = None 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: class Meta:
model = CustomUser model = CustomUser
@ -33,6 +38,22 @@ class CustomUserCreationForm(UserCreationForm):
class SimpleCustomUserCreationForm(UserCreationForm): class SimpleCustomUserCreationForm(UserCreationForm):
usable_password = None 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: class Meta:
model = CustomUser model = CustomUser
fields = UserCreationForm.Meta.fields + ('email', 'phone', 'first_name', 'last_name', 'licence_id', 'country') 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): def clean_licence_id(self):
licence_id = self.cleaned_data.get('licence_id') licence_id = self.cleaned_data.get('licence_id')
if licence_id:
# Convert to uppercase licence_id = licence_id.replace(' ', '').strip().upper()
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}")
return licence_id return licence_id
def clean_last_name(self): def clean_last_name(self):
@ -193,6 +206,22 @@ class ProfileUpdateForm(forms.ModelForm):
# Remove autofocus from the 'username' field # Remove autofocus from the 'username' field
self.fields['username'].widget.attrs.pop("autofocus", None) 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: class Meta:
model = CustomUser model = CustomUser
fields = ['first_name', 'last_name', 'licence_id', 'username', 'email', 'phone'] fields = ['first_name', 'last_name', 'licence_id', 'username', 'email', 'phone']

@ -76,7 +76,7 @@ class TournamentEmailService:
body_parts.append(f"Votre inscription en liste d'attente du tournoi {tournament_details_str} est confirmée.") body_parts.append(f"Votre inscription en liste d'attente du tournoi {tournament_details_str} est confirmée.")
else: else:
body_parts.append(f"Votre inscription au tournoi {tournament_details_str} est confirmée.") 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") cloture_date = tournament.local_registration_federal_limit().strftime("%d/%m/%Y à %H:%M")
loc = "" loc = ""
if cloture_date is not None: if cloture_date is not None:

@ -48,6 +48,13 @@ class TournamentRegistrationService:
if not self.context['add_player_form'].is_valid(): if not self.context['add_player_form'].is_valid():
return 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 player_data = self.context['add_player_form'].cleaned_data
licence_id = player_data.get('licence_id', '').upper() licence_id = player_data.get('licence_id', '').upper()
@ -112,6 +119,12 @@ class TournamentRegistrationService:
self.context['registration_successful'] = True self.context['registration_successful'] = True
def handle_get_request(self): 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['add_player_form'] = AddPlayerForm()
self.context['team_form'] = self.initialize_team_form() self.context['team_form'] = self.initialize_team_form()
self.initialize_session_data() self.initialize_session_data()

@ -20,27 +20,34 @@
{% load static %} {% load static %}
{% load tz %} {% load tz %}
{% if form.errors or password_change_form.errors %}
<div class="cell medium-6 large-6 topblock my-block"> <div class="cell medium-6 large-6 topblock my-block">
<div class="bubble"> <div class="bubble">
<label class="title">Mes informations</label> <div>
{% if form.errors %} {% for field in form %}
<div class="alert"> {% if field.errors %}
{% for field, errors in form.errors.items %} {% for error in field.errors %}
{% for error in errors %} <div class="alert">{{ field.label }} : {{ error }}</div>
<p>{{ error }}</p>
{% endfor %} {% endfor %}
{% endif %}
{% endfor %} {% endfor %}
</div> </div>
<div>
{% for field in password_change_form %}
{% if field.errors %}
{% for error in field.errors %}
<div class="alert">{{ field.label }} : {{ error }}</div>
{% endfor %}
{% endif %} {% endif %}
<!-- Add non-field errors (if any) -->
{% if form.non_field_errors %}
<div class="alert">
{% for error in form.non_field_errors %}
<p>{{ error }}</p>
{% endfor %} {% endfor %}
</div> </div>
{% endif %} </div>
</div>
{% endif %}
<div class="cell medium-6 large-6 topblock my-block">
<div class="bubble">
<label class="title">Mes informations</label>
<form method="post"> <form method="post">
{% csrf_token %} {% csrf_token %}
{{ form.as_p }} {{ form.as_p }}
@ -51,25 +58,7 @@
<div class="cell medium-6 large-6 topblock my-block"> <div class="cell medium-6 large-6 topblock my-block">
<div class="bubble"> <div class="bubble">
<label class="title">Mot de passe</label> <label class="title">Mot de passe</label>
{% if password_change_form.errors %} <form method="post" action="{% url 'custom_password_change' %}">
<div class="alert">
{% for field, errors in password_change_form.errors.items %}
{% for error in errors %}
<p>{{ error }}</p>
{% endfor %}
{% endfor %}
</div>
{% endif %}
{% if password_change_form.non_field_errors %}
<div class="alert">
{% for error in password_change_form.non_field_errors %}
<p>{{ error }}</p>
{% endfor %}
</div>
{% endif %}
<form method="post" action="{% url 'password_change' %}">
{% csrf_token %} {% csrf_token %}
{{ password_change_form.as_p }} {{ password_change_form.as_p }}
<button type="submit" class="rounded-button">Modifier le mot de passe</button> <button type="submit" class="rounded-button">Modifier le mot de passe</button>

@ -28,20 +28,27 @@
{% else %} {% else %}
{% if team_form.errors %} {% if team_form.errors %}
<div class="alert alert-error"> <div>
{% if team_form.non_field_errors %}
{% for error in team_form.non_field_errors %}
<p>{{ error }}</p>
{% endfor %}
{% endif %}
{% for field in team_form %} {% for field in team_form %}
{% if field.errors %}
{% for error in field.errors %} {% for error in field.errors %}
<p>{{ error }}</p> <div class="alert">{{ field.label }} : {{ error }}</div>
{% endfor %} {% endfor %}
{% endif %}
{% endfor %} {% endfor %}
</div> </div>
{% endif %} {% endif %}
{% if messages %}
<div class="messages">
{% for message in messages %}
<div class="alert {% if message.tags %}alert-{{ message.tags }}{% endif %}">
{{ message }}
</div>
{% endfor %}
</div>
{% endif %}
<form method="post"> <form method="post">
{% csrf_token %} {% csrf_token %}
@ -52,6 +59,12 @@
Informations de contact Informations de contact
</div> </div>
</p> </p>
{% if team_form.non_field_errors %}
{% for error in team_form.non_field_errors %}
<p>{{ error }}</p>
{% endfor %}
{% endif %}
{{ team_form.as_p }} <!-- Render team registration form fields here --> {{ team_form.as_p }} <!-- Render team registration form fields here -->
</div> </div>
@ -124,24 +137,6 @@
{% endif %} {% endif %}
{% endif %} {% endif %}
<div class="margin10">
{% if add_player_form.errors %}
<div class="alert alert-error">
{% if add_player_form.non_field_errors %}
{% for error in add_player_form.non_field_errors %}
<p>{{ error }}</p>
{% endfor %}
{% endif %}
{% for field in add_player_form %}
{% for error in field.errors %}
<p>{{ error }}</p>
{% endfor %}
{% endfor %}
</div>
{% endif %}
</div>
<button type="submit" name="add_player" class="rounded-button"> <button type="submit" name="add_player" class="rounded-button">
{% if add_player_form.user_without_licence %} {% if add_player_form.user_without_licence %}
Confirmer Confirmer

@ -11,7 +11,7 @@
<div class="grid-x"> <div class="grid-x">
<div class="bubble"> <div class="bubble">
<div class="cell medium-6 large-6 my-block"> <div class="cell medium-6 large-6 my-block">
{% if form.errors %} {% if form.non_field_errors %}
<div class="alert alert-error"> <div class="alert alert-error">
{% if form.non_field_errors %} {% if form.non_field_errors %}
{% for error in form.non_field_errors %} {% for error in form.non_field_errors %}
@ -28,7 +28,9 @@
{% endif %} {% endif %}
<form method="post" action="{% url 'custom-login' %}"> <form method="post" action="{% url 'custom-login' %}">
{% csrf_token %} {% csrf_token %}
{% if request.GET.next and 'reset' not in request.GET.next and 'password_reset' not in request.GET.next %}
<input type="hidden" name="next" value="{{ request.GET.next }}"> <input type="hidden" name="next" value="{{ request.GET.next }}">
{% endif %}
<label for="username">Identifiant ou e-mail </label> <label for="username">Identifiant ou e-mail </label>
<input type="text" name="username" id="username" required> <input type="text" name="username" id="username" required>

@ -10,9 +10,9 @@
<p> <p>
Votre mot de passe a été réinitialisé avec succès. Vous pouvez maintenant vous connecter avec votre nouveau mot de passe. Votre mot de passe a été réinitialisé avec succès. Vous pouvez maintenant vous connecter avec votre nouveau mot de passe.
</p> </p>
<p> <div class="topmargin20">
<a href="{% url 'login' %}" class="rounded-button">Se connecter</a> <a href="{% url 'login' %}" class="rounded-button">Se connecter</a>
</p> </div>
</div> </div>
</div> </div>
</div> </div>

@ -7,17 +7,6 @@
<div class="grid-x"> <div class="grid-x">
<div class="bubble"> <div class="bubble">
<div class="cell medium-6 large-6 my-block"> <div class="cell medium-6 large-6 my-block">
{% if form.errors %}
<div class="alert">
{% for field, errors in form.errors.items %}
{% for error in errors %}
<p>{{ error }}</p>
{% endfor %}
{% endfor %}
</div>
{% endif %}
<!-- Add non-field errors (if any) -->
{% if form.non_field_errors %} {% if form.non_field_errors %}
<div class="alert"> <div class="alert">
{% for error in form.non_field_errors %} {% for error in form.non_field_errors %}
@ -35,13 +24,10 @@
<button type="submit" class="rounded-button">Réinitialiser le mot de passe</button> <button type="submit" class="rounded-button">Réinitialiser le mot de passe</button>
</form> </form>
<p> <div class="topmargin20">
<a href="{% url 'login' %}" class="styled-link">Retour à la connexion</a> <a href="{% url 'login' %}" class="styled-link">Retour à la connexion</a>
</p>
</div> </div>
{% for message in messages %} </div>
<div class="alert alert-{{ message.tags }}">{{ message }}</div>
{% endfor %}
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

@ -11,9 +11,9 @@
Un e-mail contenant un lien pour réinitialiser votre mot de passe a été envoyé à votre adresse. 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. Veuillez vérifier votre boîte de réception.
</p> </p>
<p> <div class="topmargin20">
<a href="{% url 'login' %}" class="styled-link">Retour à la connexion</a> <a href="{% url 'login' %}" class="styled-link">Retour à la connexion</a>
</p> </div>
</div> </div>
</div> </div>
</div> </div>

@ -7,16 +7,6 @@
<div class="grid-x"> <div class="grid-x">
<div class="bubble"> <div class="bubble">
<div class="cell medium-6 large-6 my-block"> <div class="cell medium-6 large-6 my-block">
{% if form.errors %}
<div class="alert">
{% for field, errors in form.errors.items %}
{% for error in errors %}
<p>{{ error }}</p>
{% endfor %}
{% endfor %}
</div>
{% endif %}
<!-- Add non-field errors (if any) --> <!-- Add non-field errors (if any) -->
{% if form.non_field_errors %} {% if form.non_field_errors %}
<div class="alert"> <div class="alert">
@ -31,13 +21,10 @@
<input type="email" name="email" id="email" required> <input type="email" name="email" id="email" required>
<button type="submit" class="rounded-button">Envoyer le lien de réinitialisation</button> <button type="submit" class="rounded-button">Envoyer le lien de réinitialisation</button>
</form> </form>
<p> <div class="topmargin20">
<a href="{% url 'login' %}" class="styled-link">Retour à la connexion</a> <a href="{% url 'login' %}" class="styled-link">Retour à la connexion</a>
</p>
</div> </div>
{% for message in messages %} </div>
<div class="alert alert-{{ message.tags }}">{{ message }}</div>
{% endfor %}
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

@ -8,20 +8,25 @@
{% load static %} {% load static %}
{% load tz %} {% load tz %}
<div class="grid-x"> <div class="grid">
<div class="bubble">
<div class="cell medium-6 large-6 my-block">
{% if form.errors %} {% if form.errors %}
<div class="alert"> <div class="cell medium-6 large-6 topblock my-block">
{% for field, errors in form.errors.items %} <div class="bubble">
{% for error in errors %} <div>
<p>{{ error }}</p> {% for field in form %}
{% if field.errors %}
{% for error in field.errors %}
<div class="alert">{{ field.label }} : {{ error }}</div>
{% endfor %} {% endfor %}
{% endif %}
{% endfor %} {% endfor %}
</div> </div>
</div>
</div>
{% endif %} {% endif %}
<!-- Add non-field errors (if any) --> <div class="cell medium-6 large-6 my-block">
<div class="bubble">
{% if form.non_field_errors %} {% if form.non_field_errors %}
<div class="alert"> <div class="alert">
{% for error in form.non_field_errors %} {% for error in form.non_field_errors %}
@ -36,7 +41,7 @@
<button type="submit" class="rounded-button">Créer votre compte</button> <button type="submit" class="rounded-button">Créer votre compte</button>
</form> </form>
</div> </div>
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

@ -81,12 +81,6 @@
{% if tournament.is_unregistration_possible %} {% if tournament.is_unregistration_possible %}
<p> <p>
<div class="margin10">
{% for message in messages %}
<div class="alert alert-{{ message.tags }}">{{ message }}</div>
{% endfor %}
</div>
<a href="{% url 'unregister_tournament' tournament.id %}" <a href="{% url 'unregister_tournament' tournament.id %}"
class="rounded-button destructive-button" class="rounded-button destructive-button"
onclick="return confirm('Êtes-vous sûr de vouloir vous désinscrire ?');"> onclick="return confirm('Êtes-vous sûr de vouloir vous désinscrire ?');">
@ -198,9 +192,6 @@
</p> </p>
{% endif %} {% endif %}
{% else %} {% else %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }}">{{ message }}</div>
{% endfor %}
<p> <p>
<div> <div>
<a href="{% url 'login' %}?next={{ request.path }}" class="styled-link">Connectez-vous !</a> <a href="{% url 'login' %}?next={{ request.path }}" class="styled-link">Connectez-vous !</a>

@ -53,13 +53,7 @@ urlpatterns = [
path('utils/xls-to-csv/', views.xls_to_csv, name='xls-to-csv'), path('utils/xls-to-csv/', views.xls_to_csv, name='xls-to-csv'),
path('mail-test/', views.simple_form_view, name='mail-test'), path('mail-test/', views.simple_form_view, name='mail-test'),
path('login/', CustomLoginView.as_view(), name='custom-login'), path('login/', CustomLoginView.as_view(), name='custom-login'),
path('password_change/', path('custom_password_change/', views.custom_password_change, name='custom_password_change'),
auth_views.PasswordChangeView.as_view(
success_url='/profile/', # Redirect back to profile after success
form_class=CustomPasswordChangeForm
),
name='password_change'
),
path('logout/', views.custom_logout, name='custom_logout'), path('logout/', views.custom_logout, name='custom_logout'),
path('signup/', views.signup, name='signup'), # URL pattern for signup path('signup/', views.signup, name='signup'), # URL pattern for signup
# path('profile/', views.profile, name='profile'), # URL pattern for signup # path('profile/', views.profile, name='profile'), # URL pattern for signup
@ -68,7 +62,9 @@ urlpatterns = [
path('tournaments/<str:tournament_id>/unregister/', views.unregister_tournament, name='unregister_tournament'), path('tournaments/<str:tournament_id>/unregister/', views.unregister_tournament, name='unregister_tournament'),
path('password_reset/', auth_views.PasswordResetView.as_view(), name='password_reset'), 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('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('profile/', views.ProfileUpdateView.as_view(), name='profile'),
path('admin/tournament-import/', views.tournament_import_view, name='tournament_import'), path('admin/tournament-import/', views.tournament_import_view, name='tournament_import'),
path('admin/status/', views.status_page, name='status_page'), path('admin/status/', views.status_page, name='status_page'),

@ -2,6 +2,11 @@
import os import os
import csv 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.shortcuts import render, get_object_or_404
from django.http import JsonResponse, HttpResponse from django.http import JsonResponse, HttpResponse
from django.utils.encoding import force_str from django.utils.encoding import force_str
@ -818,6 +823,54 @@ class CustomPasswordResetConfirmView(PasswordResetConfirmView):
except (TypeError, ValueError, User.DoesNotExist): except (TypeError, ValueError, User.DoesNotExist):
raise Http404("User not found") 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 @login_required
def my_tournaments(request): def my_tournaments(request):
user = request.user user = request.user

Loading…
Cancel
Save