diff --git a/core/templates/core/cgu_approve.jinja b/core/templates/core/cgu_approve.jinja
new file mode 100644
index 00000000..a152ef27
--- /dev/null
+++ b/core/templates/core/cgu_approve.jinja
@@ -0,0 +1,39 @@
+{% extends "core/base.jinja" %}
+
+{%- block additional_css -%}
+
+{%- endblock -%}
+
+{% block title %}
+ {% trans %}Login{% endtrans %}
+{% endblock %}
+
+{% block info_boxes %}
+{% endblock %}
+
+{% block nav %}
+{% endblock %}
+
+{% block content %}
+
{% trans %}Terms of Service{% endtrans %}
+
+
+{% endblock %}
+
diff --git a/core/urls.py b/core/urls.py
index d53697a9..0dccdc2e 100644
--- a/core/urls.py
+++ b/core/urls.py
@@ -31,6 +31,7 @@ from core.converters import (
TwoDigitMonthConverter,
)
from core.views import (
+ CGUApprovalView,
FileDeleteView,
FileEditPropView,
FileEditView,
@@ -125,6 +126,7 @@ urlpatterns = [
name="password_reset_complete",
),
path("register/", UserCreationView.as_view(), name="register"),
+ path("cgu/", CGUApprovalView.as_view(), name="approve_cgu"),
# Group handling
path("group/", GroupListView.as_view(), name="group_list"),
path("group/new/", GroupCreateView.as_view(), name="group_new"),
diff --git a/core/views/forms.py b/core/views/forms.py
index c10a7da6..40afa426 100644
--- a/core/views/forms.py
+++ b/core/views/forms.py
@@ -30,9 +30,7 @@ from django import forms
from django.conf import settings
from django.contrib.auth.forms import AuthenticationForm, UserCreationForm
from django.contrib.auth.models import Permission
-from django.contrib.staticfiles.management.commands.collectstatic import (
- staticfiles_storage,
-)
+from django.contrib.staticfiles.storage import staticfiles_storage
from django.core.exceptions import ValidationError
from django.db import transaction
from django.forms import (
@@ -43,6 +41,7 @@ from django.forms import (
Widget,
)
from django.urls import reverse
+from django.utils.functional import lazy
from django.utils.safestring import mark_safe
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _
@@ -110,6 +109,34 @@ class FutureDateTimeField(forms.DateTimeField):
return {"min": widget.format_value(now())}
+class CGUApprovalField(forms.BooleanField):
+ cgu_file_id = settings.SITH_CGU_FILE_ID
+ default_error_messages = {"required": _("You must approve the terms of service.")}
+ __label = None
+
+ def __init__(self, *, label_suffix: str | None = "", **kwargs):
+ # Because the core app of the sith is so huge,
+ # and because we require a url from the latter,
+ # putting the reverse into the __init__ will result in it
+ # being evaluated at server startup time (even with reverse_lazy).
+ # This will result in a circular import.
+ # Thus, we must keep the label in its own property and force it to be lazy.
+ kwargs["label"] = lazy(self.get_label, str)
+ kwargs["required"] = True
+ super().__init__(label_suffix=label_suffix, **kwargs)
+
+ def get_label(self):
+ if not self.__label:
+ self.__label = mark_safe(
+ _(
+ "I have read and I approve the "
+ 'Terms of Service'
+ )
+ % {"url": reverse("core:page", kwargs={"page_name": self.cgu_file_id})}
+ )
+ return self.__label
+
+
# Forms
@@ -149,23 +176,14 @@ class RegisteringForm(UserCreationForm):
class Meta:
model = User
fields = ("first_name", "last_name", "email", "cgu_approved")
- field_classes = {"email": AntiSpamEmailField}
+ field_classes = {"email": AntiSpamEmailField, "cgu_approved": CGUApprovalField}
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.fields["cgu_approved"].required = True
- self.fields["cgu_approved"].label_suffix = ""
- self.fields["cgu_approved"].label = mark_safe(
- _(
- "I have read and I approve the "
- 'End User License Agreement'
- )
- % {
- "url": reverse(
- "core:download", kwargs={"file_id": settings.SITH_CGU_FILE_ID}
- )
- }
- )
+
+class CGUApprovalForm(forms.ModelForm):
+ class Meta:
+ model = User
+ fields = ["cgu_approved"]
+ field_classes = {"cgu_approved": CGUApprovalField}
class UserProfileForm(forms.ModelForm):
diff --git a/core/views/user.py b/core/views/user.py
index 31462551..4e16bf64 100644
--- a/core/views/user.py
+++ b/core/views/user.py
@@ -27,12 +27,12 @@ from datetime import timedelta
# This file contains all the views that concern the user model
from operator import itemgetter
from smtplib import SMTPException
-from typing import TYPE_CHECKING
+from typing import TYPE_CHECKING, Any
from django.contrib import messages
from django.contrib.auth import login, views
from django.contrib.auth.decorators import login_required
-from django.contrib.auth.forms import PasswordChangeForm, SetPasswordForm
+from django.contrib.auth.forms import SetPasswordForm
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.contrib.messages.views import SuccessMessageMixin
from django.core.exceptions import PermissionDenied
@@ -60,6 +60,7 @@ from honeypot.decorators import check_honeypot
from core.auth.mixins import CanEditMixin, CanEditPropMixin, CanViewMixin
from core.models import Gift, Preferences, User
from core.views.forms import (
+ CGUApprovalForm,
GiftForm,
LoginForm,
RegisteringForm,
@@ -71,6 +72,7 @@ from core.views.forms import (
from core.views.mixins import FragmentMixin, TabedViewMixin, UseFragmentsMixin
from counter.models import Refilling, Selling
from eboutic.models import Invoice
+from sith import settings
from trombi.views import UserTrombiForm
if TYPE_CHECKING:
@@ -82,9 +84,16 @@ class SithLoginView(views.LoginView):
template_name = "core/login.jinja"
authentication_form = LoginForm
- form_class = PasswordChangeForm
redirect_authenticated_user = True
+ def get_success_url(self) -> str:
+ redirect_to = self.get_redirect_url()
+ default_url = self.get_default_redirect_url()
+ if not self.request.user.cgu_approved:
+ query = {"next": redirect_to} if redirect_to else {}
+ return reverse("core:approve_cgu", query=query)
+ return redirect_to or default_url
+
class SithPasswordChangeView(views.PasswordChangeView):
"""Allows a user to change its password."""
@@ -188,6 +197,27 @@ class UserCreationView(FormView):
return super().form_valid(form)
+class CGUApprovalView(views.RedirectURLMixin, UpdateView):
+ form_class = CGUApprovalForm
+ next_page = settings.LOGIN_REDIRECT_URL
+ template_name = "core/cgu_approve.jinja"
+
+ def dispatch(self, request, *args, **kwargs):
+ if self.request.user.is_anonymous:
+ return redirect("core:login")
+ if self.request.user.cgu_approved:
+ return redirect(self.get_success_url())
+ return super().dispatch(request, *args, **kwargs)
+
+ def get_object(self, *args, **kwargs):
+ return self.request.user
+
+ def get_context_data(self, **kwargs) -> dict[str, Any]:
+ return super().get_context_data(**kwargs) | {
+ self.redirect_field_name: self.get_redirect_url()
+ }
+
+
class UserMeRedirect(LoginRequiredMixin, RedirectView):
def get_redirect_url(self, *args, **kwargs):
if remaining := kwargs.get("remaining_path"):
diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po
index 1ed6df60..31520888 100644
--- a/locale/fr/LC_MESSAGES/django.po
+++ b/locale/fr/LC_MESSAGES/django.po
@@ -6,7 +6,7 @@
msgid ""
msgstr ""
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2026-09-15 23:49+0200\n"
+"POT-Creation-Date: 2026-09-21 13:16+0200\n"
"PO-Revision-Date: 2016-07-18\n"
"Last-Translator: Maréchal \n"
@@ -736,8 +736,9 @@ msgstr "Méthode de paiement"
#: counter/templates/counter/fragments/create_student_card.jinja
#: counter/templates/counter/last_ops.jinja
#: election/templates/election/election_detail.jinja
-#: forum/templates/forum/macros.jinja pedagogy/templates/pedagogy/guide.jinja
-#: pedagogy/templates/pedagogy/ue_detail.jinja sas/templates/sas/album.jinja
+#: forum/templates/forum/macros.jinja
+#: pedagogy/templates/pedagogy/fragments/ue_details/comments.jinja
+#: pedagogy/templates/pedagogy/guide.jinja sas/templates/sas/album.jinja
#: sas/templates/sas/moderation.jinja sas/templates/sas/picture.jinja
#: trombi/templates/trombi/detail.jinja
#: trombi/templates/trombi/edit_profile.jinja
@@ -931,8 +932,9 @@ msgstr "Outils"
#: counter/templates/counter/cash_summary_list.jinja
#: counter/templates/counter/counter_list.jinja
#: election/templates/election/election_detail.jinja
-#: forum/templates/forum/macros.jinja pedagogy/templates/pedagogy/guide.jinja
-#: pedagogy/templates/pedagogy/ue_detail.jinja sas/templates/sas/album.jinja
+#: forum/templates/forum/macros.jinja
+#: pedagogy/templates/pedagogy/fragments/ue_details/comments.jinja
+#: pedagogy/templates/pedagogy/guide.jinja sas/templates/sas/album.jinja
#: trombi/templates/trombi/detail.jinja
#: trombi/templates/trombi/edit_profile.jinja
msgid "Edit"
@@ -2113,7 +2115,8 @@ msgstr "R&D"
msgid "Site created by the IT Department of the AE"
msgstr "Site réalisé par le Pôle Informatique de l'AE"
-#: core/templates/core/base/header.jinja core/templates/core/login.jinja
+#: core/templates/core/base/header.jinja core/templates/core/cgu_approve.jinja
+#: core/templates/core/login.jinja
#: core/templates/core/password_reset_complete.jinja
msgid "Login"
msgstr "Connexion"
@@ -2211,6 +2214,18 @@ msgstr "FAQ"
msgid "Wiki"
msgstr "Wiki"
+#: core/templates/core/cgu_approve.jinja
+msgid "Terms of Service"
+msgstr "Conditions générales d'utilisation"
+
+#: core/templates/core/cgu_approve.jinja
+msgid ""
+"To continue using our services, please read and approve the AE website's "
+"terms of service"
+msgstr ""
+"Pour continuer à utiliser nos services, veuillez lire et approuver les "
+"conditions générales d'utilisation du site AE."
+
#: core/templates/core/create.jinja
#, python-format
msgid "Create %(name)s"
@@ -2237,6 +2252,7 @@ msgstr "Confirmation"
#: core/templates/core/file_delete_confirm.jinja
#: counter/templates/counter/counter_click.jinja
#: counter/templates/counter/fragments/delete_student_card.jinja
+#: pedagogy/templates/pedagogy/fragments/comment_report.jinja
#: sas/templates/sas/ask_picture_removal.jinja
msgid "Cancel"
msgstr "Annuler"
@@ -2263,6 +2279,7 @@ msgstr "Propriétés"
#: core/templates/core/file_delete_confirm.jinja
#: counter/templates/counter/fragments/delete_student_card.jinja
+#: pedagogy/templates/pedagogy/fragments/ue_details/comments.jinja
#, python-format
msgid "Are you sure you want to delete \"%(obj)s\"?"
msgstr "Êtes-vous sûr de vouloir supprimer \"%(obj)s\" ?"
@@ -3138,18 +3155,22 @@ msgid "Ensure this timestamp is set in the future"
msgstr "Assurez-vous que cet horodatage est dans le futur"
#: core/views/forms.py
-msgid "Username, email, or account number"
-msgstr "Nom d'utilisateur, email, ou numéro de compte AE"
+msgid "You must approve the terms of service."
+msgstr "Vous devez approuver les conditions générales d'utilisation"
#: core/views/forms.py
#, python-format
msgid ""
-"I have read and I approve the End User "
-"License Agreement"
+"I have read and I approve the Terms of "
+"Service"
msgstr ""
"J'ai lu et j'approuve les Conditions "
"Générales d'utilisation"
+#: core/views/forms.py
+msgid "Username, email, or account number"
+msgstr "Nom d'utilisateur, email, ou numéro de compte AE"
+
#: core/views/forms.py
msgid ""
"Profile: you need to be visible on the picture, in order to be recognized "
@@ -5122,6 +5143,80 @@ msgstr "signaler"
msgid "reporter"
msgstr "signalant"
+#: pedagogy/templates/pedagogy/fragments/comment_report.jinja
+msgid "Report"
+msgstr "Signaler"
+
+#: pedagogy/templates/pedagogy/fragments/ue_comment_form.jinja
+msgid "Leave comment"
+msgstr "Laisser un commentaire"
+
+#: pedagogy/templates/pedagogy/fragments/ue_details/comments.jinja
+#: trombi/templates/trombi/export.jinja
+msgid "Comments"
+msgstr "Commentaires"
+
+#: pedagogy/templates/pedagogy/fragments/ue_details/comments.jinja
+#: pedagogy/templates/pedagogy/fragments/ue_details/grade.jinja
+msgid "Global grade"
+msgstr "Note globale"
+
+#: pedagogy/templates/pedagogy/fragments/ue_details/comments.jinja
+#: pedagogy/templates/pedagogy/fragments/ue_details/grade.jinja
+msgid "Utility"
+msgstr "Utilité"
+
+#: pedagogy/templates/pedagogy/fragments/ue_details/comments.jinja
+#: pedagogy/templates/pedagogy/fragments/ue_details/grade.jinja
+msgid "Interest"
+msgstr "Intérêt"
+
+#: pedagogy/templates/pedagogy/fragments/ue_details/comments.jinja
+#: pedagogy/templates/pedagogy/fragments/ue_details/grade.jinja
+msgid "Teaching"
+msgstr "Enseignement"
+
+#: pedagogy/templates/pedagogy/fragments/ue_details/comments.jinja
+#: pedagogy/templates/pedagogy/fragments/ue_details/grade.jinja
+msgid "Work load"
+msgstr "Charge de travail"
+
+#: pedagogy/templates/pedagogy/fragments/ue_details/comments.jinja
+msgid "This comment has been reported"
+msgstr "Ce commentaire a été signalé"
+
+#: pedagogy/templates/pedagogy/fragments/ue_details/comments.jinja
+msgid "Report this comment"
+msgstr "Signaler ce commentaire"
+
+#: pedagogy/templates/pedagogy/fragments/ue_details/form.jinja
+msgid ""
+"You already posted a comment on this UE. If you want to comment again, "
+"please modify or delete your previous comment."
+msgstr ""
+"Vous avez déjà commenté cette UE. Si vous voulez de nouveau commenter, "
+"veuillez modifier ou supprimer votre commentaire précédent."
+
+#: pedagogy/templates/pedagogy/fragments/ue_details/grade.jinja
+msgid "Objectives"
+msgstr "Objectifs"
+
+#: pedagogy/templates/pedagogy/fragments/ue_details/grade.jinja
+msgid "Program"
+msgstr "Programme"
+
+#: pedagogy/templates/pedagogy/fragments/ue_details/grade.jinja
+msgid "Earned skills"
+msgstr "Compétences acquises"
+
+#: pedagogy/templates/pedagogy/fragments/ue_details/grade.jinja
+msgid "Key concepts"
+msgstr "Concepts clefs"
+
+#: pedagogy/templates/pedagogy/fragments/ue_details/grade.jinja
+msgid "UE manager: "
+msgstr "Gestionnaire d'UE : "
+
#: pedagogy/templates/pedagogy/guide.jinja
msgid "A guide of courses available at UTBM."
msgstr "Un guide de tous les cours disponibles à l'UTBM."
@@ -5130,6 +5225,10 @@ msgstr "Un guide de tous les cours disponibles à l'UTBM."
msgid "Search UE"
msgstr "Recherche d'UE"
+#: pedagogy/templates/pedagogy/guide.jinja
+msgid "Hide closed UEs"
+msgstr "Cacher les UEs fermées"
+
#: pedagogy/templates/pedagogy/guide.jinja
#, python-format
msgid "%(display_name)s"
@@ -5202,71 +5301,6 @@ msgstr "TE : "
msgid "THE: "
msgstr "THE : "
-#: pedagogy/templates/pedagogy/ue_detail.jinja
-msgid "Global grade"
-msgstr "Note globale"
-
-#: pedagogy/templates/pedagogy/ue_detail.jinja
-msgid "Utility"
-msgstr "Utilité"
-
-#: pedagogy/templates/pedagogy/ue_detail.jinja
-msgid "Interest"
-msgstr "Intérêt"
-
-#: pedagogy/templates/pedagogy/ue_detail.jinja
-msgid "Teaching"
-msgstr "Enseignement"
-
-#: pedagogy/templates/pedagogy/ue_detail.jinja
-msgid "Work load"
-msgstr "Charge de travail"
-
-#: pedagogy/templates/pedagogy/ue_detail.jinja
-msgid "Objectives"
-msgstr "Objectifs"
-
-#: pedagogy/templates/pedagogy/ue_detail.jinja
-msgid "Program"
-msgstr "Programme"
-
-#: pedagogy/templates/pedagogy/ue_detail.jinja
-msgid "Earned skills"
-msgstr "Compétences acquises"
-
-#: pedagogy/templates/pedagogy/ue_detail.jinja
-msgid "Key concepts"
-msgstr "Concepts clefs"
-
-#: pedagogy/templates/pedagogy/ue_detail.jinja
-msgid "UE manager: "
-msgstr "Gestionnaire d'UE : "
-
-#: pedagogy/templates/pedagogy/ue_detail.jinja pedagogy/tests/tests.py
-msgid ""
-"You already posted a comment on this UE. If you want to comment again, "
-"please modify or delete your previous comment."
-msgstr ""
-"Vous avez déjà commenté cette UE. Si vous voulez de nouveau commenter, "
-"veuillez modifier ou supprimer votre commentaire précédent."
-
-#: pedagogy/templates/pedagogy/ue_detail.jinja
-msgid "Leave comment"
-msgstr "Laisser un commentaire"
-
-#: pedagogy/templates/pedagogy/ue_detail.jinja
-#: trombi/templates/trombi/export.jinja
-msgid "Comments"
-msgstr "Commentaires"
-
-#: pedagogy/templates/pedagogy/ue_detail.jinja
-msgid "This comment has been reported"
-msgstr "Ce commentaire a été signalé"
-
-#: pedagogy/templates/pedagogy/ue_detail.jinja
-msgid "Report this comment"
-msgstr "Signaler ce commentaire"
-
#: pedagogy/templates/pedagogy/ue_edit.jinja
msgid "Edit UE"
msgstr "Éditer l'UE"