mirror of
https://gitlab.com/animath/si/plateforme-corres2math.git
synced 2025-07-01 12:01:15 +02:00
Compare commits
32 Commits
55a727612c
...
master
Author | SHA1 | Date | |
---|---|---|---|
4c9e63be3d | |||
35a197be07
|
|||
01f6d74cae | |||
d55671efd1
|
|||
bcdbc445c6
|
|||
95a69f35d2
|
|||
eec941c816
|
|||
64d471936f
|
|||
02c977264d
|
|||
8f86ea15c8
|
|||
7628387158
|
|||
a82c6b4673 | |||
bf2feb9c35
|
|||
4ee5ac309d | |||
969e53b712
|
|||
84e149e8c9
|
|||
9e6a3eb1ca
|
|||
1f9f60d880
|
|||
205760f2e9
|
|||
8f742b8e14
|
|||
996d00c7f0
|
|||
8f09ca5553
|
|||
b761670133
|
|||
f2ca4b7446 | |||
3faf1294d8 | |||
24e1cf3845 | |||
dbcf15c4f3 | |||
10115a0419 | |||
833f9147ce | |||
91640f8fb1 | |||
ce048a30d6 | |||
d104c2ff1f |
@ -1,4 +1,4 @@
|
||||
FROM python:3-alpine
|
||||
FROM python:3.8-alpine
|
||||
|
||||
ENV PYTHONUNBUFFERED 1
|
||||
ENV DJANGO_ALLOW_ASYNC_UNSAFE 1
|
||||
|
4
LICENSE
4
LICENSE
@ -632,7 +632,7 @@ state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
Copyright (C) 2020 Animath
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail.
|
||||
If the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
<program> Copyright (C) 2020 Animath
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
@ -1 +1,4 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
default_app_config = 'api.apps.APIConfig'
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django.apps import AppConfig
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from rest_framework import serializers
|
||||
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from unittest.case import skipIf
|
||||
|
||||
from django.conf import settings
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django.conf import settings
|
||||
from django.conf.urls import include, url
|
||||
from rest_framework import routers
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from rest_framework.filters import SearchFilter
|
||||
|
@ -1 +1,4 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
default_app_config = 'eastereggs.apps.EastereggsConfig'
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
|
@ -0,0 +1,2 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django.urls import path
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
|
@ -0,0 +1,2 @@
|
||||
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
@ -0,0 +1,2 @@
|
||||
# Copyright (C) 2020 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
@ -1 +1,4 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
default_app_config = 'participation.apps.ParticipationConfig'
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django.contrib import admin
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django.apps import AppConfig
|
||||
from django.db.models.signals import post_save, pre_delete, pre_save
|
||||
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import re
|
||||
|
||||
from bootstrap_datepicker_plus import DateTimePickerInput
|
||||
|
@ -0,0 +1,2 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import os
|
||||
|
||||
from asgiref.sync import async_to_sync
|
||||
@ -85,5 +88,7 @@ class Command(BaseCommand):
|
||||
f"@{admin.matrix_username}:correspondances-maths.fr", 95)
|
||||
Matrix.set_room_power_level("#faq:correspondances-maths.fr",
|
||||
f"@{admin.matrix_username}:correspondances-maths.fr", 95)
|
||||
Matrix.set_room_power_level("#je-cherche-une-equipe:correspondances-maths.fr",
|
||||
f"@{admin.matrix_username}:correspondances-maths.fr", 95)
|
||||
Matrix.set_room_power_level("#flood:correspondances-maths.fr",
|
||||
f"@{admin.matrix_username}:correspondances-maths.fr", 95)
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from corres2math.lists import get_sympa_client
|
||||
from django.core.management import BaseCommand
|
||||
from django.db.models import Q
|
||||
@ -26,13 +29,15 @@ class Command(BaseCommand):
|
||||
f" des Correspondances.", "education", raise_error=False)
|
||||
|
||||
for team in Team.objects.filter(participation__valid=True).all():
|
||||
sympa.subscribe(team.email, "equipes", f"Equipe {team.name}", True, True)
|
||||
team.create_mailing_list()
|
||||
sympa.subscribe(team.email, "equipes", f"Equipe {team.name}", True)
|
||||
sympa.subscribe(team.email, f"probleme-{team.participation.problem}", f"Equipe {team.name}", True)
|
||||
|
||||
for team in Team.objects.filter(Q(participation__valid=False) | Q(participation__valid__isnull=True)).all():
|
||||
team.create_mailing_list()
|
||||
sympa.subscribe(team.email, "equipes-non-valides", f"Equipe {team.name}", True)
|
||||
|
||||
for student in StudentRegistration.objects.filter(team__isnull=False).all():
|
||||
sympa.subscribe(student.user.email, f"equipe-{student.team.trigram.lower}", True, f"{student}")
|
||||
sympa.subscribe(student.user.email, f"equipe-{student.team.trigram.lower()}", True, f"{student}")
|
||||
for coach in CoachRegistration.objects.filter(team__isnull=False).all():
|
||||
sympa.subscribe(coach.user.email, f"equipe-{coach.team.trigram.lower}", True, f"{coach}")
|
||||
sympa.subscribe(coach.user.email, f"equipe-{coach.team.trigram.lower()}", True, f"{coach}")
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from corres2math.matrix import Matrix, RoomVisibility
|
||||
from django.core.management import BaseCommand
|
||||
from participation.models import Participation
|
||||
|
@ -0,0 +1,2 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
@ -285,7 +288,7 @@ class Phase(models.Model):
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def current_phase(cls):
|
||||
def current_phase(cls) -> "Phase":
|
||||
"""
|
||||
Retrieve the current phase of this day
|
||||
"""
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from haystack import indexes
|
||||
|
||||
from .models import Participation, Team, Video
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from corres2math.lists import get_sympa_client
|
||||
from participation.models import Participation, Team, Video
|
||||
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
import django_tables2 as tables
|
||||
@ -39,6 +42,9 @@ class TeamTable(tables.Table):
|
||||
attrs = {
|
||||
'class': 'table table condensed table-striped',
|
||||
}
|
||||
row_attrs = {
|
||||
'class': lambda record: '' if record.participation.solution.link else 'bg-warning',
|
||||
}
|
||||
model = Team
|
||||
fields = ('name', 'trigram', 'problem',)
|
||||
template_name = 'django_tables2/bootstrap4.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 :
|
||||
<a href="{% url "participation:team_detail" pk=team.pk %}">{% url "participation:team_detail" pk=team.pk %}</a>
|
||||
<a href="https://{{ domain }}{% url "participation:team_detail" pk=team.pk %}">
|
||||
https://{{ domain }}{% url "participation:team_detail" pk=team.pk %}
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
@ -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,
|
||||
|
||||
|
@ -194,13 +194,14 @@
|
||||
{% trans "This video platform is not supported yet." as unsupported_platform %}
|
||||
{% include "base_modal.html" with modal_id="displayOtherSolution" modal_action="" modal_button="" modal_additional_class="modal-lg" modal_content=participation.received_participation.solution.as_iframe|default:unsupported_platform %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if user.registration.participates and current_phase.phase_number == 2 %}
|
||||
{% if current_phase.phase_number == 2 %}
|
||||
{% trans "Add question" as modal_title %}
|
||||
{% trans "Add" as modal_button %}
|
||||
{% url "participation:add_question" pk=participation.pk as modal_action %}
|
||||
{% include "base_modal.html" with modal_id="addQuestion" modal_button_type="success" %}
|
||||
{% endif %}
|
||||
|
||||
{% for question in participation.questions.all %}
|
||||
{% with number_str=forloop.counter|stringformat:"d"%}
|
||||
{% with modal_id="updateQuestion"|add:number_str %}
|
||||
@ -260,12 +261,13 @@
|
||||
});
|
||||
{% endif %}
|
||||
|
||||
{% if user.registration.participates and current_phase.phase_number == 2 %}
|
||||
{% if current_phase.phase_number == 2 %}
|
||||
$('button[data-target="#addQuestionModal"]').click(function() {
|
||||
let modalBody = $("#addQuestionModal div.modal-body");
|
||||
if (!modalBody.html().trim())
|
||||
modalBody.load("{% url "participation:add_question" pk=participation.pk %} #form-content");
|
||||
});
|
||||
{% endif %}
|
||||
|
||||
{% for question in participation.questions.all %}
|
||||
$('button[data-target="#updateQuestion{{ forloop.counter }}Modal"]').click(function() {
|
||||
@ -274,13 +276,14 @@
|
||||
modalBody.load("{% url "participation:update_question" pk=question.pk %} #form-content");
|
||||
});
|
||||
|
||||
{% if current_phase.phase_number == 2 %}
|
||||
$('button[data-target="#deleteQuestion{{ forloop.counter }}Modal"]').click(function() {
|
||||
let modalBody = $("#deleteQuestion{{ forloop.counter }}Modal div.modal-body");
|
||||
if (!modalBody.html().trim())
|
||||
modalBody.load("{% url "participation:delete_question" pk=question.pk %} #form-content");
|
||||
});
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
$('button[data-target="#uploadSolutionModal"]').click(function() {
|
||||
let modalBody = $("#uploadSolutionModal div.modal-body");
|
||||
|
@ -0,0 +1,2 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django import template
|
||||
|
||||
from ..models import Phase
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from datetime import timedelta
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
@ -409,12 +412,6 @@ class TestStudentParticipation(TestCase):
|
||||
self.user.registration.team = self.team
|
||||
self.user.registration.save()
|
||||
|
||||
# Team is pending validation
|
||||
self.team.participation.valid = False
|
||||
self.team.participation.save()
|
||||
response = self.client.post(reverse("participation:team_leave"))
|
||||
self.assertEqual(response.status_code, 403)
|
||||
|
||||
# Team is valid
|
||||
self.team.participation.valid = True
|
||||
self.team.participation.save()
|
||||
@ -669,7 +666,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()
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django.urls import path
|
||||
from django.views.generic import TemplateView
|
||||
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from io import BytesIO
|
||||
from zipfile import ZipFile
|
||||
|
||||
@ -5,6 +8,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 +42,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 +90,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 +216,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 +272,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 +308,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
|
||||
@ -332,7 +344,7 @@ class TeamLeaveView(LoginRequiredMixin, TemplateView):
|
||||
return self.handle_no_permission()
|
||||
if not request.user.registration.participates or not request.user.registration.team:
|
||||
raise PermissionDenied(_("You are not in a team."))
|
||||
if request.user.registration.team.participation.valid is not None:
|
||||
if request.user.registration.team.participation.valid:
|
||||
raise PermissionDenied(_("The team is already validated or the validation is pending."))
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
@ -376,6 +388,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 \
|
||||
@ -463,6 +477,12 @@ class UpdateQuestionView(LoginRequiredMixin, UpdateView):
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
raise PermissionDenied
|
||||
|
||||
def form_valid(self, form):
|
||||
if not self.request.user.registration.is_admin and Phase.current_phase().phase_number != 2:
|
||||
form.add_error(None, _("You can update your questions now."))
|
||||
return self.form_invalid(form)
|
||||
return super().form_valid(form)
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse_lazy("participation:participation_detail", args=(self.object.participation.pk,))
|
||||
|
||||
@ -485,6 +505,11 @@ class DeleteQuestionView(LoginRequiredMixin, DeleteView):
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
raise PermissionDenied
|
||||
|
||||
def delete(self, request, *args, **kwargs):
|
||||
if not request.user.registration.is_admin and Phase.current_phase().phase_number != 2:
|
||||
raise PermissionDenied(_("You can update your questions now."))
|
||||
return super().delete(request, *args, **kwargs)
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse_lazy("participation:participation_detail", args=(self.object.participation.pk,))
|
||||
|
||||
@ -500,6 +525,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)
|
||||
|
@ -1 +1,4 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
default_app_config = 'registration.apps.RegistrationConfig'
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django.contrib import admin
|
||||
from polymorphic.admin import PolymorphicChildModelAdmin, PolymorphicParentModelAdmin
|
||||
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django.apps import AppConfig
|
||||
from django.db.models.signals import post_save, pre_save
|
||||
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from cas_server.auth import DjangoAuthUser # pragma: no cover
|
||||
|
||||
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django import forms
|
||||
from django.contrib.auth.forms import UserCreationForm
|
||||
from django.contrib.auth.models import User
|
||||
@ -64,7 +67,7 @@ class StudentRegistrationForm(forms.ModelForm):
|
||||
"""
|
||||
class Meta:
|
||||
model = StudentRegistration
|
||||
fields = ('student_class', 'school', 'give_contact_to_animath',)
|
||||
fields = ('team', 'student_class', 'school', 'give_contact_to_animath', 'email_confirmed',)
|
||||
|
||||
|
||||
class PhotoAuthorizationForm(forms.ModelForm):
|
||||
@ -72,7 +75,10 @@ class PhotoAuthorizationForm(forms.ModelForm):
|
||||
Form to send a photo authorization.
|
||||
"""
|
||||
def clean_photo_authorization(self):
|
||||
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"]
|
||||
@ -92,7 +98,7 @@ class CoachRegistrationForm(forms.ModelForm):
|
||||
"""
|
||||
class Meta:
|
||||
model = CoachRegistration
|
||||
fields = ('professional_activity', 'give_contact_to_animath',)
|
||||
fields = ('team', 'professional_activity', 'give_contact_to_animath', 'email_confirmed',)
|
||||
|
||||
|
||||
class AdminRegistrationForm(forms.ModelForm):
|
||||
@ -101,4 +107,4 @@ class AdminRegistrationForm(forms.ModelForm):
|
||||
"""
|
||||
class Meta:
|
||||
model = AdminRegistration
|
||||
fields = ('role', 'give_contact_to_animath',)
|
||||
fields = ('role', 'give_contact_to_animath', 'email_confirmed',)
|
||||
|
@ -50,7 +50,7 @@ class Migration(migrations.Migration):
|
||||
('student_class', models.IntegerField(choices=[(12, '12th grade'), (11, '11th grade'), (10, '10th grade or lower')], verbose_name='student class')),
|
||||
('school', models.CharField(max_length=255, verbose_name='school')),
|
||||
('photo_authorization', models.FileField(blank=True, default='', upload_to=registration.models.get_random_filename, verbose_name='photo authorization')),
|
||||
('team', models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='students', to='participation.team', verbose_name='team')),
|
||||
('team', models.ForeignKey(default=None, blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='students', to='participation.team', verbose_name='team')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'student registration',
|
||||
@ -63,7 +63,7 @@ class Migration(migrations.Migration):
|
||||
fields=[
|
||||
('registration_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='registration.registration')),
|
||||
('professional_activity', models.TextField(verbose_name='professional activity')),
|
||||
('team', models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='coachs', to='participation.team', verbose_name='team')),
|
||||
('team', models.ForeignKey(default=None, blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='coachs', to='participation.team', verbose_name='team')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'coach registration',
|
||||
|
@ -0,0 +1,2 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from corres2math.tokens import email_validation_token
|
||||
from django.contrib.sites.models import Site
|
||||
from django.db import models
|
||||
@ -102,6 +105,7 @@ class StudentRegistration(Registration):
|
||||
related_name="students",
|
||||
on_delete=models.PROTECT,
|
||||
null=True,
|
||||
blank=True,
|
||||
default=None,
|
||||
verbose_name=_("team"),
|
||||
)
|
||||
@ -151,6 +155,7 @@ class CoachRegistration(Registration):
|
||||
related_name="coachs",
|
||||
on_delete=models.PROTECT,
|
||||
null=True,
|
||||
blank=True,
|
||||
default=None,
|
||||
verbose_name=_("team"),
|
||||
)
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from haystack import indexes
|
||||
|
||||
from .models import Registration
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from corres2math.lists import get_sympa_client
|
||||
from corres2math.matrix import Matrix
|
||||
from django.contrib.auth.models import User
|
||||
@ -45,9 +48,9 @@ def invite_to_public_rooms(instance: Registration, created: bool, **_):
|
||||
"""
|
||||
When a user got registered, automatically invite the Matrix user into public rooms.
|
||||
"""
|
||||
if not created:
|
||||
if created:
|
||||
Matrix.invite("#annonces:correspondances-maths.fr", f"@{instance.matrix_username}:correspondances-maths.fr")
|
||||
Matrix.invite("#faq:correspondances-maths.fr", f"@{instance.matrix_username}:correspondances-maths.fr")
|
||||
Matrix.invite("#je-cherche-une-equip:correspondances-maths.fr",
|
||||
Matrix.invite("#je-cherche-une-equipe:correspondances-maths.fr",
|
||||
f"@{instance.matrix_username}:correspondances-maths.fr")
|
||||
Matrix.invite("#flood:correspondances-maths.fr", f"@{instance.matrix_username}:correspondances-maths.fr")
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
import django_tables2 as tables
|
||||
|
||||
|
@ -19,7 +19,11 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
</p>
|
||||
{% else %}
|
||||
<p>
|
||||
{% 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 %}
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
7
apps/registration/templates/registration/user_list.html
Normal file
7
apps/registration/templates/registration/user_list.html
Normal file
@ -0,0 +1,7 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% load django_tables2 %}
|
||||
|
||||
{% block content %}
|
||||
{% render_table table %}
|
||||
{% endblock %}
|
@ -0,0 +1,2 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django import template
|
||||
from django_tables2 import Table
|
||||
from participation.models import Participation, Team, Video
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from datetime import timedelta
|
||||
import os
|
||||
|
||||
@ -5,13 +8,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
|
||||
|
||||
@ -31,6 +35,28 @@ class TestIndexPage(TestCase):
|
||||
response = self.client.get(reverse("registration:reset_admin"))
|
||||
self.assertRedirects(response, reverse("login") + "?next=" + reverse("registration:reset_admin"), 302, 200)
|
||||
|
||||
User.objects.create()
|
||||
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:
|
||||
@ -199,6 +225,13 @@ class TestRegistration(TestCase):
|
||||
response = self.client.get(reverse("registration:user_detail", args=(self.user.pk,)))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_user_list(self):
|
||||
"""
|
||||
Display the list of all users.
|
||||
"""
|
||||
response = self.client.get(reverse("registration:user_list"))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_update_user(self):
|
||||
"""
|
||||
Update the user information, for each type of user.
|
||||
@ -222,6 +255,8 @@ class TestRegistration(TestCase):
|
||||
last_name="Name",
|
||||
email="new_" + user.email,
|
||||
give_contact_to_animath=True,
|
||||
email_confirmed=True,
|
||||
team_id="",
|
||||
))
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
@ -230,6 +265,8 @@ class TestRegistration(TestCase):
|
||||
last_name="Name",
|
||||
email="new_" + user.email,
|
||||
give_contact_to_animath=True,
|
||||
email_confirmed=True,
|
||||
team_id="",
|
||||
)
|
||||
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)
|
||||
@ -253,6 +290,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"),
|
||||
|
@ -1,7 +1,10 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django.urls import path
|
||||
|
||||
from .views import MyAccountDetailView, ResetAdminView, SignupView, UserDetailView, UserImpersonateView, \
|
||||
UserResendValidationEmailView, UserUpdateView, UserUploadPhotoAuthorizationView, UserValidateView, \
|
||||
UserListView, UserResendValidationEmailView, UserUpdateView, UserUploadPhotoAuthorizationView, UserValidateView, \
|
||||
UserValidationEmailSentView
|
||||
|
||||
app_name = "registration"
|
||||
@ -18,5 +21,6 @@ urlpatterns = [
|
||||
path("user/<int:pk>/upload-photo-authorization/", UserUploadPhotoAuthorizationView.as_view(),
|
||||
name="upload_user_photo_authorization"),
|
||||
path("user/<int:pk>/impersonate/", UserImpersonateView.as_view(), name="user_impersonate"),
|
||||
path("user/list/", UserListView.as_view(), name="user_list"),
|
||||
path("reset-admin/", ResetAdminView.as_view(), name="reset_admin"),
|
||||
]
|
||||
|
@ -1,6 +1,10 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import os
|
||||
|
||||
from corres2math.tokens import email_validation_token
|
||||
from corres2math.views import AdminMixin
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.auth.models import User
|
||||
@ -12,11 +16,13 @@ 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
|
||||
from .models import Registration, StudentRegistration
|
||||
from .tables import RegistrationTable
|
||||
|
||||
|
||||
class SignupView(CreateView):
|
||||
@ -43,6 +49,11 @@ class SignupView(CreateView):
|
||||
context["student_registration_form"] = StudentRegistrationForm(self.request.POST or None)
|
||||
context["coach_registration_form"] = CoachRegistrationForm(self.request.POST or None)
|
||||
|
||||
del context["student_registration_form"].fields["team"]
|
||||
del context["student_registration_form"].fields["email_confirmed"]
|
||||
del context["coach_registration_form"].fields["team"]
|
||||
del context["coach_registration_form"].fields["email_confirmed"]
|
||||
|
||||
return context
|
||||
|
||||
@transaction.atomic
|
||||
@ -52,6 +63,8 @@ class SignupView(CreateView):
|
||||
registration_form = StudentRegistrationForm(self.request.POST)
|
||||
else:
|
||||
registration_form = CoachRegistrationForm(self.request.POST)
|
||||
del registration_form.fields["team"]
|
||||
del registration_form.fields["email_confirmed"]
|
||||
|
||||
if not registration_form.is_valid():
|
||||
return self.form_invalid(form)
|
||||
@ -158,6 +171,8 @@ class UserDetailView(LoginRequiredMixin, DetailView):
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
user = request.user
|
||||
if not user.is_authenticated:
|
||||
return self.handle_no_permission()
|
||||
# Only an admin or the concerned user can see the information
|
||||
if not user.registration.is_admin and user.pk != kwargs["pk"]:
|
||||
raise PermissionDenied
|
||||
@ -169,6 +184,15 @@ class UserDetailView(LoginRequiredMixin, DetailView):
|
||||
return context
|
||||
|
||||
|
||||
class UserListView(AdminMixin, SingleTableView):
|
||||
"""
|
||||
Display the list of all registered users.
|
||||
"""
|
||||
model = Registration
|
||||
table_class = RegistrationTable
|
||||
template_name = "registration/user_list.html"
|
||||
|
||||
|
||||
class UserUpdateView(LoginRequiredMixin, UpdateView):
|
||||
"""
|
||||
Update the detail about a user and its registration.
|
||||
@ -189,6 +213,10 @@ class UserUpdateView(LoginRequiredMixin, UpdateView):
|
||||
context["title"] = _("Update user {user}").format(user=str(self.object.registration))
|
||||
context["registration_form"] = user.registration.form_class(data=self.request.POST or None,
|
||||
instance=self.object.registration)
|
||||
if not self.request.user.registration.is_admin:
|
||||
if "team" in context["registration_form"].fields:
|
||||
del context["registration_form"].fields["team"]
|
||||
del context["registration_form"].fields["email_confirmed"]
|
||||
return context
|
||||
|
||||
@transaction.atomic
|
||||
@ -196,6 +224,11 @@ class UserUpdateView(LoginRequiredMixin, UpdateView):
|
||||
user = form.instance
|
||||
registration_form = user.registration.form_class(data=self.request.POST or None,
|
||||
instance=self.object.registration)
|
||||
if not self.request.user.registration.is_admin:
|
||||
if "team" in registration_form.fields:
|
||||
del registration_form.fields["team"]
|
||||
del registration_form.fields["email_confirmed"]
|
||||
|
||||
if not registration_form.is_valid():
|
||||
return self.form_invalid(form)
|
||||
|
||||
|
@ -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
|
||||
|
@ -0,0 +1,2 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
"""
|
||||
ASGI config for corres2math project.
|
||||
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import os
|
||||
|
||||
_client = None
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from enum import Enum
|
||||
import os
|
||||
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from threading import local
|
||||
|
||||
from django.conf import settings
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
"""
|
||||
Django settings for corres2math project.
|
||||
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
|
||||
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import os
|
||||
|
||||
# Break it, fix it!
|
||||
|
40
corres2math/templates/about.html
Normal file
40
corres2math/templates/about.html
Normal file
@ -0,0 +1,40 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block contenttitle %}
|
||||
<h1>À propos</h1>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
La plateforme est développée avec le framework <a href="https://www.djangoproject.com/">Django</a> et le code
|
||||
source est accessible librement sur <a href="https://gitlab.com/animath/si/plateforme-corres2math">Gitlab</a>.
|
||||
Le code est distribué sous la licence <a href="https://www.gnu.org/licenses/gpl-3.0.html">GNU GPL v3</a>,
|
||||
qui vous autorise à consulter le code, à le partager, à réutiliser des parties du code et à contribuer.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Le site principal présent sur <a href="https://inscription.correspondances-maths.fr/">https://inscription.correspondances-maths.fr</a>
|
||||
est hébergé chez <a href="https://www.scaleway.com/fr/">Scaleway</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
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.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Pour toute demande ou réclammation, merci de nous contacter à l'adresse
|
||||
<a target="_blank" href="mailto:contact@correspondances-maths.fr">
|
||||
contact@correspondances-maths.fr
|
||||
</a>.
|
||||
</p>
|
||||
{% endblock %}
|
@ -71,6 +71,9 @@
|
||||
{% endif %}
|
||||
</li>
|
||||
{% if user.is_authenticated and user.registration.is_admin %}
|
||||
<li class="nav-item active">
|
||||
<a href="{% url "registration:user_list" %}" class="nav-link"><i class="fas fa-user"></i> {% trans "Users" %}</a>
|
||||
</li>
|
||||
<li class="nav-item active">
|
||||
<a href="#" class="nav-link" data-toggle="modal" data-target="#teamsModal"><i class="fas fa-users"></i> {% trans "Teams" %}</a>
|
||||
</li>
|
||||
@ -189,7 +192,7 @@
|
||||
class="form-inline">
|
||||
<span class="text-muted mr-1">
|
||||
<a target="_blank" href="mailto:contact@correspondances-maths.fr"
|
||||
class="text-muted">{% trans "Contact us" %}</a> —
|
||||
class="text-muted"><i class="fas fa-envelope"></i> {% trans "Contact us" %}</a>
|
||||
</span>
|
||||
{% csrf_token %}
|
||||
<select title="language" name="language"
|
||||
@ -204,10 +207,15 @@
|
||||
{{ lang_name }} ({{ lang_code }})
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</select>
|
||||
<noscript>
|
||||
<input type="submit">
|
||||
</noscript>
|
||||
</noscript>
|
||||
<a target="_blank" class="text-muted" href="{% url "about" %}">{% trans "About" %}</a> —
|
||||
<a target="_blank" class="text-muted"
|
||||
href="https://gitlab.com/animath/si/plateforme-corres2math">
|
||||
<i class="fab fa-gitlab"></i>
|
||||
</a>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col text-right">
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import os
|
||||
|
||||
from django.core.handlers.asgi import ASGIHandler
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django.contrib.auth.tokens import PasswordResetTokenGenerator
|
||||
|
||||
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
"""corres2math URL Configuration
|
||||
|
||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||
@ -23,7 +26,8 @@ from registration.views import PhotoAuthorizationView
|
||||
from .views import AdminSearchView
|
||||
|
||||
urlpatterns = [
|
||||
path('', TemplateView.as_view(template_name="index.html", extra_context=dict(title="Accueil")), name='index'),
|
||||
path('', TemplateView.as_view(template_name="index.html"), name='index'),
|
||||
path('about/', TemplateView.as_view(template_name="about.html"), name='about'),
|
||||
path('i18n/', include('django.conf.urls.i18n')),
|
||||
path('admin/doc/', include('django.contrib.admindocs.urls')),
|
||||
path('admin/', admin.site.urls, name="admin"),
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from haystack.generic_views import SearchView
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright (C) 2020 by Animath
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
"""
|
||||
WSGI config for corres2math project.
|
||||
|
||||
|
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Corres2math\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-12-04 01:42+0100\n"
|
||||
"POT-Creation-Date: 2020-12-22 21:30+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: Yohann D'ANELLO <yohann.danello@animath.fr>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -99,13 +99,13 @@ 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/admin.py:16 apps/participation/models.py:121
|
||||
#: apps/participation/admin.py:16 apps/participation/models.py:132
|
||||
#: apps/participation/tables.py:35 apps/participation/tables.py:62
|
||||
msgid "problem number"
|
||||
msgstr "numéro de problème"
|
||||
|
||||
#: apps/participation/admin.py:21 apps/participation/models.py:127
|
||||
#: apps/participation/models.py:181
|
||||
#: apps/participation/admin.py:21 apps/participation/models.py:138
|
||||
#: apps/participation/models.py:192
|
||||
msgid "valid"
|
||||
msgstr "valide"
|
||||
|
||||
@ -184,96 +184,96 @@ msgstr ""
|
||||
"Donner l'autorisation de publier la vidéo sur le site principal pour "
|
||||
"promouvoir les Correspondances."
|
||||
|
||||
#: apps/participation/models.py:96
|
||||
#: apps/participation/models.py:107
|
||||
#, python-brace-format
|
||||
msgid "Team {name} ({trigram})"
|
||||
msgstr "Équipe {name} ({trigram})"
|
||||
|
||||
#: apps/participation/models.py:99 apps/participation/models.py:114
|
||||
#: apps/participation/models.py:110 apps/participation/models.py:125
|
||||
#: apps/registration/models.py:106 apps/registration/models.py:155
|
||||
msgid "team"
|
||||
msgstr "équipe"
|
||||
|
||||
#: apps/participation/models.py:100
|
||||
#: apps/participation/models.py:111
|
||||
msgid "teams"
|
||||
msgstr "équipes"
|
||||
|
||||
#: apps/participation/models.py:118
|
||||
#: apps/participation/models.py:129
|
||||
#, python-brace-format
|
||||
msgid "Problem #{problem:d}"
|
||||
msgstr "Problème n°{problem:d}"
|
||||
|
||||
#: apps/participation/models.py:128 apps/participation/models.py:182
|
||||
#: apps/participation/models.py:139 apps/participation/models.py:193
|
||||
msgid "The video got the validation of the administrators."
|
||||
msgstr "La vidéo a été validée par les administrateurs."
|
||||
|
||||
#: apps/participation/models.py:137
|
||||
#: apps/participation/models.py:148
|
||||
msgid "solution video"
|
||||
msgstr "vidéo de solution"
|
||||
|
||||
#: apps/participation/models.py:146
|
||||
#: apps/participation/models.py:157
|
||||
msgid "received participation"
|
||||
msgstr "participation reçue"
|
||||
|
||||
#: apps/participation/models.py:155
|
||||
#: apps/participation/models.py:166
|
||||
msgid "synthesis video"
|
||||
msgstr "vidéo de synthèse"
|
||||
|
||||
#: apps/participation/models.py:162
|
||||
#: apps/participation/models.py:173
|
||||
#, python-brace-format
|
||||
msgid "Participation of the team {name} ({trigram})"
|
||||
msgstr "Participation de l'équipe {name} ({trigram})"
|
||||
|
||||
#: apps/participation/models.py:165 apps/participation/models.py:239
|
||||
#: apps/participation/models.py:176 apps/participation/models.py:250
|
||||
msgid "participation"
|
||||
msgstr "participation"
|
||||
|
||||
#: apps/participation/models.py:166
|
||||
#: apps/participation/models.py:177
|
||||
msgid "participations"
|
||||
msgstr "participations"
|
||||
|
||||
#: apps/participation/models.py:174
|
||||
#: apps/participation/models.py:185
|
||||
msgid "link"
|
||||
msgstr "lien"
|
||||
|
||||
#: apps/participation/models.py:175
|
||||
#: apps/participation/models.py:186
|
||||
msgid "The full video link."
|
||||
msgstr "Le lien complet de la vidéo."
|
||||
|
||||
#: apps/participation/models.py:224
|
||||
#: apps/participation/models.py:235
|
||||
#, python-brace-format
|
||||
msgid "Video of team {name} ({trigram})"
|
||||
msgstr "Vidéo de l'équipe {name} ({trigram})"
|
||||
|
||||
#: apps/participation/models.py:228
|
||||
#: apps/participation/models.py:239
|
||||
msgid "video"
|
||||
msgstr "vidéo"
|
||||
|
||||
#: apps/participation/models.py:229
|
||||
#: apps/participation/models.py:240
|
||||
msgid "videos"
|
||||
msgstr "vidéos"
|
||||
|
||||
#: apps/participation/models.py:244
|
||||
#: apps/participation/models.py:255
|
||||
msgid "question"
|
||||
msgstr "question"
|
||||
|
||||
#: apps/participation/models.py:258
|
||||
#: apps/participation/models.py:269
|
||||
msgid "phase number"
|
||||
msgstr "phase"
|
||||
|
||||
#: apps/participation/models.py:263
|
||||
#: apps/participation/models.py:274
|
||||
msgid "phase description"
|
||||
msgstr "description"
|
||||
|
||||
#: apps/participation/models.py:267
|
||||
#: apps/participation/models.py:278
|
||||
msgid "start date of the given phase"
|
||||
msgstr "début de la phase"
|
||||
|
||||
#: apps/participation/models.py:272
|
||||
#: apps/participation/models.py:283
|
||||
msgid "end date of the given phase"
|
||||
msgstr "fin de la phase"
|
||||
|
||||
#: apps/participation/models.py:290
|
||||
#: apps/participation/models.py:299
|
||||
msgid ""
|
||||
"Phase {phase_number:d} starts on {start:%Y-%m-%d %H:%M} and ends on {end:%Y-"
|
||||
"%m-%d %H:%M}"
|
||||
@ -281,11 +281,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:294
|
||||
#: apps/participation/models.py:303
|
||||
msgid "phase"
|
||||
msgstr "phase"
|
||||
|
||||
#: apps/participation/models.py:295
|
||||
#: apps/participation/models.py:304
|
||||
msgid "phases"
|
||||
msgstr "phases"
|
||||
|
||||
@ -324,12 +324,12 @@ msgstr ""
|
||||
"contacter :)"
|
||||
|
||||
#: apps/participation/templates/participation/create_team.html:11
|
||||
#: corres2math/templates/base.html:237
|
||||
#: corres2math/templates/base.html:247
|
||||
msgid "Create"
|
||||
msgstr "Créer"
|
||||
|
||||
#: apps/participation/templates/participation/join_team.html:11
|
||||
#: corres2math/templates/base.html:232
|
||||
#: corres2math/templates/base.html:242
|
||||
msgid "Join"
|
||||
msgstr "Rejoindre"
|
||||
|
||||
@ -503,7 +503,7 @@ msgstr "Définir l'équipe qui recevra votre vidéo"
|
||||
|
||||
#: apps/participation/templates/participation/participation_detail.html:181
|
||||
#: apps/participation/templates/participation/participation_detail.html:233
|
||||
#: apps/participation/views.py:494
|
||||
#: apps/participation/views.py:510
|
||||
msgid "Upload video"
|
||||
msgstr "Envoyer la vidéo"
|
||||
|
||||
@ -538,7 +538,7 @@ msgid "Update question"
|
||||
msgstr "Modifier la question"
|
||||
|
||||
#: apps/participation/templates/participation/participation_detail.html:217
|
||||
#: apps/participation/views.py:470
|
||||
#: apps/participation/views.py:486
|
||||
msgid "Delete question"
|
||||
msgstr "Supprimer la question"
|
||||
|
||||
@ -548,8 +548,8 @@ msgid "Display synthesis"
|
||||
msgstr "Afficher la synthèse"
|
||||
|
||||
#: apps/participation/templates/participation/phase_list.html:10
|
||||
#: apps/participation/views.py:513 corres2math/templates/base.html:68
|
||||
#: corres2math/templates/base.html:70 corres2math/templates/base.html:221
|
||||
#: apps/participation/views.py:531 corres2math/templates/base.html:68
|
||||
#: corres2math/templates/base.html:70 corres2math/templates/base.html:231
|
||||
msgid "Calendar"
|
||||
msgstr "Calendrier"
|
||||
|
||||
@ -661,7 +661,7 @@ msgid "Update team"
|
||||
msgstr "Modifier l'équipe"
|
||||
|
||||
#: apps/participation/templates/participation/team_detail.html:127
|
||||
#: apps/participation/views.py:323
|
||||
#: apps/participation/views.py:337
|
||||
msgid "Leave team"
|
||||
msgstr "Quitter l'équipe"
|
||||
|
||||
@ -670,53 +670,53 @@ msgid "Are you sure that you want to leave this team?"
|
||||
msgstr "Êtes-vous sûr·e de vouloir quitter cette équipe ?"
|
||||
|
||||
#: apps/participation/templates/participation/team_list.html:6
|
||||
#: corres2math/templates/base.html:225
|
||||
#: corres2math/templates/base.html:235
|
||||
msgid "All teams"
|
||||
msgstr "Toutes les équipes"
|
||||
|
||||
#: apps/participation/views.py:36 corres2math/templates/base.html:81
|
||||
#: corres2math/templates/base.html:236
|
||||
#: apps/participation/views.py:37 corres2math/templates/base.html:84
|
||||
#: corres2math/templates/base.html:246
|
||||
msgid "Create team"
|
||||
msgstr "Créer une équipe"
|
||||
|
||||
#: apps/participation/views.py:43 apps/participation/views.py:89
|
||||
#: apps/participation/views.py:46 apps/participation/views.py:94
|
||||
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:45 apps/participation/views.py:91
|
||||
#: apps/participation/views.py:48 apps/participation/views.py:96
|
||||
msgid "You are already in a team."
|
||||
msgstr "Vous êtes déjà dans une équipe."
|
||||
|
||||
#: apps/participation/views.py:82 corres2math/templates/base.html:86
|
||||
#: corres2math/templates/base.html:231
|
||||
#: apps/participation/views.py:85 corres2math/templates/base.html:89
|
||||
#: corres2math/templates/base.html:241
|
||||
msgid "Join team"
|
||||
msgstr "Rejoindre une équipe"
|
||||
|
||||
#: apps/participation/views.py:142 apps/participation/views.py:329
|
||||
#: apps/participation/views.py:362
|
||||
#: apps/participation/views.py:147 apps/participation/views.py:343
|
||||
#: apps/participation/views.py:376
|
||||
msgid "You are not in a team."
|
||||
msgstr "Vous n'êtes pas dans une équipe."
|
||||
|
||||
#: apps/participation/views.py:143 apps/participation/views.py:363
|
||||
#: apps/participation/views.py:148 apps/participation/views.py:377
|
||||
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:165
|
||||
#: apps/participation/views.py:170
|
||||
#, python-brace-format
|
||||
msgid "Detail of team {trigram}"
|
||||
msgstr "Détails de l'équipe {trigram}"
|
||||
|
||||
#: apps/participation/views.py:197
|
||||
#: apps/participation/views.py:202
|
||||
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:200
|
||||
#: apps/participation/views.py:205
|
||||
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:203
|
||||
#: apps/participation/views.py:208
|
||||
msgid ""
|
||||
"The team can't be validated: missing email address confirmations, photo "
|
||||
"authorizations, people or the chosen problem is not set."
|
||||
@ -725,51 +725,51 @@ msgstr ""
|
||||
"d'adresse e-mail, soit une autorisation parentale, soit des personnes soit "
|
||||
"le problème n'a pas été choisi."
|
||||
|
||||
#: apps/participation/views.py:222
|
||||
#: apps/participation/views.py:227
|
||||
msgid "You are not an administrator."
|
||||
msgstr "Vous n'êtes pas administrateur."
|
||||
|
||||
#: apps/participation/views.py:225
|
||||
#: apps/participation/views.py:230
|
||||
msgid "This team has no pending validation."
|
||||
msgstr "L'équipe n'a pas de validation en attente."
|
||||
|
||||
#: apps/participation/views.py:244
|
||||
#: apps/participation/views.py:254
|
||||
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:272
|
||||
#: apps/participation/views.py:284
|
||||
#, python-brace-format
|
||||
msgid "Update team {trigram}"
|
||||
msgstr "Mise à jour de l'équipe {trigram}"
|
||||
|
||||
#: apps/participation/views.py:309 apps/registration/views.py:243
|
||||
#: apps/participation/views.py:323 apps/registration/views.py:283
|
||||
#, python-brace-format
|
||||
msgid "Photo authorization of {student}.{ext}"
|
||||
msgstr "Autorisation de droit à l'image de {student}.{ext}"
|
||||
|
||||
#: apps/participation/views.py:313
|
||||
#: apps/participation/views.py:327
|
||||
#, 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:331
|
||||
#: apps/participation/views.py:345
|
||||
msgid "The team is already validated or the validation is pending."
|
||||
msgstr "La validation de l'équipe est déjà faite ou en cours."
|
||||
|
||||
#: apps/participation/views.py:375
|
||||
#: apps/participation/views.py:391
|
||||
msgid "The team is not validated yet."
|
||||
msgstr "L'équipe n'est pas encore validée."
|
||||
|
||||
#: apps/participation/views.py:385
|
||||
#: apps/participation/views.py:401
|
||||
#, python-brace-format
|
||||
msgid "Participation of team {trigram}"
|
||||
msgstr "Participation de l'équipe {trigram}"
|
||||
|
||||
#: apps/participation/views.py:422
|
||||
#: apps/participation/views.py:438
|
||||
msgid "Create question"
|
||||
msgstr "Créer une question"
|
||||
|
||||
#: apps/participation/views.py:522
|
||||
#: apps/participation/views.py:540
|
||||
msgid "Calendar update"
|
||||
msgstr "Mise à jour du calendrier"
|
||||
|
||||
@ -789,7 +789,11 @@ msgstr "encadrant"
|
||||
msgid "This email address is already used."
|
||||
msgstr "Cette adresse e-mail est déjà utilisée."
|
||||
|
||||
#: apps/registration/forms.py:77
|
||||
#: apps/registration/forms.py:78
|
||||
msgid "The uploaded file size must be under 2 Mo."
|
||||
msgstr "Le fichier envoyé doit peser moins de 2 Mo."
|
||||
|
||||
#: apps/registration/forms.py:80
|
||||
msgid "The uploaded file must be a PDF, PNG of JPEG file."
|
||||
msgstr "Le fichier envoyé doit être au format PDF, PNG ou JPEG."
|
||||
|
||||
@ -891,13 +895,21 @@ msgstr "Votre email a été validé avec succès."
|
||||
msgid "You can now <a href=\"%(login_url)s\">log in</a>."
|
||||
msgstr "Vous pouvez désormais vous <a href=\"%(login_url)s\">connecter</a>."
|
||||
|
||||
#: apps/registration/templates/registration/email_validation_complete.html:22
|
||||
#: apps/registration/templates/registration/email_validation_complete.html:23
|
||||
msgid ""
|
||||
"The link was invalid. The token may have expired. Please send us an email to "
|
||||
"activate your account."
|
||||
"The link was invalid. The token may have expired, or your account is already "
|
||||
"activated. However, your account seems to be already valid."
|
||||
msgstr ""
|
||||
"Le lien est invalide. Le jeton a peut-être expiré. Merci de nous envoyer un "
|
||||
"mail pour activer votre compte."
|
||||
"Le lien est invalide. Le jeton a peut-être expiré, ou votre compte est déjà "
|
||||
"activé. Toutefois, il semble que votre compte est déjà valide."
|
||||
|
||||
#: apps/registration/templates/registration/email_validation_complete.html:25
|
||||
msgid ""
|
||||
"The link was invalid. The token may have expired, or your account is already "
|
||||
"activated. Please send us an email to activate your account."
|
||||
msgstr ""
|
||||
"Le lien est invalide. Le jeton a peut-être expiré, ou votre compte est déjà "
|
||||
"activé. Merci de nous envoyer un mail pour activer votre compte."
|
||||
|
||||
#: apps/registration/templates/registration/email_validation_email_sent.html:10
|
||||
msgid "Account activation"
|
||||
@ -966,8 +978,8 @@ msgid "Your password has been set. You may go ahead and log in now."
|
||||
msgstr "Votre mot de passe a été changé. Vous pouvez désormais vous connecter."
|
||||
|
||||
#: apps/registration/templates/registration/password_reset_complete.html:10
|
||||
#: corres2math/templates/base.html:134 corres2math/templates/base.html:241
|
||||
#: corres2math/templates/base.html:242
|
||||
#: corres2math/templates/base.html:139 corres2math/templates/base.html:251
|
||||
#: corres2math/templates/base.html:252
|
||||
#: corres2math/templates/registration/login.html:7
|
||||
#: corres2math/templates/registration/login.html:8
|
||||
#: corres2math/templates/registration/login.html:25
|
||||
@ -1024,7 +1036,7 @@ msgstr "Réinitialiser mon mot de passe"
|
||||
#: apps/registration/templates/registration/signup.html:5
|
||||
#: apps/registration/templates/registration/signup.html:8
|
||||
#: apps/registration/templates/registration/signup.html:20
|
||||
#: apps/registration/views.py:29
|
||||
#: apps/registration/views.py:32
|
||||
msgid "Sign up"
|
||||
msgstr "Inscription"
|
||||
|
||||
@ -1101,40 +1113,40 @@ msgid "Update user"
|
||||
msgstr "Modifier l'utilisateur"
|
||||
|
||||
#: apps/registration/templates/registration/user_detail.html:77
|
||||
#: apps/registration/views.py:216
|
||||
#: apps/registration/views.py:246
|
||||
msgid "Upload photo authorization"
|
||||
msgstr "Téléverser l'autorisation de droit à l'image"
|
||||
|
||||
#: apps/registration/views.py:37
|
||||
#: apps/registration/views.py:40
|
||||
msgid "You can't register now."
|
||||
msgstr "Vous ne pouvez pas vous inscrire maintenant."
|
||||
|
||||
#: apps/registration/views.py:74
|
||||
#: apps/registration/views.py:84
|
||||
msgid "Email validation"
|
||||
msgstr "Validation de l'adresse mail"
|
||||
|
||||
#: apps/registration/views.py:76
|
||||
#: apps/registration/views.py:86
|
||||
msgid "Validate email"
|
||||
msgstr "Valider l'adresse mail"
|
||||
|
||||
#: apps/registration/views.py:115
|
||||
#: apps/registration/views.py:125
|
||||
msgid "Email validation unsuccessful"
|
||||
msgstr "Échec de la validation de l'adresse mail"
|
||||
|
||||
#: apps/registration/views.py:126
|
||||
#: apps/registration/views.py:136
|
||||
msgid "Email validation email sent"
|
||||
msgstr "Mail de confirmation de l'adresse mail envoyé"
|
||||
|
||||
#: apps/registration/views.py:134
|
||||
#: apps/registration/views.py:144
|
||||
msgid "Resend email validation link"
|
||||
msgstr "Renvoyé le lien de validation de l'adresse mail"
|
||||
|
||||
#: apps/registration/views.py:168
|
||||
#: apps/registration/views.py:180
|
||||
#, python-brace-format
|
||||
msgid "Detail of user {user}"
|
||||
msgstr "Détails de l'utilisateur {user}"
|
||||
|
||||
#: apps/registration/views.py:189
|
||||
#: apps/registration/views.py:210
|
||||
#, python-brace-format
|
||||
msgid "Update user {user}"
|
||||
msgstr "Mise à jour de l'utilisateur {user}"
|
||||
@ -1205,46 +1217,50 @@ msgid "Home"
|
||||
msgstr "Accueil"
|
||||
|
||||
#: corres2math/templates/base.html:75
|
||||
msgid "Users"
|
||||
msgstr "Utilisateurs"
|
||||
|
||||
#: corres2math/templates/base.html:78
|
||||
msgid "Teams"
|
||||
msgstr "Équipes"
|
||||
|
||||
#: corres2math/templates/base.html:92
|
||||
#: corres2math/templates/base.html:95
|
||||
msgid "My team"
|
||||
msgstr "Mon équipe"
|
||||
|
||||
#: corres2math/templates/base.html:97
|
||||
#: corres2math/templates/base.html:100
|
||||
msgid "My participation"
|
||||
msgstr "Ma participation"
|
||||
|
||||
#: corres2math/templates/base.html:104
|
||||
#: corres2math/templates/base.html:107
|
||||
msgid "Chat"
|
||||
msgstr "Chat"
|
||||
|
||||
#: corres2math/templates/base.html:108
|
||||
#: corres2math/templates/base.html:111
|
||||
msgid "Administration"
|
||||
msgstr "Administration"
|
||||
|
||||
#: corres2math/templates/base.html:116
|
||||
#: corres2math/templates/base.html:119
|
||||
msgid "Search..."
|
||||
msgstr "Chercher ..."
|
||||
|
||||
#: corres2math/templates/base.html:125
|
||||
#: corres2math/templates/base.html:128
|
||||
msgid "Return to admin view"
|
||||
msgstr "Retourner à l'interface administrateur"
|
||||
|
||||
#: corres2math/templates/base.html:130
|
||||
#: corres2math/templates/base.html:134
|
||||
msgid "Register"
|
||||
msgstr "S'inscrire"
|
||||
|
||||
#: corres2math/templates/base.html:146
|
||||
#: corres2math/templates/base.html:151
|
||||
msgid "My account"
|
||||
msgstr "Mon compte"
|
||||
|
||||
#: corres2math/templates/base.html:149
|
||||
#: corres2math/templates/base.html:154
|
||||
msgid "Log out"
|
||||
msgstr "Déconnexion"
|
||||
|
||||
#: corres2math/templates/base.html:166
|
||||
#: corres2math/templates/base.html:171
|
||||
#, python-format
|
||||
msgid ""
|
||||
"Your email address is not validated. Please click on the link you received "
|
||||
@ -1255,11 +1271,15 @@ msgstr ""
|
||||
"avez reçu par mail. Vous pouvez renvoyer un mail en cliquant sur <a href="
|
||||
"\"%(send_email_url)s\">ce lien</a>."
|
||||
|
||||
#: corres2math/templates/base.html:190
|
||||
#: corres2math/templates/base.html:195
|
||||
msgid "Contact us"
|
||||
msgstr "Nous contacter"
|
||||
|
||||
#: corres2math/templates/base.html:228
|
||||
#: corres2math/templates/base.html:214
|
||||
msgid "About"
|
||||
msgstr "À propos"
|
||||
|
||||
#: corres2math/templates/base.html:238
|
||||
msgid "Search results"
|
||||
msgstr "Résultats de la recherche"
|
||||
|
||||
|
@ -5,6 +5,7 @@ upstream corres2math {
|
||||
server {
|
||||
listen 80;
|
||||
server_name corres2math;
|
||||
client_max_body_size 50M;
|
||||
|
||||
location / {
|
||||
proxy_pass http://corres2math;
|
||||
|
@ -1,4 +1,4 @@
|
||||
Django~=3.1
|
||||
Django~=3.0
|
||||
django-bootstrap-datepicker-plus~=3.0
|
||||
django-cas-server~=1.2
|
||||
django-crispy-forms~=1.9
|
||||
|
Reference in New Issue
Block a user