diff --git a/core/static/core/forms.scss b/core/static/core/forms.scss
index be876c9b..2abffbba 100644
--- a/core/static/core/forms.scss
+++ b/core/static/core/forms.scss
@@ -157,6 +157,7 @@ form {
margin-bottom: .25rem;
font-size: 80%;
display: block;
+ max-width: calc(100% - calc(var(--nf-input-size) * 2))
}
fieldset {
diff --git a/core/static/user/user_edit.scss b/core/static/user/user_edit.scss
index 20995da6..e428956a 100644
--- a/core/static/user/user_edit.scss
+++ b/core/static/user/user_edit.scss
@@ -5,17 +5,6 @@
}
.profile {
- &-visible {
- display: flex;
- flex-direction: column;
- align-items: center;
- gap: 5px;
- padding-top: 10px;
- input[type="checkbox"]+label {
- max-width: unset;
- }
- }
-
&-pictures {
box-sizing: border-box;
display: flex;
diff --git a/core/static/user/user_preferences.scss b/core/static/user/user_preferences.scss
index c61142d0..ded5e08d 100644
--- a/core/static/user/user_preferences.scss
+++ b/core/static/user/user_preferences.scss
@@ -19,28 +19,6 @@
}
}
}
-
- &-cards,
- &-trombi {
- >p {
- display: flex;
- flex-direction: column;
- align-items: flex-start;
- text-align: justify;
- gap: 5px;
- margin: 0;
-
- >input,
- >select {
- min-width: 300px;
- }
- }
- }
-
- &-submit-btn {
- margin-top: 10px !important;
- max-width: 100px;
- }
}
.justify {
diff --git a/core/templates/core/base/notifications.jinja b/core/templates/core/base/notifications.jinja
index f941a1a6..89fb7aad 100644
--- a/core/templates/core/base/notifications.jinja
+++ b/core/templates/core/base/notifications.jinja
@@ -1,14 +1,11 @@
messages.push(e?.detail)"
diff --git a/core/templates/core/fragment/user_visibility.jinja b/core/templates/core/fragment/user_visibility.jinja
new file mode 100644
index 00000000..f89720d6
--- /dev/null
+++ b/core/templates/core/fragment/user_visibility.jinja
@@ -0,0 +1,33 @@
+
\ No newline at end of file
diff --git a/core/templates/core/user_edit.jinja b/core/templates/core/user_edit.jinja
index 46603562..9cad0dee 100644
--- a/core/templates/core/user_edit.jinja
+++ b/core/templates/core/user_edit.jinja
@@ -147,18 +147,7 @@
{%- endfor -%}
- {# Checkboxes #}
-
-
{%- if form.instance == user -%}
{%- trans -%}Change my password{%- endtrans -%}
@@ -170,7 +159,6 @@
{%- endif -%}
-
diff --git a/core/templates/core/user_preferences.jinja b/core/templates/core/user_preferences.jinja
index d20f708f..0276197e 100644
--- a/core/templates/core/user_preferences.jinja
+++ b/core/templates/core/user_preferences.jinja
@@ -1,7 +1,13 @@
{% extends "core/base.jinja" %}
+{%- block additional_js -%}
+
+{%- endblock -%}
+
{%- block additional_css -%}
+
+
{%- endblock -%}
{% block title %}
@@ -11,30 +17,22 @@
{% block content %}
{% trans %}Preferences{% endtrans %}
-
{% trans %}General{% endtrans %}
-
-
{% trans %}Trombi{% endtrans %}
-
- {% if trombi_form %}
-
-
- {% else %}
-
{% trans trombi=profile.trombi_user.trombi %}You already choose to be in that Trombi: {{ trombi }}.{% endtrans %}
-
- {% trans %}Go to my Trombi tools{% endtrans %}
-
- {% endif %}
+
+
{% trans %}Visibility{% endtrans %}
+ {{ user_visibility_fragment }}
+
{% if student_card_fragment %}
{% trans %}Student card{% endtrans %}
{{ student_card_fragment }}
@@ -43,5 +41,21 @@
add a student card yourself, you'll need a NFC reader. We store the UID of the card which is 14 characters long.{% endtrans %}
{% endif %}
+
+
+
{% trans %}Trombi{% endtrans %}
+
+ {% if trombi_form %}
+
+ {% else %}
+
{% trans trombi=profile.trombi_user.trombi %}You already choose to be in that Trombi: {{ trombi }}.{% endtrans %}
+
+ {% trans %}Go to my Trombi tools{% endtrans %}
+
+ {% endif %}
{% endblock %}
\ No newline at end of file
diff --git a/core/urls.py b/core/urls.py
index e617d6b0..7e4b4e74 100644
--- a/core/urls.py
+++ b/core/urls.py
@@ -77,6 +77,7 @@ from core.views import (
UserUpdateGroupView,
UserUpdateProfileView,
UserView,
+ UserVisibilityFormFragment,
delete_user_godfather,
logout,
notification,
@@ -135,6 +136,11 @@ urlpatterns = [
"group/
/detail/", GroupTemplateView.as_view(), name="group_detail"
),
# User views
+ path(
+ "fragment/user//",
+ UserVisibilityFormFragment.as_view(),
+ name="user_visibility_fragment",
+ ),
path(
"user/me//",
UserMeRedirect.as_view(),
diff --git a/core/views/forms.py b/core/views/forms.py
index f593c1d0..e46daf92 100644
--- a/core/views/forms.py
+++ b/core/views/forms.py
@@ -48,12 +48,13 @@ from phonenumber_field.widgets import RegionalPhoneNumberWidget
from PIL import Image
from antispam.forms import AntiSpamEmailField
-from core.models import Gift, Group, Page, PageRev, SithFile, User
+from core.models import Gift, Group, Page, PageRev, Preferences, SithFile, User
from core.utils import resize_image
from core.views.widgets.ajax_select import (
AutoCompleteSelect,
AutoCompleteSelectGroup,
AutoCompleteSelectMultipleGroup,
+ AutoCompleteSelectMultipleUser,
AutoCompleteSelectUser,
)
from core.views.widgets.markdown import MarkdownInput
@@ -179,7 +180,6 @@ class UserProfileForm(forms.ModelForm):
"school",
"promo",
"forum_signature",
- "is_viewable",
]
widgets = {
"date_of_birth": SelectDate,
@@ -264,6 +264,38 @@ class UserProfileForm(forms.ModelForm):
self._post_clean()
+class UserVisibilityForm(forms.ModelForm):
+ class Meta:
+ model = User
+ fields = ["is_viewable", "whitelisted_users"]
+ widgets = {
+ "is_viewable": forms.CheckboxInput(attrs={"class": "switch"}),
+ "whitelisted_users": AutoCompleteSelectMultipleUser,
+ }
+
+ __preferences_fields = forms.fields_for_model(
+ Preferences,
+ ["show_my_stats"],
+ widgets={"show_my_stats": forms.CheckboxInput(attrs={"class": "switch"})},
+ )
+ show_my_stats = __preferences_fields["show_my_stats"]
+
+ def __init__(
+ self, *args, initial: dict | None = None, instance: User | None = None, **kwargs
+ ):
+ if instance:
+ initial = initial or {}
+ initial["show_my_stats"] = instance.preferences.show_my_stats
+ super().__init__(*args, initial=initial, instance=instance, **kwargs)
+
+ def save(self, commit=True) -> User: # noqa: FBT002
+ instance = super().save(commit=commit)
+ if commit:
+ instance.preferences.show_my_stats = self.cleaned_data["show_my_stats"]
+ instance.preferences.save()
+ return instance
+
+
class UserGroupsForm(forms.ModelForm):
error_css_class = "error"
required_css_class = "required"
diff --git a/core/views/user.py b/core/views/user.py
index 9991aa2f..f0e83edb 100644
--- a/core/views/user.py
+++ b/core/views/user.py
@@ -28,10 +28,12 @@ from datetime import timedelta
from operator import itemgetter
from smtplib import SMTPException
+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.mixins import LoginRequiredMixin, UserPassesTestMixin
+from django.contrib.messages.views import SuccessMessageMixin
from django.core.exceptions import PermissionDenied
from django.db.models import DateField, F, QuerySet, Sum
from django.db.models.functions import Trunc
@@ -64,8 +66,9 @@ from core.views.forms import (
UserGodfathersForm,
UserGroupsForm,
UserProfileForm,
+ UserVisibilityForm,
)
-from core.views.mixins import TabedViewMixin, UseFragmentsMixin
+from core.views.mixins import FragmentMixin, TabedViewMixin, UseFragmentsMixin
from counter.models import Refilling, Selling
from eboutic.models import Invoice
from trombi.views import UserTrombiForm
@@ -460,6 +463,30 @@ class UserClubView(UserTabsMixin, CanViewMixin, DetailView):
current_tab = "clubs"
+class UserVisibilityFormFragment(FragmentMixin, SuccessMessageMixin, UpdateView):
+ model = User
+ form_class = UserVisibilityForm
+ template_name = "core/fragment/user_visibility.jinja"
+ pk_url_kwarg = "user_id"
+
+ def get_form_kwargs(self):
+ return super().get_form_kwargs() | {"label_suffix": ""}
+
+ def form_valid(self, form):
+ response = super().form_valid(form)
+ messages.success(
+ self.request, _("Visibility parameters updated."), extra_tags="visibility"
+ )
+ return response
+
+ def render_fragment(self, request, **kwargs) -> SafeString:
+ self.object = kwargs.get("user")
+ return super().render_fragment(request, **kwargs)
+
+ def get_success_url(self, **kwargs):
+ return self.request.path
+
+
class UserPreferencesView(UserTabsMixin, UseFragmentsMixin, CanEditMixin, UpdateView):
"""Edit a user's preferences."""
@@ -473,7 +500,10 @@ class UserPreferencesView(UserTabsMixin, UseFragmentsMixin, CanEditMixin, Update
current_tab = "prefs"
def get_form_kwargs(self):
- return super().get_form_kwargs() | {"instance": self.object.preferences}
+ return super().get_form_kwargs() | {
+ "instance": self.object.preferences,
+ "label_suffix": "",
+ }
def get_success_url(self):
return self.request.path
@@ -483,6 +513,9 @@ class UserPreferencesView(UserTabsMixin, UseFragmentsMixin, CanEditMixin, Update
from counter.views.student_card import StudentCardFormFragment
res = super().get_fragment_context_data()
+ res["user_visibility_fragment"] = UserVisibilityFormFragment.as_fragment()(
+ self.request, user=self.object
+ )
if hasattr(self.object, "customer"):
res["student_card_fragment"] = StudentCardFormFragment.as_fragment()(
self.request, customer=self.object.customer