diff --git a/apps/participation/templates/participation/team_detail.html b/apps/participation/templates/participation/team_detail.html
index 5ac6626..f6fcf21 100644
--- a/apps/participation/templates/participation/team_detail.html
+++ b/apps/participation/templates/participation/team_detail.html
@@ -62,6 +62,9 @@
@@ -120,6 +123,11 @@
{% trans "Update" as modal_button %}
{% url "participation:update_team" pk=team.pk as modal_action %}
{% include "base_modal.html" with modal_id="updateTeam" %}
+
+ {% trans "Leave team" as modal_title %}
+ {% trans "Leave" as modal_button %}
+ {% url "participation:team_leave" as modal_action %}
+ {% include "base_modal.html" with modal_id="leaveTeam" modal_button_type="danger" %}
{% endblock %}
{% block extrajavascript %}
@@ -130,6 +138,11 @@
if (!modalBody.html().trim())
modalBody.load("{% url "participation:update_team" pk=team.pk %} #form-content");
});
+ $('button[data-target="#leaveTeamModal"]').click(function() {
+ let modalBody = $("#leaveTeamModal div.modal-body");
+ if (!modalBody.html().trim())
+ modalBody.load("{% url "participation:team_leave" %} #form-content");
+ });
});
{% endblock %}
diff --git a/apps/participation/templates/participation/team_leave.html b/apps/participation/templates/participation/team_leave.html
new file mode 100644
index 0000000..72d3db9
--- /dev/null
+++ b/apps/participation/templates/participation/team_leave.html
@@ -0,0 +1,13 @@
+{% extends "base.html" %}
+
+{% load i18n %}
+
+{% block content %}
+
+{% endblock %}
diff --git a/apps/participation/urls.py b/apps/participation/urls.py
index 54efc74..10b7676 100644
--- a/apps/participation/urls.py
+++ b/apps/participation/urls.py
@@ -1,7 +1,8 @@
from django.urls import path
from .views import CalendarView, CreateTeamView, JoinTeamView, MyParticipationDetailView, MyTeamDetailView, \
- ParticipationDetailView, PhaseUpdateView, TeamAuthorizationsView, TeamDetailView, TeamUpdateView, UploadVideoView
+ ParticipationDetailView, PhaseUpdateView, TeamAuthorizationsView, TeamDetailView, TeamLeaveView, TeamUpdateView, \
+ UploadVideoView
app_name = "participation"
@@ -13,6 +14,7 @@ urlpatterns = [
path("team//", TeamDetailView.as_view(), name="team_detail"),
path("team//update/", TeamUpdateView.as_view(), name="update_team"),
path("team//authorizations/", TeamAuthorizationsView.as_view(), name="team_authorizations"),
+ path("team/leave/", TeamLeaveView.as_view(), name="team_leave"),
path("detail/", MyParticipationDetailView.as_view(), name="my_participation_detail"),
path("detail//", ParticipationDetailView.as_view(), name="participation_detail"),
path("detail/upload-video//", UploadVideoView.as_view(), name="upload_video"),
diff --git a/apps/participation/views.py b/apps/participation/views.py
index 0ca63e4..fa6d6bf 100644
--- a/apps/participation/views.py
+++ b/apps/participation/views.py
@@ -2,6 +2,9 @@ from io import BytesIO
import os
from zipfile import ZipFile
+from django.shortcuts import redirect
+from django.views.generic.base import View, TemplateView
+
from corres2math.lists import get_sympa_client
from corres2math.views import AdminMixin
from django.contrib.auth.mixins import LoginRequiredMixin
@@ -236,6 +239,29 @@ class TeamAuthorizationsView(LoginRequiredMixin, DetailView):
return response
+class TeamLeaveView(LoginRequiredMixin, TemplateView):
+ template_name = "participation/team_leave.html"
+
+ def dispatch(self, request, *args, **kwargs):
+ if not request.user.is_authenticated:
+ return self.handle_no_permission()
+ if not request.user.registration.team:
+ raise PermissionDenied(_("You are not in a team."))
+ if request.user.registration.team.participation.valid is not None:
+ raise PermissionDenied(_("The team is already validated or the validation is pending."))
+ return super().dispatch(request, *args, **kwargs)
+
+ @transaction.atomic()
+ def post(self, request, *args, **kwargs):
+ team = request.user.registration.team
+ request.user.registration.team = None
+ request.user.registration.save()
+ get_sympa_client().unsubscribe(request.user.email, f"equipe-{team.trigram.lower()}", False)
+ if team.students.count() + team.coachs.count() == 0:
+ team.delete()
+ return redirect(reverse_lazy("index"))
+
+
class MyParticipationDetailView(LoginRequiredMixin, RedirectView):
def get_redirect_url(self, *args, **kwargs):
user = self.request.user
diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po
index 1d24bce..76a01c1 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-10-20 15:21+0200\n"
+"POT-Creation-Date: 2020-10-20 15:53+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: Yohann D'ANELLO \n"
"Language-Team: LANGUAGE \n"
@@ -99,7 +99,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:15 apps/participation/models.py:27
+#: apps/participation/forms.py:15 apps/participation/models.py:28
msgid "The trigram must be composed of three uppercase letters."
msgstr "Le trigramme doit être composé de trois lettres majuscules."
@@ -131,28 +131,28 @@ msgstr "Cette phase doit commencer après les phases précédentes."
msgid "This phase must end after the next phases."
msgstr "Cette phase doit finir avant les phases suivantes."
-#: apps/participation/models.py:20 apps/participation/tables.py:30
+#: apps/participation/models.py:21 apps/participation/tables.py:30
#: apps/participation/tables.py:52 apps/participation/tables.py:78
msgid "name"
msgstr "nom"
-#: apps/participation/models.py:26 apps/participation/tables.py:57
+#: apps/participation/models.py:27 apps/participation/tables.py:57
msgid "trigram"
msgstr "trigramme"
-#: apps/participation/models.py:34
+#: apps/participation/models.py:35
msgid "access code"
msgstr "code d'accès"
-#: apps/participation/models.py:35
+#: apps/participation/models.py:36
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:39
+#: apps/participation/models.py:40
msgid "Grant Animath to publish my video"
msgstr "Autoriser Animath à publier ma vidéo"
-#: apps/participation/models.py:40
+#: apps/participation/models.py:41
msgid ""
"Give the authorisation to publish the video on the main website to promote "
"the action."
@@ -160,101 +160,101 @@ msgstr ""
"Donner l'autorisation de publier la vidéo sur le site principal pour "
"promouvoir les Correspondances."
-#: apps/participation/models.py:67
+#: apps/participation/models.py:72
#, python-brace-format
msgid "Team {name} ({trigram})"
msgstr "Équipe {name} ({trigram})"
-#: apps/participation/models.py:70 apps/participation/models.py:81
+#: apps/participation/models.py:75 apps/participation/models.py:86
#: apps/registration/models.py:89 apps/registration/models.py:134
msgid "team"
msgstr "équipe"
-#: apps/participation/models.py:71
+#: apps/participation/models.py:76
msgid "teams"
msgstr "équipes"
-#: apps/participation/models.py:85
+#: apps/participation/models.py:90
#, python-brace-format
msgid "Problem #{problem:d}"
msgstr "Problème n°{problem:d}"
-#: apps/participation/models.py:88 apps/participation/tables.py:35
+#: apps/participation/models.py:93 apps/participation/tables.py:35
#: apps/participation/tables.py:62
msgid "problem number"
msgstr "numéro de problème"
-#: apps/participation/models.py:94 apps/participation/models.py:145
+#: apps/participation/models.py:99 apps/participation/models.py:150
msgid "valid"
msgstr "valide"
-#: apps/participation/models.py:95 apps/participation/models.py:146
+#: apps/participation/models.py:100 apps/participation/models.py:151
msgid "The video got the validation of the administrators."
msgstr "La vidéo a été validée par les administrateurs."
-#: apps/participation/models.py:104
+#: apps/participation/models.py:109
msgid "solution video"
msgstr "vidéo de solution"
-#: apps/participation/models.py:113
+#: apps/participation/models.py:118
msgid "received participation"
msgstr "participation reçue"
-#: apps/participation/models.py:122
+#: apps/participation/models.py:127
msgid "synthesis video"
msgstr "vidéo de synthèse"
-#: apps/participation/models.py:129
+#: apps/participation/models.py:134
#, python-brace-format
msgid "Participation of the team {name} ({trigram})"
msgstr "Participation de l'équipe {name} ({trigram})"
-#: apps/participation/models.py:132
+#: apps/participation/models.py:137
msgid "participation"
msgstr "participation"
-#: apps/participation/models.py:133
+#: apps/participation/models.py:138
msgid "participations"
msgstr "participations"
-#: apps/participation/models.py:138
+#: apps/participation/models.py:143
msgid "link"
msgstr "lien"
-#: apps/participation/models.py:139
+#: apps/participation/models.py:144
msgid "The full video link."
msgstr "Le lien complet de la vidéo."
-#: apps/participation/models.py:173
+#: apps/participation/models.py:178
#, python-brace-format
msgid "Video of team {name} ({trigram})"
msgstr "Vidéo de l'équipe {name} ({trigram})"
-#: apps/participation/models.py:177
+#: apps/participation/models.py:182
msgid "video"
msgstr "vidéo"
-#: apps/participation/models.py:178
+#: apps/participation/models.py:183
msgid "videos"
msgstr "vidéos"
-#: apps/participation/models.py:185
+#: apps/participation/models.py:190
msgid "phase number"
msgstr "phase"
-#: apps/participation/models.py:190
+#: apps/participation/models.py:195
msgid "phase description"
msgstr "description"
-#: apps/participation/models.py:194
+#: apps/participation/models.py:199
msgid "start date of the given phase"
msgstr "début de la phase"
-#: apps/participation/models.py:199
+#: apps/participation/models.py:204
msgid "end date of the given phase"
msgstr "fin de la phase"
-#: apps/participation/models.py:214
+#: apps/participation/models.py:219
msgid ""
"Phase {phase_number:d} starts on {start:%Y-%m-%d %H:%M} and ends on {end:%Y-"
"%m-%d %H:%M}"
@@ -262,11 +262,11 @@ msgstr ""
"Phase {phase_number:d} démarrant le {start:%d/%m/%Y %H:%M} et finissant le "
"{end:%d/%m/%Y %H:%M}"
-#: apps/participation/models.py:218
+#: apps/participation/models.py:223
msgid "phase"
msgstr "phase"
-#: apps/participation/models.py:219
+#: apps/participation/models.py:224
msgid "phases"
msgstr "phases"
@@ -281,9 +281,9 @@ msgid "Join"
msgstr "Rejoindre"
#: apps/participation/templates/participation/participation_detail.html:6
-#: apps/participation/templates/participation/team_detail.html:28
-#: apps/participation/templates/participation/team_detail.html:37
-#: apps/participation/templates/participation/team_detail.html:42
+#: apps/participation/templates/participation/team_detail.html:31
+#: apps/participation/templates/participation/team_detail.html:40
+#: apps/participation/templates/participation/team_detail.html:45
#: apps/registration/templates/registration/user_detail.html:6
#: apps/registration/templates/registration/user_detail.html:26
msgid "any"
@@ -299,7 +299,7 @@ msgid "Team:"
msgstr "Équipe :"
#: apps/participation/templates/participation/participation_detail.html:16
-#: apps/participation/templates/participation/team_detail.html:41
+#: apps/participation/templates/participation/team_detail.html:44
msgid "Chosen problem:"
msgstr "Problème choisi :"
@@ -337,8 +337,8 @@ msgstr "La plateforme de cette vidéo n'est pas encore supportée."
#: apps/participation/templates/participation/phase_form.html:11
#: apps/participation/templates/participation/phase_list.html:18
-#: apps/participation/templates/participation/team_detail.html:61
-#: apps/participation/templates/participation/team_detail.html:117
+#: apps/participation/templates/participation/team_detail.html:64
+#: apps/participation/templates/participation/team_detail.html:123
#: apps/participation/templates/participation/update_team.html:12
#: apps/registration/templates/registration/update_user.html:12
#: apps/registration/templates/registration/user_detail.html:64
@@ -364,34 +364,45 @@ msgid "Trigram:"
msgstr "Trigramme :"
#: apps/participation/templates/participation/team_detail.html:20
+#: apps/registration/templates/registration/user_detail.html:20
+msgid "Email:"
+msgstr "Adresse e-mail :"
+
+#: apps/participation/templates/participation/team_detail.html:23
msgid "Access code:"
msgstr "Code d'accès :"
-#: apps/participation/templates/participation/team_detail.html:23
+#: apps/participation/templates/participation/team_detail.html:26
msgid "Coachs:"
msgstr "Encadrants :"
-#: apps/participation/templates/participation/team_detail.html:32
+#: apps/participation/templates/participation/team_detail.html:35
msgid "Participants:"
msgstr "Participants :"
-#: apps/participation/templates/participation/team_detail.html:45
+#: apps/participation/templates/participation/team_detail.html:48
msgid "Grant Animath to publish our video:"
msgstr "Autoriser Animath à publier notre vidéo :"
-#: apps/participation/templates/participation/team_detail.html:48
+#: apps/participation/templates/participation/team_detail.html:51
msgid "Authorizations:"
msgstr "Autorisations :"
-#: apps/participation/templates/participation/team_detail.html:54
+#: apps/participation/templates/participation/team_detail.html:57
msgid "Not uploaded yet"
msgstr "Pas encore envoyée"
-#: apps/participation/templates/participation/team_detail.html:70
+#: apps/participation/templates/participation/team_detail.html:66
+#: apps/participation/templates/participation/team_detail.html:128
+#: apps/participation/templates/participation/team_leave.html:9
+msgid "Leave"
+msgstr "Quitter"
+
+#: apps/participation/templates/participation/team_detail.html:76
msgid "Access to team participation"
msgstr "Accéder à la participation de l'équipe"
-#: apps/participation/templates/participation/team_detail.html:77
+#: apps/participation/templates/participation/team_detail.html:83
msgid ""
"Your team has at least 3 members and all photo authorizations were given: "
"the team can be validated."
@@ -399,11 +410,11 @@ msgstr ""
"Votre équipe contient au moins 3 personnes et toutes les autorisations de "
"droit à l'image ont été données : l'équipe peut être validée."
-#: apps/participation/templates/participation/team_detail.html:82
+#: apps/participation/templates/participation/team_detail.html:88
msgid "Submit my team to validation"
msgstr "Soumettre mon équipe à validation"
-#: apps/participation/templates/participation/team_detail.html:88
+#: apps/participation/templates/participation/team_detail.html:94
msgid ""
"Your team must be composed of 3 members and each member must upload its "
"photo authorization and confirm its email address."
@@ -411,15 +422,15 @@ msgstr ""
"Votre équipe doit être composée de 3 membres et chaque membre doit envoyer "
"son autorisation de droit à l'image et confirmé son adresse e-mail."
-#: apps/participation/templates/participation/team_detail.html:93
+#: apps/participation/templates/participation/team_detail.html:99
msgid "This team didn't ask for validation yet."
msgstr "L'équipe n'a pas encore demandé à être validée."
-#: apps/participation/templates/participation/team_detail.html:99
+#: apps/participation/templates/participation/team_detail.html:105
msgid "Your validation is pending."
msgstr "Votre validation est en attente."
-#: apps/participation/templates/participation/team_detail.html:103
+#: apps/participation/templates/participation/team_detail.html:109
msgid ""
"The team requested to be validated. You may now control the authorizations "
"and confirm that they can participate."
@@ -427,85 +438,92 @@ msgstr ""
"L'équipe a demandé à être validée. Vous pouvez désormais contrôler les "
"différentes autorisations et confirmer qu'elle peut participer."
-#: apps/participation/templates/participation/team_detail.html:109
+#: apps/participation/templates/participation/team_detail.html:115
msgid "Validate"
msgstr "Valider"
-#: apps/participation/templates/participation/team_detail.html:110
+#: apps/participation/templates/participation/team_detail.html:116
msgid "Invalidate"
msgstr "Invalider"
-#: apps/participation/templates/participation/team_detail.html:116
+#: apps/participation/templates/participation/team_detail.html:122
msgid "Update team"
msgstr "Modifier l'équipe"
-#: apps/participation/views.py:30 templates/base.html:77
+#: apps/participation/templates/participation/team_detail.html:127
+msgid "Leave team"
+msgstr "Quitter l'équipe"
+
+#: apps/participation/templates/participation/team_leave.html:7
+msgid "Are you sure that you want to leave this team?"
+msgstr "Êtes-vous sûr·e de vouloir quitter cette équipe ?"
+
+#: apps/participation/views.py:32 templates/base.html:77
#: templates/base.html:228
msgid "Create team"
msgstr "Créer une équipe"
-#: apps/participation/views.py:37 apps/participation/views.py:67
+#: apps/participation/views.py:39 apps/participation/views.py:69
msgid "You don't participate, so you can't create a team."
msgstr "Vous ne participez pas, vous ne pouvez pas créer d'équipe."
-#: apps/participation/views.py:39 apps/participation/views.py:69
+#: apps/participation/views.py:41 apps/participation/views.py:71
msgid "You are already in a team."
msgstr "Vous êtes déjà dans une équipe."
-#: apps/participation/views.py:60 templates/base.html:82
+#: apps/participation/views.py:62 templates/base.html:82
#: templates/base.html:224
msgid "Join team"
msgstr "Rejoindre une équipe"
-#: apps/participation/views.py:95 apps/participation/views.py:249
+#: apps/participation/views.py:97 apps/participation/views.py:248
+#: apps/participation/views.py:259
msgid "You are not in a team."
msgstr "Vous n'êtes pas dans une équipe."
-#: apps/participation/views.py:96 apps/participation/views.py:250
+#: apps/participation/views.py:98 apps/participation/views.py:260
msgid "You don't participate, so you don't have any team."
msgstr "Vous ne participez pas, vous n'avez donc pas d'équipe."
-#: apps/participation/views.py:135
+#: apps/participation/views.py:137
msgid "You don't participate, so you can't request the validation of the team."
msgstr ""
"Vous ne participez pas, vous ne pouvez pas demander la validation de "
"l'équipe."
-#: apps/participation/views.py:138
+#: apps/participation/views.py:140
msgid "The validation of the team is already done or pending."
msgstr "La validation de l'équipe est déjà faite ou en cours."
-#: apps/participation/views.py:151
+#: apps/participation/views.py:153
msgid "You are not an administrator."
msgstr "Vous n'êtes pas administrateur."
-#: apps/participation/views.py:154
+#: apps/participation/views.py:156
msgid "This team has no pending validation."
msgstr "L'équipe n'a pas de validation en attente."
-#: apps/participation/views.py:176
+#: apps/participation/views.py:175
msgid "You must specify if you validate the registration or not."
msgstr "Vous devez spécifier si vous validez l'inscription ou non."
-#: apps/participation/views.py:233 apps/registration/views.py:213
+#: apps/participation/views.py:232 apps/registration/views.py:213
#, python-brace-format
msgid "Photo authorization of {student}.{ext}"
msgstr "Autorisation de droit à l'image de {student}.{ext}"
-#: apps/participation/views.py:237
+#: apps/participation/views.py:236
#, python-brace-format
msgid "Photo authorizations of team {trigram}.zip"
msgstr "Autorisations de droit à l'image de l'équipe {trigram}.zip"
-#: apps/participation/views.py:259
+#: apps/participation/views.py:269
msgid "The team is not validated yet."
msgstr "L'équipe n'est pas encore validée."
-#: apps/participation/views.py:268
-#, fuzzy, python-brace-format
-#| msgid "Participation of the team {name} ({trigram})"
+#: apps/participation/views.py:278
msgid "Participation of team {trigram}"
-msgstr "Participation de l'équipe {name} ({trigram})"
+msgstr "Participation de l'équipe {trigram}"
#: apps/registration/forms.py:13
msgid "role"
@@ -779,10 +797,6 @@ msgstr "Nom de famille :"
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:22
msgid "Not confirmed"
msgstr "Non confirmée"