diff --git a/apps/participation/templates/participation/mails/request_validation.html b/apps/participation/templates/participation/mails/request_validation.html index 9693d0a..bc9da86 100644 --- a/apps/participation/templates/participation/mails/request_validation.html +++ b/apps/participation/templates/participation/mails/request_validation.html @@ -13,7 +13,9 @@ Bonjour {{ user.registration }}, L'équipe « {{ team.name }} » ({{ team.trigram }}) vient de demander à valider son équipe pour participer au {{ team.participation.get_problem_display }} des Correspondances des Jeunes Mathématicien·ne·s. Vous pouvez décider d'accepter ou de refuser l'équipe en vous rendant sur la page de l'équipe : -{% url "participation:team_detail" pk=team.pk %} + + https://{{ domain }}{% url "participation:team_detail" pk=team.pk %} +
diff --git a/apps/participation/templates/participation/mails/request_validation.txt b/apps/participation/templates/participation/mails/request_validation.txt index 6bd4f3e..522f173 100644 --- a/apps/participation/templates/participation/mails/request_validation.txt +++ b/apps/participation/templates/participation/mails/request_validation.txt @@ -3,7 +3,7 @@ Bonjour {{ user.registration }}, L'équipe « {{ team.name }} » ({{ team.trigram }}) vient de demander à valider son équipe pour participer au {{ team.participation.get_problem_display }} des Correspondances des Jeunes Mathématicien·ne·s. Vous pouvez décider d'accepter ou de refuser l'équipe en vous rendant sur la page de l'équipe : -{% url "participation:team_detail" pk=team.pk %} +https://{{ domain }}{% url "participation:team_detail" pk=team.pk %} Cordialement, diff --git a/apps/participation/tests.py b/apps/participation/tests.py index b1b7bd2..82a0309 100644 --- a/apps/participation/tests.py +++ b/apps/participation/tests.py @@ -669,7 +669,7 @@ class TestStudentParticipation(TestCase): def test_forbidden_access(self): """ - Load personnal pages and ensure that these are protected. + Load personal pages and ensure that these are protected. """ self.user.registration.team = self.team self.user.registration.save() diff --git a/apps/participation/views.py b/apps/participation/views.py index 5534278..34e2cf0 100644 --- a/apps/participation/views.py +++ b/apps/participation/views.py @@ -5,6 +5,7 @@ from corres2math.lists import get_sympa_client from corres2math.matrix import Matrix from corres2math.views import AdminMixin from django.contrib.auth.mixins import LoginRequiredMixin +from django.contrib.sites.models import Site from django.core.exceptions import PermissionDenied from django.core.mail import send_mail from django.db import transaction @@ -38,6 +39,8 @@ class CreateTeamView(LoginRequiredMixin, CreateView): def dispatch(self, request, *args, **kwargs): user = request.user + if not user.is_authenticated: + return super().handle_no_permission() registration = user.registration if not registration.participates: raise PermissionDenied(_("You don't participate, so you can't create a team.")) @@ -84,6 +87,8 @@ class JoinTeamView(LoginRequiredMixin, FormView): def dispatch(self, request, *args, **kwargs): user = request.user + if not user.is_authenticated: + return super().handle_no_permission() registration = user.registration if not registration.participates: raise PermissionDenied(_("You don't participate, so you can't create a team.")) @@ -208,7 +213,7 @@ class TeamDetailView(LoginRequiredMixin, FormMixin, ProcessFormView, DetailView) self.object.participation.save() for admin in AdminRegistration.objects.all(): - mail_context = dict(user=admin.user, team=self.object) + mail_context = dict(user=admin.user, team=self.object, domain=Site.objects.first().domain) mail_plain = render_to_string("participation/mails/request_validation.txt", mail_context) mail_html = render_to_string("participation/mails/request_validation.html", mail_context) admin.user.email_user("[Corres2math] Validation d'équipe", mail_plain, html_message=mail_html) @@ -264,6 +269,8 @@ class TeamUpdateView(LoginRequiredMixin, UpdateView): def dispatch(self, request, *args, **kwargs): user = request.user + if not user.is_authenticated: + return super().handle_no_permission() if user.registration.is_admin or user.registration.participates and \ user.registration.team and \ user.registration.team.pk == kwargs["pk"]: @@ -298,6 +305,8 @@ class TeamAuthorizationsView(LoginRequiredMixin, DetailView): def dispatch(self, request, *args, **kwargs): user = request.user + if not user.is_authenticated: + return super().handle_no_permission() if user.registration.is_admin or user.registration.participates and user.registration.team.pk == kwargs["pk"]: return super().dispatch(request, *args, **kwargs) raise PermissionDenied @@ -376,6 +385,8 @@ class ParticipationDetailView(LoginRequiredMixin, DetailView): def dispatch(self, request, *args, **kwargs): user = request.user + if not user.is_authenticated: + return super().handle_no_permission() if not self.get_object().valid: raise PermissionDenied(_("The team is not validated yet.")) if user.registration.is_admin or user.registration.participates \ @@ -500,6 +511,8 @@ class UploadVideoView(LoginRequiredMixin, UpdateView): def dispatch(self, request, *args, **kwargs): user = request.user + if not user.is_authenticated: + return super().handle_no_permission() if user.registration.is_admin or user.registration.participates \ and user.registration.team.participation.pk == self.get_object().participation.pk: return super().dispatch(request, *args, **kwargs) diff --git a/apps/registration/forms.py b/apps/registration/forms.py index ecb2b9f..5c4aec3 100644 --- a/apps/registration/forms.py +++ b/apps/registration/forms.py @@ -72,10 +72,13 @@ class PhotoAuthorizationForm(forms.ModelForm): Form to send a photo authorization. """ def clean_photo_authorization(self): - file = self.files["photo_authorization"] - if file.content_type not in ["application/pdf", "image/png", "image/jpeg"]: - raise ValidationError(_("The uploaded file must be a PDF, PNG of JPEG file.")) - return self.cleaned_data["photo_authorization"] + if "photo_authorization" in self.files: + file = self.files["photo_authorization"] + if file.size > 2e6: + raise ValidationError(_("The uploaded file size must be under 2 Mo.")) + if file.content_type not in ["application/pdf", "image/png", "image/jpeg"]: + raise ValidationError(_("The uploaded file must be a PDF, PNG of JPEG file.")) + return self.cleaned_data["photo_authorization"] def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) diff --git a/apps/registration/templates/registration/email_validation_complete.html b/apps/registration/templates/registration/email_validation_complete.html index f58a7e5..4039e0b 100644 --- a/apps/registration/templates/registration/email_validation_complete.html +++ b/apps/registration/templates/registration/email_validation_complete.html @@ -19,7 +19,11 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% else %}- {% trans "The link was invalid. The token may have expired. Please send us an email to activate your account." %} + {% if user.is_authenticated and user.registration.email_confirmed %} + {% trans "The link was invalid. The token may have expired, or your account is already activated. However, your account seems to be already valid." %} + {% else %} + {% trans "The link was invalid. The token may have expired, or your account is already activated. Please send us an email to activate your account." %} + {% endif %}
{% endif %} diff --git a/apps/registration/tests.py b/apps/registration/tests.py index f33dc97..4f3ff34 100644 --- a/apps/registration/tests.py +++ b/apps/registration/tests.py @@ -5,13 +5,14 @@ from corres2math.tokens import email_validation_token from django.contrib.auth.models import User from django.contrib.contenttypes.models import ContentType from django.contrib.sites.models import Site +from django.core.files.uploadedfile import SimpleUploadedFile from django.core.management import call_command from django.test import TestCase from django.urls import reverse from django.utils import timezone from django.utils.encoding import force_bytes from django.utils.http import urlsafe_base64_encode -from participation.models import Phase +from participation.models import Phase, Team from .models import AdminRegistration, CoachRegistration, StudentRegistration @@ -35,6 +36,24 @@ class TestIndexPage(TestCase): response = self.client.get(reverse("registration:user_detail", args=(1,))) self.assertRedirects(response, reverse("login") + "?next=" + reverse("registration:user_detail", args=(1,))) + Team.objects.create() + response = self.client.get(reverse("participation:team_detail", args=(1,))) + self.assertRedirects(response, reverse("login") + "?next=" + reverse("participation:team_detail", args=(1,))) + response = self.client.get(reverse("participation:update_team", args=(1,))) + self.assertRedirects(response, reverse("login") + "?next=" + reverse("participation:update_team", args=(1,))) + response = self.client.get(reverse("participation:create_team")) + self.assertRedirects(response, reverse("login") + "?next=" + reverse("participation:create_team")) + response = self.client.get(reverse("participation:join_team")) + self.assertRedirects(response, reverse("login") + "?next=" + reverse("participation:join_team")) + response = self.client.get(reverse("participation:team_authorizations", args=(1,))) + self.assertRedirects(response, reverse("login") + "?next=" + + reverse("participation:team_authorizations", args=(1,))) + response = self.client.get(reverse("participation:participation_detail", args=(1,))) + self.assertRedirects(response, reverse("login") + "?next=" + + reverse("participation:participation_detail", args=(1,))) + response = self.client.get(reverse("participation:upload_video", args=(1,))) + self.assertRedirects(response, reverse("login") + "?next=" + reverse("participation:upload_video", args=(1,))) + class TestRegistration(TestCase): def setUp(self) -> None: @@ -268,6 +287,14 @@ class TestRegistration(TestCase): )) self.assertEqual(response.status_code, 200) + # Don't send too large files + response = self.client.post(reverse("registration:upload_user_photo_authorization", + args=(self.student.registration.pk,)), data=dict( + photo_authorization=SimpleUploadedFile("file.pdf", content=int(0).to_bytes(2000001, "big"), + content_type="application/pdf"), + )) + self.assertEqual(response.status_code, 200) + response = self.client.post(reverse("registration:upload_user_photo_authorization", args=(self.student.registration.pk,)), data=dict( photo_authorization=open("corres2math/static/Autorisation de droit à l'image - majeur.pdf", "rb"), diff --git a/apps/registration/views.py b/apps/registration/views.py index 6247e09..a0efeeb 100644 --- a/apps/registration/views.py +++ b/apps/registration/views.py @@ -1,7 +1,5 @@ import os -from django_tables2 import SingleTableView - from corres2math.tokens import email_validation_token from corres2math.views import AdminMixin from django.conf import settings @@ -15,11 +13,12 @@ from django.urls import reverse_lazy from django.utils.http import urlsafe_base64_decode from django.utils.translation import gettext_lazy as _ from django.views.generic import CreateView, DetailView, RedirectView, TemplateView, UpdateView, View +from django_tables2 import SingleTableView from magic import Magic from participation.models import Phase from .forms import CoachRegistrationForm, PhotoAuthorizationForm, SignupForm, StudentRegistrationForm, UserForm -from .models import StudentRegistration, Registration +from .models import Registration, StudentRegistration from .tables import RegistrationTable diff --git a/corres2math.cron b/corres2math.cron index 1e07e98..80117f0 100644 --- a/corres2math.cron +++ b/corres2math.cron @@ -3,3 +3,6 @@ * * * * * cd /code && python manage.py send_mail -c 1 * * * * * cd /code && python manage.py retry_deferred -c 1 0 0 * * * cd /code && python manage.py purge_mail_log 7 -c 1 + +# Rebuild search index +0 * * * * cd /code && python manage.py update_index -v 0 diff --git a/corres2math/templates/about.html b/corres2math/templates/about.html new file mode 100644 index 0000000..963afea --- /dev/null +++ b/corres2math/templates/about.html @@ -0,0 +1,40 @@ +{% extends "base.html" %} + +{% block contenttitle %} ++ La plateforme d'inscription des Correspondances des Jeunes Mathématiciennes a été développée entre 2019 et 2021 + par Yohann D'ANELLO, bénévole pour l'association Animath. Elle est vouée à être utilisée par les participants + pour intéragir avec les organisateurs et les autres participants. +
+ ++ La plateforme est développée avec le framework Django et le code + source est accessible librement sur Gitlab. + Le code est distribué sous la licence GNU GPL v3, + qui vous autorise à consulter le code, à le partager, à réutiliser des parties du code et à contribuer. +
+ ++ Le site principal présent sur https://inscription.correspondances-maths.fr + est hébergé chez Scaleway. +
+ ++ Les données collectées par cette plateforme sont utilisées uniquement dans le cadre des Correspondances et sont + détruites dès l'action touche à sa fin, soit au plus tard 1 an après le début de l'action. Sur autorisation + explicite, des informations de contact peuvent être conservées afin d'être tenu au courant des actions futures + de l'association Animath. Aucune information personnelle n'est collectée à votre insu. Aucune information + personnelle n'est cédée à des tiers. +
+ ++ Pour toute demande ou réclammation, merci de nous contacter à l'adresse + + contact@correspondances-maths.fr + . +
+{% endblock %} diff --git a/corres2math/templates/base.html b/corres2math/templates/base.html index a06cf17..9f24a75 100644 --- a/corres2math/templates/base.html +++ b/corres2math/templates/base.html @@ -192,7 +192,7 @@ class="form-inline"> {% trans "Contact us" %} — + class="text-muted"> {% trans "Contact us" %} {% csrf_token %} + + + {% trans "About" %} — + + +