diff --git a/com/views.py b/com/views.py
index eae9bf6a..72b4cc41 100644
--- a/com/views.py
+++ b/com/views.py
@@ -243,7 +243,7 @@ class NewsListView(TemplateView):
User.objects.filter(
date_of_birth__month=localdate().month,
date_of_birth__day=localdate().day,
- is_subscriber_viewable=True,
+ is_viewable=True,
)
.filter(role__in=["STUDENT", "FORMER STUDENT"])
.order_by("-date_of_birth"),
diff --git a/core/migrations/0048_alter_user_options.py b/core/migrations/0048_alter_user_options.py
new file mode 100644
index 00000000..f446273a
--- /dev/null
+++ b/core/migrations/0048_alter_user_options.py
@@ -0,0 +1,33 @@
+# Generated by Django 5.2.8 on 2025-11-09 15:20
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ dependencies = [("core", "0047_alter_notification_date_alter_notification_type")]
+
+ operations = [
+ migrations.AlterModelOptions(
+ name="user",
+ options={
+ "permissions": [("view_hidden_user", "Can view hidden users")],
+ "verbose_name": "user",
+ "verbose_name_plural": "users",
+ },
+ ),
+ migrations.RenameField(
+ model_name="user", old_name="is_subscriber_viewable", new_name="is_viewable"
+ ),
+ migrations.AlterField(
+ model_name="user",
+ name="is_viewable",
+ field=models.BooleanField(
+ default=True,
+ verbose_name="Profile visible by subscribers",
+ help_text=(
+ "If you disable this option, only admin users "
+ "will be able to see your profile."
+ ),
+ ),
+ ),
+ ]
diff --git a/core/models.py b/core/models.py
index bf54a33a..3c53419b 100644
--- a/core/models.py
+++ b/core/models.py
@@ -315,13 +315,24 @@ class User(AbstractUser):
parent_address = models.CharField(
_("parent address"), max_length=128, blank=True, default=""
)
- is_subscriber_viewable = models.BooleanField(
- _("is subscriber viewable"), default=True
+ is_viewable = models.BooleanField(
+ _("Profile visible by subscribers"),
+ help_text=_(
+ "If you disable this option, only admin users "
+ "will be able to see your profile."
+ ),
+ default=True,
)
godfathers = models.ManyToManyField("User", related_name="godchildren", blank=True)
objects = CustomUserManager()
+ class Meta(AbstractUser.Meta):
+ abstract = False
+ permissions = [
+ ("view_hidden_user", "Can view hidden users"),
+ ]
+
def __str__(self):
return self.get_display_name()
@@ -604,8 +615,12 @@ class User(AbstractUser):
def can_be_edited_by(self, user):
return user.is_root or user.is_board_member
- def can_be_viewed_by(self, user):
- return (user.was_subscribed and self.is_subscriber_viewable) or user.is_root
+ def can_be_viewed_by(self, user: User) -> bool:
+ return (
+ user.id == self.id
+ or user.has_perm("core.view_hidden_user")
+ or (user.has_perm("core.view_user") and self.is_viewable)
+ )
def get_mini_item(self):
return """
diff --git a/core/static/user/user_edit.scss b/core/static/user/user_edit.scss
index 888ae729..5b20fcee 100644
--- a/core/static/user/user_edit.scss
+++ b/core/static/user/user_edit.scss
@@ -7,10 +7,13 @@
.profile {
&-visible {
display: flex;
- justify-content: center;
+ flex-direction: column;
align-items: center;
gap: 5px;
padding-top: 10px;
+ input[type="checkbox"]+label {
+ max-width: unset;
+ }
}
&-pictures {
@@ -116,23 +119,19 @@
display: flex;
flex-direction: row;
flex-wrap: wrap;
- gap: 10px;
+ gap: var(--nf-input-size) 10px;
justify-content: center;
}
&-field {
display: flex;
- flex-direction: row;
- align-items: center;
flex-wrap: wrap;
justify-content: center;
- gap: 10px;
width: 100%;
max-width: 330px;
min-width: 300px;
@media (max-width: 750px) {
- gap: 4px;
max-width: 100%;
}
@@ -145,22 +144,6 @@
}
}
- &-label {
- text-align: left !important;
- }
-
- &-content {
- > * {
- box-sizing: border-box;
- text-align: left !important;
- margin: 0;
-
- > * {
- text-align: left !important;
- }
- }
- }
-
textarea {
height: 7rem;
}
diff --git a/core/templates/core/user_edit.jinja b/core/templates/core/user_edit.jinja
index 8d015467..2f069da7 100644
--- a/core/templates/core/user_edit.jinja
+++ b/core/templates/core/user_edit.jinja
@@ -116,12 +116,12 @@
{# All fields #}
{%- for field in form -%}
- {%- if field.name in ["quote","profile_pict","avatar_pict","scrub_pict","is_subscriber_viewable","forum_signature"] -%}
+ {%- if field.name in ["quote","profile_pict","avatar_pict","scrub_pict","is_viewable","forum_signature"] -%}
{%- continue -%}
{%- endif -%}
-
{{ field.label }}
+ {{ field.label_tag() }}
{{ field }}
{%- if field.errors -%}
@@ -136,7 +136,7 @@
{%- for field in [form.quote, form.forum_signature] -%}
-
{{ field.label }}
+ {{ field.label_tag() }}
{{ field }}
{%- if field.errors -%}
@@ -149,8 +149,13 @@
{# Checkboxes #}
- {{ form.is_subscriber_viewable }}
- {{ form.is_subscriber_viewable.label }}
+
+ {{ form.is_viewable }}
+ {{ form.is_viewable.label_tag() }}
+
+
+ {{ form.is_viewable.help_text }}
+
diff --git a/core/tests/test_family.py b/core/tests/test_family.py
index 795de590..f64e103f 100644
--- a/core/tests/test_family.py
+++ b/core/tests/test_family.py
@@ -55,7 +55,7 @@ class TestFetchFamilyApi(TestCase):
assert response.status_code == 403
def test_fetch_family_hidden_user(self):
- self.main_user.is_subscriber_viewable = False
+ self.main_user.is_viewable = False
self.main_user.save()
for user_to_login, error_code in [
(self.main_user, 200),
diff --git a/core/views/forms.py b/core/views/forms.py
index fdfe61cf..a7d0589c 100644
--- a/core/views/forms.py
+++ b/core/views/forms.py
@@ -202,7 +202,7 @@ class UserProfileForm(forms.ModelForm):
"school",
"promo",
"forum_signature",
- "is_subscriber_viewable",
+ "is_viewable",
]
widgets = {
"date_of_birth": SelectDate,
@@ -211,8 +211,8 @@ class UserProfileForm(forms.ModelForm):
"quote": forms.Textarea,
}
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
+ def __init__(self, *args, label_suffix: str = "", **kwargs):
+ super().__init__(*args, label_suffix=label_suffix, **kwargs)
# Image fields are injected here to override the file field provided by the model
# This would be better if we could have a SithImage sort of model input instead of a generic SithFile
diff --git a/election/templates/election/election_detail.jinja b/election/templates/election/election_detail.jinja
index b93ab9b7..b450e2c0 100644
--- a/election/templates/election/election_detail.jinja
+++ b/election/templates/election/election_detail.jinja
@@ -141,7 +141,7 @@
{%- endif %}
- {%- if user.is_subscriber_viewable %}
+ {%- if user.is_viewable %}
{% if candidature.user.profile_pict %}
{% else %}
diff --git a/galaxy/models.py b/galaxy/models.py
index 94380d6c..235818a5 100644
--- a/galaxy/models.py
+++ b/galaxy/models.py
@@ -199,7 +199,7 @@ class Galaxy(models.Model):
cls, picture_count_threshold: int = DEFAULT_PICTURE_COUNT_THRESHOLD
) -> QuerySet[User]:
return (
- User.objects.filter(is_subscriber_viewable=True)
+ User.objects.filter(is_viewable=True)
.exclude(subscriptions=None)
.annotate(
pictures_count=Count("pictures"),
diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po
index 101b01d5..3aa23a72 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: 2025-11-07 14:50+0100\n"
+"POT-Creation-Date: 2025-11-09 18:03+0100\n"
"PO-Revision-Date: 2016-07-18\n"
"Last-Translator: Maréchal \n"
@@ -1532,8 +1532,15 @@ msgid "parent address"
msgstr "adresse des parents"
#: core/models.py
-msgid "is subscriber viewable"
-msgstr "profil visible par les cotisants"
+msgid "Profile visible by subscribers"
+msgstr "Profil visible par les cotisants"
+
+#: core/models.py
+msgid ""
+"If you disable this option, only admin users will be able to see your "
+"profile."
+msgstr ""
+"Si vous désactivez cette option, seuls les admins pourront voir votre profil."
#: core/models.py
msgid "A user with that username already exists"
@@ -5112,14 +5119,6 @@ msgstr "Membre de Sbarro ou de l'ESTA"
msgid "One semester Welcome Week"
msgstr "Un semestre Welcome Week"
-#: sith/settings.py
-msgid "One month for free"
-msgstr "Un mois gratuit"
-
-#: sith/settings.py
-msgid "Two months for free"
-msgstr "Deux mois gratuits"
-
#: sith/settings.py
msgid "Eurok's volunteer"
msgstr "Bénévole Eurockéennes"
@@ -5133,7 +5132,9 @@ msgid "One day"
msgstr "Un jour"
#: sith/settings.py
-msgid "GA staff member (2 weeks)"
+#, fuzzy
+#| msgid "GA staff member (2 weeks)"
+msgid "GA staff member"
msgstr "Membre staff GA (2 semaines)"
#: sith/settings.py
@@ -5677,3 +5678,12 @@ msgstr "Vous ne pouvez plus écrire de commentaires, la date est passée."
#, python-format
msgid "Maximum characters: %(max_length)s"
msgstr "Nombre de caractères max: %(max_length)s"
+
+#~ msgid "is viewable"
+#~ msgstr "profil visible"
+
+#~ msgid "One month for free"
+#~ msgstr "Un mois gratuit"
+
+#~ msgid "Two months for free"
+#~ msgstr "Deux mois gratuits"
diff --git a/matmat/views.py b/matmat/views.py
index 1f037234..30eb9541 100644
--- a/matmat/views.py
+++ b/matmat/views.py
@@ -105,7 +105,7 @@ class SearchFormListView(FormerSubscriberMixin, SingleObjectMixin, ListView):
self.can_see_hidden = True
if not (request.user.is_board_member or request.user.is_root):
self.can_see_hidden = False
- self.init_query = self.init_query.exclude(is_subscriber_viewable=False)
+ self.init_query = self.init_query.filter(is_viewable=True)
return super().dispatch(request, *args, **kwargs)
@@ -130,7 +130,7 @@ class SearchFormListView(FormerSubscriberMixin, SingleObjectMixin, ListView):
else:
q = []
if not self.can_see_hidden and len(q) > 0:
- q = [user for user in q if user.is_subscriber_viewable]
+ q = [user for user in q if user.is_viewable]
else:
search_dict = {}
for key, value in self.valid_form.items():
diff --git a/sas/models.py b/sas/models.py
index 0e8d5b88..04061fa2 100644
--- a/sas/models.py
+++ b/sas/models.py
@@ -270,9 +270,7 @@ class PeoplePictureRelationQuerySet(models.QuerySet):
if user.is_root or user.is_in_group(pk=settings.SITH_GROUP_SAS_ADMIN_ID):
return self
if user.was_subscribed:
- return self.filter(
- Q(user_id=user.id) | Q(user__is_subscriber_viewable=True)
- )
+ return self.filter(Q(user_id=user.id) | Q(user__is_viewable=True))
return self.filter(user_id=user.id)
diff --git a/sas/tests/test_api.py b/sas/tests/test_api.py
index 1c5bb5ac..a22ab0ed 100644
--- a/sas/tests/test_api.py
+++ b/sas/tests/test_api.py
@@ -189,7 +189,7 @@ class TestPictureRelation(TestSas):
def test_fetch_relations_including_hidden_users(self):
"""Test that normal subscribers users cannot see hidden profiles"""
picture = self.album_a.children_pictures.last()
- self.user_a.is_subscriber_viewable = False
+ self.user_a.is_viewable = False
self.user_a.save()
url = reverse("api:picture_identifications", kwargs={"picture_id": picture.id})
diff --git a/sas/tests/test_model.py b/sas/tests/test_model.py
index 5a5c5fe8..537d7fd7 100644
--- a/sas/tests/test_model.py
+++ b/sas/tests/test_model.py
@@ -53,7 +53,7 @@ def test_identifications_viewable_by_user():
identifications = baker.make(
PeoplePictureRelation, picture=picture, _quantity=10, _bulk_create=True
)
- identifications[0].user.is_subscriber_viewable = False
+ identifications[0].user.is_viewable = False
identifications[0].user.save()
assert (