diff --git a/club/migrations/0018_clubrole_linked_groups.py b/club/migrations/0018_clubrole_linked_groups.py index b22b1f86..0f1aa4e8 100644 --- a/club/migrations/0018_clubrole_linked_groups.py +++ b/club/migrations/0018_clubrole_linked_groups.py @@ -18,7 +18,7 @@ class Migration(migrations.Migration): "Groups that are automatically given or removed " "to user receiving or losing this club role" ), - related_name="club_roles", + related_name="linked_roles", to="core.group", verbose_name="Linked groups", ), diff --git a/core/migrations/0051_user_eula_approved.py b/core/migrations/0051_user_eula_approved.py new file mode 100644 index 00000000..1477184e --- /dev/null +++ b/core/migrations/0051_user_eula_approved.py @@ -0,0 +1,15 @@ +# Generated by Django 5.2.17 on 2026-09-15 11:02 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [("core", "0050_alter_sithfile_moderator")] + + operations = [ + migrations.AddField( + model_name="user", + name="cgu_approved", + field=models.BooleanField(default=False, verbose_name="EULA approved"), + ), + ] diff --git a/core/models.py b/core/models.py index 208f1937..debceb6c 100644 --- a/core/models.py +++ b/core/models.py @@ -291,6 +291,7 @@ class User(AbstractUser): ), blank=True, ) + cgu_approved = models.BooleanField(_("EULA approved"), default=False) godfathers = models.ManyToManyField("User", related_name="godchildren", blank=True) objects = CustomUserManager() diff --git a/core/static/user/login.scss b/core/static/user/login.scss index 5d506edd..8e135413 100644 --- a/core/static/user/login.scss +++ b/core/static/user/login.scss @@ -49,67 +49,29 @@ body { max-width: 500px; margin-top: 20px; - >p, - >div { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; + input[type="submit"] { width: 100%; + max-width: 300px; margin: 0; - - >label { - width: 100%; - - @media (min-width: 500px) { - width: 300px; - } - } } - >input, - >p>input, - >div>input { - box-sizing: border-box; - width: 100%; - max-width: 500px; - - @media (min-width: 500px) { - max-width: 300px; - } - } - - >.errorlist { + .errorlist { color: red; text-align: center; margin: 10px 0 0 0; list-style-type: none; } - >.required>.helptext { - text-align: center; - font-style: italic; - } - - >.required:last-of-type { - box-sizing: border-box; + div, fieldset { max-width: 300px; - flex-direction: row; - flex-wrap: wrap; - justify-content: space-between; - - >label { - width: 100%; - } + } + .captcha { + box-sizing: border-box; >img { width: 70px; object-fit: contain; } - - >input { - width: 200px; - } } } } diff --git a/core/templates/core/login.jinja b/core/templates/core/login.jinja index a6c3332d..9506ffdd 100644 --- a/core/templates/core/login.jinja +++ b/core/templates/core/login.jinja @@ -50,13 +50,12 @@ - - {# Assumes you setup the password_reset view in your URLconf #} -

+

{% trans %}Lost password?{% endtrans %} -    +
+
{% trans %}Create account{% endtrans %} -

