diff --git a/apps/registration/forms.py b/apps/registration/forms.py
index 0bcca9b..ae519f0 100644
--- a/apps/registration/forms.py
+++ b/apps/registration/forms.py
@@ -26,6 +26,18 @@ class SignupForm(UserCreationForm):
fields = ('first_name', 'last_name', 'email', 'password1', 'password2', 'role',)
+class UserForm(forms.ModelForm):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.fields["first_name"].required = True
+ self.fields["last_name"].required = True
+ self.fields["email"].required = True
+
+ class Meta:
+ model = User
+ fields = ('first_name', 'last_name', 'email',)
+
+
class StudentRegistrationForm(forms.ModelForm):
class Meta:
model = StudentRegistration
diff --git a/apps/registration/models.py b/apps/registration/models.py
index 4ab8d2b..e2dcc05 100644
--- a/apps/registration/models.py
+++ b/apps/registration/models.py
@@ -50,6 +50,10 @@ class Registration(PolymorphicModel):
def type(self):
raise NotImplementedError
+ @property
+ def form_class(self):
+ raise NotImplementedError
+
@property
def participates(self):
return isinstance(self, StudentRegistration) or isinstance(self, CoachRegistration)
@@ -90,6 +94,11 @@ class StudentRegistration(Registration):
def type(self):
return _("student")
+ @property
+ def form_class(self):
+ from registration.forms import StudentRegistrationForm
+ return StudentRegistrationForm
+
class Meta:
verbose_name = _("student registration")
verbose_name_plural = _("student registrations")
@@ -113,6 +122,11 @@ class CoachRegistration(Registration):
def type(self):
return _("coach")
+ @property
+ def form_class(self):
+ from registration.forms import CoachRegistrationForm
+ return CoachRegistrationForm
+
class Meta:
verbose_name = _("coach registration")
verbose_name_plural = _("coach registrations")
@@ -127,6 +141,11 @@ class AdminRegistration(Registration):
def type(self):
return _("admin")
+ @property
+ def form_class(self):
+ from registration.forms import AdminRegistrationForm
+ return AdminRegistrationForm
+
class Meta:
verbose_name = _("admin registration")
verbose_name_plural = _("admin registrations")
diff --git a/apps/registration/templates/registration/update_user.html b/apps/registration/templates/registration/update_user.html
new file mode 100644
index 0000000..7e282d5
--- /dev/null
+++ b/apps/registration/templates/registration/update_user.html
@@ -0,0 +1,15 @@
+{% extends "base.html" %}
+
+{% load crispy_forms_filters i18n %}
+
+{% block content %}
+
+{% endblock content %}
+
diff --git a/apps/registration/templates/registration/update_user_modal.html b/apps/registration/templates/registration/update_user_modal.html
new file mode 100644
index 0000000..34e6cb9
--- /dev/null
+++ b/apps/registration/templates/registration/update_user_modal.html
@@ -0,0 +1,21 @@
+{% load i18n %}
+
+
\ No newline at end of file
diff --git a/apps/registration/templates/registration/user_detail.html b/apps/registration/templates/registration/user_detail.html
new file mode 100644
index 0000000..06800af
--- /dev/null
+++ b/apps/registration/templates/registration/user_detail.html
@@ -0,0 +1,59 @@
+{% extends "base.html" %}
+
+{% load i18n %}
+
+{% block content %}
+{% trans "any" as any %}
+
+
+
+
+
+ - {% trans "Last name:" %}
+ - {{ user.last_name }}
+
+ - {% trans "First name:" %}
+ - {{ user.first_name }}
+
+ - {% trans "Email:" %}
+ - {{ user.email }}
+
+ {% if user.registration.studentregistration %}
+ - {% trans "Student class:" %}
+ - {{ user.registration.get_student_class_display }}
+
+ - {% trans "School:" %}
+ - {{ user.registration.school }}
+ {% elif user.registration.coachregistration %}
+ - {% trans "Profesional activity:" %}
+ - {{ user.registration.professional_activity }}
+ {% elif user.registration.adminregistration %}
+ - {% trans "Role:" %}
+ - {{ user.registration.role }}
+ {% endif %}
+
+ - {% trans "Grant Animath to contact me in the future about other actions:" %}
+ - {{ user.registration.give_contact_to_animath|yesno }}
+
+
+
+
+
+ {% include "registration/update_user_modal.html" %}
+{% endblock %}
+
+{% block extrajavascript %}
+
+{% endblock %}
diff --git a/apps/registration/tests.py b/apps/registration/tests.py
index 9c815f9..a81945d 100644
--- a/apps/registration/tests.py
+++ b/apps/registration/tests.py
@@ -23,6 +23,11 @@ class TestRegistration(TestCase):
)
self.client.force_login(self.user)
+ self.student = User.objects.create(email="student@example.com")
+ StudentRegistration.objects.create(user=self.student, student_class=11, school="Earth")
+ self.coach = User.objects.create(email="coach@example.com")
+ CoachRegistration.objects.create(user=self.coach, professional_activity="Teacher")
+
def test_registration(self):
response = self.client.get(reverse("registration:signup"))
self.assertEqual(response.status_code, 200)
@@ -105,15 +110,46 @@ class TestRegistration(TestCase):
))
self.assertRedirects(response, reverse("index"), 302, 200)
- def test_change_email(self):
- self.user.email = "newaddressmail@example.com"
- self.user.save()
- self.assertEqual(self.user.email, self.user.username)
+ def test_user_detail(self):
+ response = self.client.get(reverse("registration:my_account_detail"))
+ self.assertRedirects(response, reverse("registration:user_detail", args=(self.user.pk,)))
+
+ response = self.client.get(reverse("registration:user_detail", args=(self.user.pk,)))
+ self.assertEqual(response.status_code, 200)
+
+ def test_update_user(self):
+ for user, data in [(self.user, dict(role="Bot")),
+ (self.student, dict(student_class=11, school="Sky")),
+ (self.coach, dict(professional_activity="God"))]:
+ response = self.client.get(reverse("registration:update_user", args=(user.pk,)))
+ self.assertEqual(response.status_code, 200)
+
+ response = self.client.post(reverse("registration:update_user", args=(user.pk,)), data=dict(
+ first_name="Changed",
+ last_name="Name",
+ email="new_" + user.email,
+ give_contact_to_animath=True,
+ ))
+ self.assertEqual(response.status_code, 200)
+
+ data.update(
+ first_name="Changed",
+ last_name="Name",
+ email="new_" + user.email,
+ give_contact_to_animath=True,
+ )
+ response = self.client.post(reverse("registration:update_user", args=(user.pk,)), data=data)
+ self.assertRedirects(response, reverse("registration:user_detail", args=(user.pk,)), 302, 200)
+ user.refresh_from_db()
+ self.assertEqual(user.email, user.username)
+ self.assertFalse(user.registration.email_confirmed)
+ self.assertEqual(user.first_name, "Changed")
def test_string_render(self):
# TODO These string field tests will be removed when used in a template
str(self.user.registration)
self.assertRaises(NotImplementedError, lambda: Registration().type)
+ self.assertRaises(NotImplementedError, lambda: Registration().form_class)
str(StudentRegistration().type)
str(CoachRegistration().type)
str(self.user.registration.type) # AdminRegistration
diff --git a/apps/registration/urls.py b/apps/registration/urls.py
index 324dc72..7bd4d95 100644
--- a/apps/registration/urls.py
+++ b/apps/registration/urls.py
@@ -1,6 +1,7 @@
from django.urls import path
-from .views import SignupView, UserResendValidationEmailView, UserValidateView, UserValidationEmailSentView
+from .views import MyAccountDetailView, SignupView, UserDetailView, UserResendValidationEmailView, UserUpdateView,\
+ UserValidateView, UserValidationEmailSentView
app_name = "registration"
@@ -10,4 +11,7 @@ urlpatterns = [
path('validate_email/resend//', UserResendValidationEmailView.as_view(),
name='email_validation_resend'),
path('validate_email///', UserValidateView.as_view(), name='email_validation'),
+ path("user/", MyAccountDetailView.as_view(), name="my_account_detail"),
+ path("user//", UserDetailView.as_view(), name="user_detail"),
+ path("user//update/", UserUpdateView.as_view(), name="update_user"),
]
diff --git a/apps/registration/views.py b/apps/registration/views.py
index a9a21ba..ef33d99 100644
--- a/apps/registration/views.py
+++ b/apps/registration/views.py
@@ -8,9 +8,9 @@ from django.shortcuts import redirect, resolve_url
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, TemplateView
+from django.views.generic import CreateView, DetailView, RedirectView, TemplateView, UpdateView
-from .forms import CoachRegistrationForm, SignupForm, StudentRegistrationForm
+from .forms import CoachRegistrationForm, SignupForm, StudentRegistrationForm, UserForm
class SignupView(CreateView):
@@ -118,3 +118,40 @@ class UserResendValidationEmailView(LoginRequiredMixin, DetailView):
user = self.get_object()
user.registration.send_email_validation_link()
return redirect('registration:email_validation_sent')
+
+
+class MyAccountDetailView(LoginRequiredMixin, RedirectView):
+ def get_redirect_url(self, *args, **kwargs):
+ return reverse_lazy("registration:user_detail", args=(self.request.user.pk,))
+
+
+class UserDetailView(LoginRequiredMixin, DetailView):
+ model = User
+ template_name = "registration/user_detail.html"
+
+
+class UserUpdateView(LoginRequiredMixin, UpdateView):
+ model = User
+ form_class = UserForm
+ template_name = "registration/update_user.html"
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ user = self.get_object()
+ context["registration_form"] = user.registration.form_class(data=self.request.POST or None,
+ instance=self.object.registration)
+ return context
+
+ @transaction.atomic
+ def form_valid(self, form):
+ user = form.instance
+ registration_form = user.registration.form_class(data=self.request.POST or None,
+ instance=self.object.registration)
+ if not registration_form.is_valid():
+ return self.form_invalid(form)
+
+ registration_form.save()
+ return super().form_valid(form)
+
+ def get_success_url(self):
+ return reverse_lazy("registration:user_detail", args=(self.object.pk,))
diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po
index 22b6098..aa518b5 100644
--- a/locale/fr/LC_MESSAGES/django.po
+++ b/locale/fr/LC_MESSAGES/django.po
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Corres2math\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2020-09-24 11:19+0200\n"
+"POT-Creation-Date: 2020-09-24 18:38+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Yohann D'ANELLO \n"
"Language-Team: LANGUAGE \n"
@@ -34,6 +34,7 @@ msgstr "Cette tâche a échoué avec succès."
#: apps/participation/templates/participation/join_team_modal.html:16
#: apps/participation/templates/participation/update_team_modal.html:16
#: apps/registration/templates/registration/login_modal.html:16
+#: apps/registration/templates/registration/update_user_modal.html:16
msgid "Close"
msgstr "Fermer"
@@ -41,7 +42,7 @@ msgstr "Fermer"
msgid "Logs"
msgstr "Logs"
-#: apps/logs/models.py:22 apps/registration/models.py:16
+#: apps/logs/models.py:22 apps/registration/models.py:15
msgid "user"
msgstr "utilisateur"
@@ -102,7 +103,7 @@ msgstr "changelogs"
msgid "Changelog of type \"{action}\" for model {model} at {timestamp}"
msgstr "Changelog de type \"{action}\" pour le modèle {model} le {timestamp}"
-#: apps/participation/forms.py:14 apps/participation/models.py:20
+#: apps/participation/forms.py:14 apps/participation/models.py:19
msgid "The trigram must be composed of three uppercase letters."
msgstr "Le trigramme doit être composé de trois lettres majuscules."
@@ -110,27 +111,27 @@ msgstr "Le trigramme doit être composé de trois lettres majuscules."
msgid "No team was found with this access code."
msgstr "Aucune équipe n'a été trouvée avec ce code d'accès."
-#: apps/participation/models.py:13
+#: apps/participation/models.py:12
msgid "name"
msgstr "nom"
-#: apps/participation/models.py:19
+#: apps/participation/models.py:18
msgid "trigram"
msgstr "trigramme"
-#: apps/participation/models.py:27
+#: apps/participation/models.py:26
msgid "access code"
msgstr "code d'accès"
-#: apps/participation/models.py:28
+#: apps/participation/models.py:27
msgid "The access code let other people to join the team."
msgstr "Le code d'accès permet aux autres participants de rejoindre l'équipe."
-#: apps/participation/models.py:32
+#: apps/participation/models.py:31
msgid "Grant Animath to publish my video"
msgstr "Autoriser Animath à publier ma vidéo"
-#: apps/participation/models.py:33
+#: apps/participation/models.py:32
msgid ""
"Give the authorisation to publish the video on the main website to promote "
"the action."
@@ -138,80 +139,80 @@ msgstr ""
"Donner l'autorisation de publier la vidéo sur le site principal pour "
"promouvoir les Correspondances."
-#: apps/participation/models.py:43
+#: apps/participation/models.py:42
#, python-brace-format
msgid "Team {name} ({trigram})"
msgstr "Équipe {name} ({trigram})"
-#: apps/participation/models.py:46 apps/participation/models.py:57
-#: apps/registration/models.py:73 apps/registration/models.py:106
+#: apps/participation/models.py:45 apps/participation/models.py:56
+#: apps/registration/models.py:76 apps/registration/models.py:114
msgid "team"
msgstr "équipe"
-#: apps/participation/models.py:47
+#: apps/participation/models.py:46
msgid "teams"
msgstr "équipes"
-#: apps/participation/models.py:61
+#: apps/participation/models.py:60
#, python-brace-format
msgid "Problem #{problem:d}"
msgstr "Problème n°{problem:d}"
-#: apps/participation/models.py:64
+#: apps/participation/models.py:63
msgid "problem number"
msgstr "numéro de problème"
-#: apps/participation/models.py:73
+#: apps/participation/models.py:72
msgid "solution video"
msgstr "vidéo de solution"
-#: apps/participation/models.py:82
+#: apps/participation/models.py:81
msgid "received participation"
msgstr "participation reçue"
-#: apps/participation/models.py:91
+#: apps/participation/models.py:90
msgid "synthesis video"
msgstr "vidéo de synthèse"
-#: apps/participation/models.py:95
+#: apps/participation/models.py:94
#, python-brace-format
msgid "Participation of the team {name} ({trigram})"
msgstr "Participation de l'équipe {name} ({trigram})"
-#: apps/participation/models.py:98 apps/participation/models.py:106
+#: apps/participation/models.py:97 apps/participation/models.py:105
msgid "participation"
msgstr "participation"
-#: apps/participation/models.py:99
+#: apps/participation/models.py:98
msgid "participations"
msgstr "participations"
-#: apps/participation/models.py:110
+#: apps/participation/models.py:109
msgid "link"
msgstr "lien"
-#: apps/participation/models.py:111
+#: apps/participation/models.py:110
msgid "The full video link."
msgstr "Le lien complet de la vidéo."
-#: apps/participation/models.py:117
+#: apps/participation/models.py:116
msgid "valid"
msgstr "valide"
-#: apps/participation/models.py:118
+#: apps/participation/models.py:117
msgid "The video got the validation of the administrators."
msgstr "La vidéo a été validée par les administrateurs."
-#: apps/participation/models.py:122
+#: apps/participation/models.py:121
#, python-brace-format
msgid "Video of team {name} ({trigram})"
msgstr "Vidéo de l'équipe {name} ({trigram})"
-#: apps/participation/models.py:126
+#: apps/participation/models.py:125
msgid "video"
msgstr "vidéo"
-#: apps/participation/models.py:127
+#: apps/participation/models.py:126
msgid "videos"
msgstr "vidéos"
@@ -236,6 +237,7 @@ msgid "Join team"
msgstr "Rejoindre une équipe"
#: apps/participation/templates/participation/team_detail.html:6
+#: apps/registration/templates/registration/user_detail.html:6
msgid "any"
msgstr "aucun"
@@ -270,6 +272,9 @@ msgstr "Autoriser Animath à publier notre vidéo :"
#: apps/participation/templates/participation/team_detail.html:37
#: apps/participation/templates/participation/update_team.html:12
#: apps/participation/templates/participation/update_team_modal.html:15
+#: apps/registration/templates/registration/update_user.html:12
+#: apps/registration/templates/registration/update_user_modal.html:15
+#: apps/registration/templates/registration/user_detail.html:42
msgid "Update"
msgstr "Modifier"
@@ -285,11 +290,11 @@ msgstr "Vous ne participez pas, vous ne pouvez pas créer d'équipe."
msgid "You are already in a team."
msgstr "Vous êtes déjà dans une équipe."
-#: apps/participation/views.py:71
+#: apps/participation/views.py:72
msgid "You are not in a team."
msgstr "Vous n'êtes pas dans une équipe."
-#: apps/participation/views.py:72
+#: apps/participation/views.py:73
msgid "You don't participate, so you don't have any team."
msgstr "Vous ne participez pas, vous n'avez donc pas d'équipe."
@@ -301,88 +306,88 @@ msgstr "rôle"
msgid "participant"
msgstr "participant"
-#: apps/registration/forms.py:14 apps/registration/models.py:115
+#: apps/registration/forms.py:14 apps/registration/models.py:123
msgid "coach"
msgstr "encadrant"
-#: apps/registration/models.py:21
+#: apps/registration/models.py:20
msgid "Grant Animath to contact me in the future about other actions"
msgstr ""
"Autoriser Animath à me recontacter à l'avenir à propos d'autres actions"
-#: apps/registration/models.py:26
+#: apps/registration/models.py:25
msgid "email confirmed"
msgstr "email confirmé"
-#: apps/registration/models.py:30
+#: apps/registration/models.py:29
msgid "Activate your Correspondances account"
msgstr "Activez votre compte des Correspondances"
-#: apps/registration/models.py:62
+#: apps/registration/models.py:65
msgid "registration"
msgstr "inscription"
-#: apps/registration/models.py:63
+#: apps/registration/models.py:66
msgid "registrations"
msgstr "inscriptions"
-#: apps/registration/models.py:78
+#: apps/registration/models.py:81
msgid "12th grade"
msgstr "Terminale"
-#: apps/registration/models.py:79
+#: apps/registration/models.py:82
msgid "11th grade"
msgstr "Première"
-#: apps/registration/models.py:80
+#: apps/registration/models.py:83
msgid "10th grade or lower"
msgstr "Seconde ou inférieur"
-#: apps/registration/models.py:82
+#: apps/registration/models.py:85
msgid "student class"
msgstr "classe"
-#: apps/registration/models.py:87
+#: apps/registration/models.py:90
msgid "school"
msgstr "école"
-#: apps/registration/models.py:92
+#: apps/registration/models.py:95
msgid "student"
msgstr "étudiant"
-#: apps/registration/models.py:95
+#: apps/registration/models.py:103
msgid "student registration"
msgstr "inscription d'élève"
-#: apps/registration/models.py:96
+#: apps/registration/models.py:104
msgid "student registrations"
msgstr "inscriptions d'élève"
-#: apps/registration/models.py:110
+#: apps/registration/models.py:118
msgid "professional activity"
msgstr "activité professionnelle"
-#: apps/registration/models.py:118
+#: apps/registration/models.py:131
msgid "coach registration"
msgstr "inscription d'encadrant"
-#: apps/registration/models.py:119
+#: apps/registration/models.py:132
msgid "coach registrations"
msgstr "inscriptions d'encadrants"
-#: apps/registration/models.py:124
+#: apps/registration/models.py:137
msgid "role of the administrator"
msgstr "rôle de l'administrateur"
-#: apps/registration/models.py:129
+#: apps/registration/models.py:142
msgid "admin"
msgstr "admin"
-#: apps/registration/models.py:132
+#: apps/registration/models.py:150
msgid "admin registration"
msgstr "inscription d'administrateur"
-#: apps/registration/models.py:133
+#: apps/registration/models.py:151
msgid "admin registrations"
msgstr "inscriptions d'administrateur"
@@ -538,6 +543,42 @@ msgstr "Réinitialiser mon mot de passe"
msgid "Sign up"
msgstr "Inscription"
+#: apps/registration/templates/registration/update_user_modal.html:8
+msgid "Update user"
+msgstr "Modifier l'utilisateur"
+
+#: apps/registration/templates/registration/user_detail.html:14
+msgid "Last name:"
+msgstr "Nom de famille :"
+
+#: apps/registration/templates/registration/user_detail.html:17
+msgid "First name:"
+msgstr "Prénom :"
+
+#: apps/registration/templates/registration/user_detail.html:20
+msgid "Email:"
+msgstr "Adresse e-mail :"
+
+#: apps/registration/templates/registration/user_detail.html:24
+msgid "Student class:"
+msgstr "Classe :"
+
+#: apps/registration/templates/registration/user_detail.html:27
+msgid "School:"
+msgstr "École :"
+
+#: apps/registration/templates/registration/user_detail.html:30
+msgid "Profesional activity:"
+msgstr "Activité professionnelle :"
+
+#: apps/registration/templates/registration/user_detail.html:33
+msgid "Role:"
+msgstr "Rôle :"
+
+#: apps/registration/templates/registration/user_detail.html:37
+msgid "Grant Animath to contact me in the future about other actions:"
+msgstr "Autorise Animath à recontacter à propos d'autres actions :"
+
#: apps/registration/views.py:55
msgid "Email validation"
msgstr "Validation de l'adresse mail"
@@ -558,11 +599,11 @@ msgstr "Mail de confirmation de l'adresse mail envoyé"
msgid "Resend email validation link"
msgstr "Renvoyé le lien de validation de l'adresse mail"
-#: corres2math/settings.py:149
+#: corres2math/settings.py:150
msgid "English"
msgstr "Anglais"
-#: corres2math/settings.py:150
+#: corres2math/settings.py:151
msgid "French"
msgstr "Français"
@@ -644,10 +685,14 @@ msgid "Register"
msgstr "S'inscrire"
#: templates/base.html:120
+msgid "My account"
+msgstr "Mon compte"
+
+#: templates/base.html:123
msgid "Log out"
msgstr "Déconnexion"
-#: templates/base.html:147
+#: templates/base.html:150
msgid "Contact us"
msgstr "Nous contacter"
diff --git a/templates/base.html b/templates/base.html
index 5bac730..2289469 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -116,6 +116,9 @@