mirror of
https://gitlab.com/animath/si/plateforme-corres2math.git
synced 2024-12-05 01:26:54 +00:00
Merge branch 'master' into 'improvements'
# Conflicts: # locale/fr/LC_MESSAGES/django.po
This commit is contained in:
commit
982b61fe03
@ -16,10 +16,8 @@ class Command(BaseCommand):
|
|||||||
if not os.path.isfile(".matrix_avatar"):
|
if not os.path.isfile(".matrix_avatar"):
|
||||||
stat_file = os.stat("corres2math/static/logo.png")
|
stat_file = os.stat("corres2math/static/logo.png")
|
||||||
with open("corres2math/static/logo.png", "rb") as f:
|
with open("corres2math/static/logo.png", "rb") as f:
|
||||||
resp, _ = Matrix.upload(f, filename="logo.png", content_type="image/png",
|
resp = Matrix.upload(f, filename="logo.png", content_type="image/png",
|
||||||
filesize=stat_file.st_size)
|
filesize=stat_file.st_size)[0][0]
|
||||||
if not hasattr(resp, "content_uri"):
|
|
||||||
raise Exception(resp)
|
|
||||||
avatar_uri = resp.content_uri
|
avatar_uri = resp.content_uri
|
||||||
with open(".matrix_avatar", "w") as f:
|
with open(".matrix_avatar", "w") as f:
|
||||||
f.write(avatar_uri)
|
f.write(avatar_uri)
|
||||||
@ -58,9 +56,20 @@ class Command(BaseCommand):
|
|||||||
preset=RoomPreset.public_chat,
|
preset=RoomPreset.public_chat,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if not async_to_sync(Matrix.resolve_room_alias)("#flood:correspondances-maths.fr"):
|
||||||
|
Matrix.create_room(
|
||||||
|
visibility=RoomVisibility.public,
|
||||||
|
alias="flood",
|
||||||
|
name="Flood",
|
||||||
|
topic="Discutez de tout et de rien !",
|
||||||
|
federate=False,
|
||||||
|
preset=RoomPreset.public_chat,
|
||||||
|
)
|
||||||
|
|
||||||
Matrix.set_room_avatar("#annonces:correspondances-maths.fr", avatar_uri)
|
Matrix.set_room_avatar("#annonces:correspondances-maths.fr", avatar_uri)
|
||||||
Matrix.set_room_avatar("#faq:correspondances-maths.fr", avatar_uri)
|
Matrix.set_room_avatar("#faq:correspondances-maths.fr", avatar_uri)
|
||||||
Matrix.set_room_avatar("#je-cherche-une-equipe:correspondances-maths.fr", avatar_uri)
|
Matrix.set_room_avatar("#je-cherche-une-equipe:correspondances-maths.fr", avatar_uri)
|
||||||
|
Matrix.set_room_avatar("#flood:correspondances-maths.fr", avatar_uri)
|
||||||
|
|
||||||
Matrix.set_room_power_level_event("#annonces:correspondances-maths.fr", "events_default", 50)
|
Matrix.set_room_power_level_event("#annonces:correspondances-maths.fr", "events_default", 50)
|
||||||
|
|
||||||
@ -69,9 +78,12 @@ class Command(BaseCommand):
|
|||||||
Matrix.invite("#faq:correspondances-maths.fr", f"@{r.matrix_username}:correspondances-maths.fr")
|
Matrix.invite("#faq:correspondances-maths.fr", f"@{r.matrix_username}:correspondances-maths.fr")
|
||||||
Matrix.invite("#je-cherche-une-equipe:correspondances-maths.fr",
|
Matrix.invite("#je-cherche-une-equipe:correspondances-maths.fr",
|
||||||
f"@{r.matrix_username}:correspondances-maths.fr")
|
f"@{r.matrix_username}:correspondances-maths.fr")
|
||||||
|
Matrix.invite("#flood:correspondances-maths.fr", f"@{r.matrix_username}:correspondances-maths.fr")
|
||||||
|
|
||||||
for admin in AdminRegistration.objects.all():
|
for admin in AdminRegistration.objects.all():
|
||||||
Matrix.set_room_power_level("#annonces:correspondances-maths.fr",
|
Matrix.set_room_power_level("#annonces:correspondances-maths.fr",
|
||||||
f"@{admin.matrix_username}:correspondances-maths.fr", 95)
|
f"@{admin.matrix_username}:correspondances-maths.fr", 95)
|
||||||
Matrix.set_room_power_level("#faq:correspondances-maths.fr",
|
Matrix.set_room_power_level("#faq:correspondances-maths.fr",
|
||||||
f"@{admin.matrix_username}:correspondances-maths.fr", 95)
|
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)
|
||||||
|
38
apps/participation/management/commands/fix_sympa_lists.py
Normal file
38
apps/participation/management/commands/fix_sympa_lists.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
from corres2math.lists import get_sympa_client
|
||||||
|
from django.core.management import BaseCommand
|
||||||
|
from django.db.models import Q
|
||||||
|
from participation.models import Team
|
||||||
|
from registration.models import CoachRegistration, StudentRegistration
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
"""
|
||||||
|
Create Sympa mailing lists and register teams.
|
||||||
|
"""
|
||||||
|
sympa = get_sympa_client()
|
||||||
|
|
||||||
|
sympa.create_list("equipes", "Équipes des Correspondances", "hotline",
|
||||||
|
"Liste de diffusion pour contacter toutes les équipes validées des Correspondances.",
|
||||||
|
"education", raise_error=False)
|
||||||
|
sympa.create_list("equipes-non-valides", "Équipes des Correspondances", "hotline",
|
||||||
|
"Liste de diffusion pour contacter toutes les équipes non-validées des Correspondances.",
|
||||||
|
"education", raise_error=False)
|
||||||
|
|
||||||
|
for problem in range(1, 4):
|
||||||
|
sympa.create_list(f"probleme-{problem}",
|
||||||
|
f"Équipes des Correspondances participant au problème {problem}", "hotline",
|
||||||
|
f"Liste de diffusion pour contacter les équipes participant au problème {problem}"
|
||||||
|
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)
|
||||||
|
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():
|
||||||
|
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}")
|
||||||
|
for coach in CoachRegistration.objects.filter(team__isnull=False).all():
|
||||||
|
sympa.subscribe(coach.user.email, f"equipe-{coach.team.trigram.lower}", True, f"{coach}")
|
@ -65,11 +65,22 @@ class Team(models.Model):
|
|||||||
"education",
|
"education",
|
||||||
raise_error=False,
|
raise_error=False,
|
||||||
)
|
)
|
||||||
|
if self.pk and self.participation.valid: # pragma: no cover
|
||||||
|
get_sympa_client().subscribe(self.email, "equipes", False, f"Equipe {self.name}")
|
||||||
|
get_sympa_client().subscribe(self.email, f"probleme-{self.participation.problem}", False,
|
||||||
|
f"Equipe {self.name}")
|
||||||
|
else:
|
||||||
|
get_sympa_client().subscribe(self.email, "equipes-non-valides", False)
|
||||||
|
|
||||||
def delete_mailing_list(self):
|
def delete_mailing_list(self):
|
||||||
"""
|
"""
|
||||||
Drop the Sympa mailing list, if the team is empty or if the trigram changed.
|
Drop the Sympa mailing list, if the team is empty or if the trigram changed.
|
||||||
"""
|
"""
|
||||||
|
if self.participation.valid: # pragma: no cover
|
||||||
|
get_sympa_client().unsubscribe(self.email, "equipes", False)
|
||||||
|
get_sympa_client().unsubscribe(self.email, f"probleme-{self.participation.problem}", False)
|
||||||
|
else:
|
||||||
|
get_sympa_client().unsubscribe(self.email, "equipes-non-valides", False)
|
||||||
get_sympa_client().delete_list(f"equipe-{self.trigram}")
|
get_sympa_client().delete_list(f"equipe-{self.trigram}")
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
@ -281,10 +292,8 @@ class Phase(models.Model):
|
|||||||
qs = Phase.objects.filter(start__lte=timezone.now(), end__gte=timezone.now())
|
qs = Phase.objects.filter(start__lte=timezone.now(), end__gte=timezone.now())
|
||||||
if qs.exists():
|
if qs.exists():
|
||||||
return qs.get()
|
return qs.get()
|
||||||
qs = Phase.objects.order_by("phase_number").all()
|
qs = Phase.objects.filter(start__lte=timezone.now()).order_by("phase_number").all()
|
||||||
if timezone.now() < qs.first().start:
|
return qs.last() if qs.exists() else None
|
||||||
return qs.first()
|
|
||||||
return qs.last()
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return _("Phase {phase_number:d} starts on {start:%Y-%m-%d %H:%M} and ends on {end:%Y-%m-%d %H:%M}")\
|
return _("Phase {phase_number:d} starts on {start:%Y-%m-%d %H:%M} and ends on {end:%Y-%m-%d %H:%M}")\
|
||||||
|
@ -2,7 +2,7 @@ from corres2math.lists import get_sympa_client
|
|||||||
from participation.models import Participation, Team, Video
|
from participation.models import Participation, Team, Video
|
||||||
|
|
||||||
|
|
||||||
def create_team_participation(instance, **_):
|
def create_team_participation(instance, created, **_):
|
||||||
"""
|
"""
|
||||||
When a team got created, create an associated team and create Video objects.
|
When a team got created, create an associated team and create Video objects.
|
||||||
"""
|
"""
|
||||||
@ -12,6 +12,8 @@ def create_team_participation(instance, **_):
|
|||||||
if not participation.synthesis:
|
if not participation.synthesis:
|
||||||
participation.synthesis = Video.objects.create()
|
participation.synthesis = Video.objects.create()
|
||||||
participation.save()
|
participation.save()
|
||||||
|
if not created:
|
||||||
|
participation.team.create_mailing_list()
|
||||||
|
|
||||||
|
|
||||||
def update_mailing_list(instance: Team, **_):
|
def update_mailing_list(instance: Team, **_):
|
||||||
|
0
apps/participation/templatetags/__init__.py
Normal file
0
apps/participation/templatetags/__init__.py
Normal file
12
apps/participation/templatetags/calendar.py
Normal file
12
apps/participation/templatetags/calendar.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
from django import template
|
||||||
|
|
||||||
|
from ..models import Phase
|
||||||
|
|
||||||
|
|
||||||
|
def current_phase(nb):
|
||||||
|
phase = Phase.current_phase()
|
||||||
|
return phase is not None and phase.phase_number == nb
|
||||||
|
|
||||||
|
|
||||||
|
register = template.Library()
|
||||||
|
register.filter("current_phase", current_phase)
|
@ -582,7 +582,7 @@ class TestStudentParticipation(TestCase):
|
|||||||
for i in range(1, 5):
|
for i in range(1, 5):
|
||||||
Phase.objects.filter(phase_number=i).update(start=timezone.now() + timedelta(days=2 * i),
|
Phase.objects.filter(phase_number=i).update(start=timezone.now() + timedelta(days=2 * i),
|
||||||
end=timezone.now() + timedelta(days=2 * i + 1))
|
end=timezone.now() + timedelta(days=2 * i + 1))
|
||||||
self.assertEqual(Phase.current_phase().phase_number, 1)
|
self.assertEqual(Phase.current_phase(), None)
|
||||||
|
|
||||||
# We are after the end
|
# We are after the end
|
||||||
for i in range(1, 5):
|
for i in range(1, 5):
|
||||||
|
@ -232,6 +232,11 @@ class TeamDetailView(LoginRequiredMixin, FormMixin, ProcessFormView, DetailView)
|
|||||||
mail_plain = render_to_string("participation/mails/team_validated.txt", mail_context)
|
mail_plain = render_to_string("participation/mails/team_validated.txt", mail_context)
|
||||||
mail_html = render_to_string("participation/mails/team_validated.html", mail_context)
|
mail_html = render_to_string("participation/mails/team_validated.html", mail_context)
|
||||||
send_mail("[Corres2math] Équipe validée", mail_plain, None, [self.object.email], html_message=mail_html)
|
send_mail("[Corres2math] Équipe validée", mail_plain, None, [self.object.email], html_message=mail_html)
|
||||||
|
|
||||||
|
get_sympa_client().subscribe(self.object.email, "equipes", False, f"Equipe {self.object.name}")
|
||||||
|
get_sympa_client().unsubscribe(self.object.email, "equipes-non-valides", False)
|
||||||
|
get_sympa_client().subscribe(self.object.email, f"probleme-{self.object.participation.problem}", False,
|
||||||
|
f"Equipe {self.object.name}")
|
||||||
elif "invalidate" in self.request.POST:
|
elif "invalidate" in self.request.POST:
|
||||||
self.object.participation.valid = None
|
self.object.participation.valid = None
|
||||||
self.object.participation.save()
|
self.object.participation.save()
|
||||||
|
@ -13,5 +13,14 @@
|
|||||||
"single_log_out": true,
|
"single_log_out": true,
|
||||||
"single_log_out_callback": ""
|
"single_log_out_callback": ""
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"model": "cas_server.replaceattributname",
|
||||||
|
"pk": 1,
|
||||||
|
"fields": {
|
||||||
|
"name": "display_name",
|
||||||
|
"replace": "",
|
||||||
|
"service_pattern": 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -50,3 +50,4 @@ def invite_to_public_rooms(instance: Registration, created: bool, **_):
|
|||||||
Matrix.invite("#faq: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-equip:correspondances-maths.fr",
|
||||||
f"@{instance.matrix_username}: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,4 @@
|
|||||||
|
from datetime import timedelta
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from corres2math.tokens import email_validation_token
|
from corres2math.tokens import email_validation_token
|
||||||
@ -7,8 +8,10 @@ from django.contrib.sites.models import Site
|
|||||||
from django.core.management import call_command
|
from django.core.management import call_command
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
from django.utils import timezone
|
||||||
from django.utils.encoding import force_bytes
|
from django.utils.encoding import force_bytes
|
||||||
from django.utils.http import urlsafe_base64_encode
|
from django.utils.http import urlsafe_base64_encode
|
||||||
|
from participation.models import Phase
|
||||||
|
|
||||||
from .models import AdminRegistration, CoachRegistration, StudentRegistration
|
from .models import AdminRegistration, CoachRegistration, StudentRegistration
|
||||||
|
|
||||||
@ -81,6 +84,13 @@ class TestRegistration(TestCase):
|
|||||||
"""
|
"""
|
||||||
Ensure that the signup form is working successfully.
|
Ensure that the signup form is working successfully.
|
||||||
"""
|
"""
|
||||||
|
# After first phase
|
||||||
|
response = self.client.get(reverse("registration:signup"))
|
||||||
|
self.assertEqual(response.status_code, 403)
|
||||||
|
|
||||||
|
Phase.objects.filter(phase_number__gte=2).update(start=timezone.now() + timedelta(days=1),
|
||||||
|
end=timezone.now() + timedelta(days=2))
|
||||||
|
|
||||||
response = self.client.get(reverse("registration:signup"))
|
response = self.client.get(reverse("registration:signup"))
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ from django.utils.http import urlsafe_base64_decode
|
|||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django.views.generic import CreateView, DetailView, RedirectView, TemplateView, UpdateView, View
|
from django.views.generic import CreateView, DetailView, RedirectView, TemplateView, UpdateView, View
|
||||||
from magic import Magic
|
from magic import Magic
|
||||||
|
from participation.models import Phase
|
||||||
|
|
||||||
from .forms import CoachRegistrationForm, PhotoAuthorizationForm, SignupForm, StudentRegistrationForm, UserForm
|
from .forms import CoachRegistrationForm, PhotoAuthorizationForm, SignupForm, StudentRegistrationForm, UserForm
|
||||||
from .models import StudentRegistration
|
from .models import StudentRegistration
|
||||||
@ -27,6 +28,15 @@ class SignupView(CreateView):
|
|||||||
template_name = "registration/signup.html"
|
template_name = "registration/signup.html"
|
||||||
extra_context = dict(title=_("Sign up"))
|
extra_context = dict(title=_("Sign up"))
|
||||||
|
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
The signup view is available only during the first phase.
|
||||||
|
"""
|
||||||
|
current_phase = Phase.current_phase()
|
||||||
|
if not current_phase or current_phase.phase_number >= 2:
|
||||||
|
raise PermissionDenied(_("You can't register now."))
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data()
|
context = super().get_context_data()
|
||||||
|
|
||||||
|
@ -216,7 +216,7 @@ class Matrix:
|
|||||||
"""
|
"""
|
||||||
client = await cls._get_client()
|
client = await cls._get_client()
|
||||||
resp = await client.room_resolve_alias(room_alias)
|
resp = await client.room_resolve_alias(room_alias)
|
||||||
return resp.room_id if resp else None
|
return resp.room_id if resp and hasattr(resp, "room_id") else None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@async_to_sync
|
@async_to_sync
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% load static i18n static %}
|
{% load static i18n static calendar %}
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
{% get_current_language as LANGUAGE_CODE %}{% get_current_language_bidi as LANGUAGE_BIDI %}
|
{% get_current_language as LANGUAGE_CODE %}{% get_current_language_bidi as LANGUAGE_BIDI %}
|
||||||
@ -126,9 +126,11 @@
|
|||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if not user.is_authenticated %}
|
{% if not user.is_authenticated %}
|
||||||
<li class="nav-item active">
|
{% if 1|current_phase %}
|
||||||
<a class="nav-link" href="{% url "registration:signup" %}"><i class="fas fa-user-plus"></i> {% trans "Register" %}</a>
|
<li class="nav-item active">
|
||||||
</li>
|
<a class="nav-link" href="{% url "registration:signup" %}"><i class="fas fa-user-plus"></i> {% trans "Register" %}</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
<li class="nav-item active">
|
<li class="nav-item active">
|
||||||
<a class="nav-link" href="#" data-toggle="modal" data-target="#loginModal">
|
<a class="nav-link" href="#" data-toggle="modal" data-target="#loginModal">
|
||||||
<i class="fas fa-sign-in-alt"></i> {% trans "Log in" %}
|
<i class="fas fa-sign-in-alt"></i> {% trans "Log in" %}
|
||||||
|
@ -986,7 +986,7 @@ msgstr "Réinitialiser mon mot de passe"
|
|||||||
#: apps/registration/templates/registration/signup.html:5
|
#: apps/registration/templates/registration/signup.html:5
|
||||||
#: apps/registration/templates/registration/signup.html:8
|
#: apps/registration/templates/registration/signup.html:8
|
||||||
#: apps/registration/templates/registration/signup.html:20
|
#: apps/registration/templates/registration/signup.html:20
|
||||||
#: apps/registration/views.py:28
|
#: apps/registration/views.py:29
|
||||||
msgid "Sign up"
|
msgid "Sign up"
|
||||||
msgstr "Inscription"
|
msgstr "Inscription"
|
||||||
|
|
||||||
@ -1063,36 +1063,40 @@ msgid "Update user"
|
|||||||
msgstr "Modifier l'utilisateur"
|
msgstr "Modifier l'utilisateur"
|
||||||
|
|
||||||
#: apps/registration/templates/registration/user_detail.html:77
|
#: apps/registration/templates/registration/user_detail.html:77
|
||||||
#: apps/registration/views.py:206
|
#: apps/registration/views.py:216
|
||||||
msgid "Upload photo authorization"
|
msgid "Upload photo authorization"
|
||||||
msgstr "Téléverser l'autorisation de droit à l'image"
|
msgstr "Téléverser l'autorisation de droit à l'image"
|
||||||
|
|
||||||
#: apps/registration/views.py:64
|
#: apps/registration/views.py:37
|
||||||
|
msgid "You can't register now."
|
||||||
|
msgstr "Vous ne pouvez pas vous inscrire maintenant."
|
||||||
|
|
||||||
|
#: apps/registration/views.py:74
|
||||||
msgid "Email validation"
|
msgid "Email validation"
|
||||||
msgstr "Validation de l'adresse mail"
|
msgstr "Validation de l'adresse mail"
|
||||||
|
|
||||||
#: apps/registration/views.py:66
|
#: apps/registration/views.py:76
|
||||||
msgid "Validate email"
|
msgid "Validate email"
|
||||||
msgstr "Valider l'adresse mail"
|
msgstr "Valider l'adresse mail"
|
||||||
|
|
||||||
#: apps/registration/views.py:105
|
#: apps/registration/views.py:115
|
||||||
msgid "Email validation unsuccessful"
|
msgid "Email validation unsuccessful"
|
||||||
msgstr "Échec de la validation de l'adresse mail"
|
msgstr "Échec de la validation de l'adresse mail"
|
||||||
|
|
||||||
#: apps/registration/views.py:116
|
#: apps/registration/views.py:126
|
||||||
msgid "Email validation email sent"
|
msgid "Email validation email sent"
|
||||||
msgstr "Mail de confirmation de l'adresse mail envoyé"
|
msgstr "Mail de confirmation de l'adresse mail envoyé"
|
||||||
|
|
||||||
#: apps/registration/views.py:124
|
#: apps/registration/views.py:134
|
||||||
msgid "Resend email validation link"
|
msgid "Resend email validation link"
|
||||||
msgstr "Renvoyé le lien de validation de l'adresse mail"
|
msgstr "Renvoyé le lien de validation de l'adresse mail"
|
||||||
|
|
||||||
#: apps/registration/views.py:158
|
#: apps/registration/views.py:168
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Detail of user {user}"
|
msgid "Detail of user {user}"
|
||||||
msgstr "Détails de l'utilisateur {user}"
|
msgstr "Détails de l'utilisateur {user}"
|
||||||
|
|
||||||
#: apps/registration/views.py:179
|
#: apps/registration/views.py:189
|
||||||
#, python-brace-format
|
#, python-brace-format
|
||||||
msgid "Update user {user}"
|
msgid "Update user {user}"
|
||||||
msgstr "Mise à jour de l'utilisateur {user}"
|
msgstr "Mise à jour de l'utilisateur {user}"
|
||||||
|
Loading…
Reference in New Issue
Block a user