+
{% endblock %} diff --git a/core/templates/core/register.jinja b/core/templates/core/register.jinja index 249de9bf..71ef7f2c 100644 --- a/core/templates/core/register.jinja +++ b/core/templates/core/register.jinja @@ -18,7 +18,17 @@
{% csrf_token %} {% render_honeypot_field %} - {{ form.as_p() }} - + {% for field in form %} + {% if field.name not in ["cgu_approved", "captcha"] %} +
{{ field.as_field_group() }}
+ {% endif %} + {% endfor %} +
{{ form.captcha.as_field_group() }}
+
+ {{ form.cgu_approved.errors }} + {{ form.cgu_approved }} + {{ form.cgu_approved.label_tag() }} +
+
{% endblock %} diff --git a/core/tests/test_core.py b/core/tests/test_core.py index aa19befa..3f065fd1 100644 --- a/core/tests/test_core.py +++ b/core/tests/test_core.py @@ -55,6 +55,7 @@ class TestUserRegistration: "password2": "plop", "captcha_0": "dummy-value", "captcha_1": "PASSED", + "cgu_approved": True, } @pytest.fixture() @@ -92,6 +93,7 @@ class TestUserRegistration: ({"first_name": ""}, "Ce champ est obligatoire."), ({"last_name": ""}, "Ce champ est obligatoire."), ({"captcha_1": "WRONG_CAPTCHA"}, "CAPTCHA invalide"), + ({"cgu_approved": ""}, "Ce champ est obligatoire."), ], ) def test_register_user_form_fail( diff --git a/core/views/forms.py b/core/views/forms.py index cfbeac69..c10a7da6 100644 --- a/core/views/forms.py +++ b/core/views/forms.py @@ -42,6 +42,8 @@ from django.forms import ( TextInput, Widget, ) +from django.urls import reverse +from django.utils.safestring import mark_safe from django.utils.timezone import now from django.utils.translation import gettext_lazy as _ from phonenumber_field.widgets import RegionalPhoneNumberWidget @@ -146,9 +148,25 @@ class RegisteringForm(UserCreationForm): class Meta: model = User - fields = ("first_name", "last_name", "email") + fields = ("first_name", "last_name", "email", "cgu_approved") field_classes = {"email": AntiSpamEmailField} + 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 UserProfileForm(forms.ModelForm): """Form handling the user profile, managing the files""" diff --git a/counter/migrations/0012_auto_20170515_2202.py b/counter/migrations/0012_auto_20170515_2202.py index 2930c505..40edd32f 100644 --- a/counter/migrations/0012_auto_20170515_2202.py +++ b/counter/migrations/0012_auto_20170515_2202.py @@ -11,7 +11,7 @@ class Migration(migrations.Migration): model_name="permanency", name="end", field=models.DateTimeField( - db_index=True, verbose_name="end date", null=True + db_index=True, verbose_name="end date", null=True, blank=True ), ) ] diff --git a/counter/migrations/0019_billinginfo.py b/counter/migrations/0019_billinginfo.py index b348c86a..e914b9be 100644 --- a/counter/migrations/0019_billinginfo.py +++ b/counter/migrations/0019_billinginfo.py @@ -6,9 +6,7 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ("counter", "0018_producttype_priority"), - ] + dependencies = [("counter", "0018_producttype_priority")] operations = [ migrations.AlterModelOptions( diff --git a/counter/migrations/0020_auto_20221215_1709.py b/counter/migrations/0020_auto_20221215_1709.py index 4cf6a95d..498711eb 100644 --- a/counter/migrations/0020_auto_20221215_1709.py +++ b/counter/migrations/0020_auto_20221215_1709.py @@ -6,9 +6,7 @@ import counter.fields class Migration(migrations.Migration): - dependencies = [ - ("counter", "0019_billinginfo"), - ] + dependencies = [("counter", "0019_billinginfo")] operations = [ migrations.AlterField( diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index a7c32468..929aebd7 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-09 07:21+0200\n" +"POT-Creation-Date: 2026-09-15 23:49+0200\n" "PO-Revision-Date: 2016-07-18\n" "Last-Translator: Maréchal \n" @@ -1835,6 +1835,10 @@ msgstr "" "Même si ce profil est caché, les utilisateurs sur cette liste pourront " "toujours le voir." +#: core/models.py +msgid "EULA approved" +msgstr "CGU approuvées" + #: core/models.py msgid "A user with that username already exists" msgstr "Un utilisateur de ce nom d'utilisateur existe déjà" @@ -3137,6 +3141,15 @@ msgstr "Assurez-vous que cet horodatage est dans le futur" msgid "Username, email, or account number" msgstr "Nom d'utilisateur, email, ou numéro de compte AE" +#: core/views/forms.py +#, python-format +msgid "" +"I have read and I approve the End User " +"License Agreement" +msgstr "" +"J'ai lu et j'approuve les Conditions " +"Générales d'utilisation" + #: core/views/forms.py msgid "" "Profile: you need to be visible on the picture, in order to be recognized " @@ -3778,8 +3791,8 @@ msgid "Emptied" msgstr "Coffre vidé" #: counter/templates/counter/cash_summary_list.jinja counter/views/cash.py +#: pedagogy/templates/pedagogy/fragments/ue_comment_form.jinja #: pedagogy/templates/pedagogy/moderation.jinja -#: pedagogy/templates/pedagogy/ue_detail.jinja #: trombi/templates/trombi/comment.jinja #: trombi/templates/trombi/user_tools.jinja msgid "Comment"