Compare commits

..
2 Commits
Author SHA1 Message Date
imperosol 8dbb472865 force CGU/EULA approval on account creation 2026-09-16 09:59:23 +02:00
imperosol 246909376f add CGU/EULA to populate command 2026-09-15 23:39:40 +02:00
14 changed files with 93 additions and 68 deletions
@@ -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",
),
+12 -5
View File
@@ -28,6 +28,7 @@ from typing import ClassVar, NamedTuple
from django.conf import settings
from django.contrib.auth.models import Permission
from django.contrib.sites.models import Site
from django.core.files.base import ContentFile
from django.core.management import call_command
from django.core.management.base import BaseCommand
from django.db import connection
@@ -120,15 +121,21 @@ class Command(BaseCommand):
)
self.profiles_root = SithFile.objects.create(name="profiles", owner=root)
home_root = SithFile.objects.create(name="users", owner=root)
# Page needed for club creation
p = Page(name=settings.SITH_CLUB_ROOT_PAGE)
p.save(force_lock=True)
club_root = SithFile.objects.create(name="clubs", owner=root)
sas = SithFile.objects.create(
name="SAS", owner=root, id=settings.SITH_SAS_ROOT_DIR_ID
)
SithFile.objects.create(
name="CGU",
is_folder=False,
file=ContentFile(
content="Conditions générales d'utilisation", name="cgu.txt"
),
owner=root,
)
# Page needed for club creation
p = Page(name=settings.SITH_CLUB_ROOT_PAGE)
p.save(force_lock=True)
clubs = self._create_clubs()
self.reset_index("club")
@@ -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"),
),
]
+1
View File
@@ -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()
+6 -44
View File
@@ -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%;
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;
}
margin: 0;
}
>.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;
}
}
}
}
+4 -5
View File
@@ -50,13 +50,12 @@
<input type="hidden" name="next" value="{{ next }}">
<input type="submit" value="{% trans %}Login{% endtrans %}">
{# Assumes you setup the password_reset view in your URLconf #}
<p>
<div>
<a href="{{ url('core:password_reset') }}">{% trans %}Lost password?{% endtrans %}</a>
&nbsp;&nbsp;
</div>
<div>
<a href="{{ url('core:register') }}">{% trans %}Create account{% endtrans %}</a>
</p>
</div>
</form>
{% endblock %}
+12 -2
View File
@@ -18,7 +18,17 @@
<form action="{{ url('core:register') }}" method="post">
{% csrf_token %}
{% render_honeypot_field %}
{{ form.as_p() }}
<input type="submit" value="{% trans %}Register{% endtrans %}" />
{% for field in form %}
{% if field.name not in ["cgu_approved", "captcha"] %}
<div>{{ field.as_field_group() }}</div>
{% endif %}
{% endfor %}
<div class="captcha">{{ form.captcha.as_field_group() }}</div>
<fieldset>
{{ form.cgu_approved.errors }}
{{ form.cgu_approved }}
{{ form.cgu_approved.label_tag() }}
</fieldset>
<input type="submit" class="btn btn-blue" value="{% trans %}Register{% endtrans %}" />
</form>
{% endblock %}
+2
View File
@@ -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(
+19 -1
View File
@@ -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 "
'<a href="%(url)s" target="_blank">End User License Agreement</a>'
)
% {
"url": reverse(
"core:download", kwargs={"file_id": settings.SITH_CGU_FILE_ID}
)
}
)
class UserProfileForm(forms.ModelForm):
"""Form handling the user profile, managing the files"""
@@ -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
),
)
]
+1 -3
View File
@@ -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(
@@ -6,9 +6,7 @@ import counter.fields
class Migration(migrations.Migration):
dependencies = [
("counter", "0019_billinginfo"),
]
dependencies = [("counter", "0019_billinginfo")]
operations = [
migrations.AlterField(
+15 -2
View File
@@ -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 <thomas.girod@utbm.fr\n"
"Language-Team: AE info <ae.info@utbm.fr>\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 <a href=\"%(url)s\" target=\"_blank\">End User "
"License Agreement</a>"
msgstr ""
"J'ai lu et j'approuve les <a href=\"%(url)s\" target=\"_blank\">Conditions "
"Générales d'utilisation</a>"
#: 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"
+2
View File
@@ -417,6 +417,8 @@ SITH_FORUM_PAGE_LENGTH = 30
SITH_SAS_ROOT_DIR_ID = env.int("SITH_SAS_ROOT_DIR_ID", default=4)
SITH_SAS_IMAGES_PER_PAGE = 60
SITH_CGU_FILE_ID = env.int("SITH_CGU_FILE_ID", default=5)
SITH_PROFILE_DEPARTMENTS = [
("TC", _("TC")),
("IMSI", _("IMSI")),