mirror of
https://github.com/ae-utbm/sith.git
synced 2026-03-14 23:55:04 +00:00
include visibility settings in the user preferences page
This commit is contained in:
@@ -157,6 +157,7 @@ form {
|
|||||||
margin-bottom: .25rem;
|
margin-bottom: .25rem;
|
||||||
font-size: 80%;
|
font-size: 80%;
|
||||||
display: block;
|
display: block;
|
||||||
|
max-width: calc(100% - calc(var(--nf-input-size) * 2))
|
||||||
}
|
}
|
||||||
|
|
||||||
fieldset {
|
fieldset {
|
||||||
|
|||||||
@@ -5,17 +5,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.profile {
|
.profile {
|
||||||
&-visible {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
gap: 5px;
|
|
||||||
padding-top: 10px;
|
|
||||||
input[type="checkbox"]+label {
|
|
||||||
max-width: unset;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&-pictures {
|
&-pictures {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -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 {
|
.justify {
|
||||||
|
|||||||
@@ -1,14 +1,11 @@
|
|||||||
<div id="quick-notifications"
|
<div id="quick-notifications"
|
||||||
x-data="{
|
x-data="{
|
||||||
messages: [
|
messages: [
|
||||||
{% if messages %}
|
{%- for message in messages -%}
|
||||||
{% for message in messages %}
|
{%- if not message.extra_tags -%}
|
||||||
{
|
{ tag: '{{ message.tags }}', text: '{{ message }}' },
|
||||||
tag: '{{ message.tags }}',
|
{%- endif -%}
|
||||||
text: '{{ message }}',
|
{%- endfor -%}
|
||||||
},
|
|
||||||
{% endfor %}
|
|
||||||
{% endif %}
|
|
||||||
]
|
]
|
||||||
}"
|
}"
|
||||||
@quick-notification-add="(e) => messages.push(e?.detail)"
|
@quick-notification-add="(e) => messages.push(e?.detail)"
|
||||||
|
|||||||
33
core/templates/core/fragment/user_visibility.jinja
Normal file
33
core/templates/core/fragment/user_visibility.jinja
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<form
|
||||||
|
hx-post="{{ url("core:user_visibility_fragment", user_id=form.instance.id) }}"
|
||||||
|
hx-disabled-elt="find input[type='submit']"
|
||||||
|
hx-swap="outerHTML" x-data="{ isViewable: {{ form.is_viewable.value()|tojson }} }"
|
||||||
|
>
|
||||||
|
{% for message in messages %}
|
||||||
|
{% if message.extra_tags=="visibility" %}
|
||||||
|
<div class="alert alert-success">
|
||||||
|
{{ message }}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ form.non_field_errors() }}
|
||||||
|
<fieldset class="form-group">
|
||||||
|
{{ form.is_viewable|add_attr("x-model=isViewable") }}
|
||||||
|
{{ form.is_viewable.label_tag() }}
|
||||||
|
<span class="helptext">{{ form.is_viewable.help_text }}</span>
|
||||||
|
{{ form.is_viewable.errors }}
|
||||||
|
</fieldset>
|
||||||
|
<fieldset class="form-group" x-show="!isViewable">
|
||||||
|
{{ form.whitelisted_users.as_field_group() }}
|
||||||
|
</fieldset>
|
||||||
|
<fieldset class="form-group" x-show="isViewable">
|
||||||
|
{{ form.show_my_stats }}
|
||||||
|
{{ form.show_my_stats.label_tag() }}
|
||||||
|
<span class="helptext">
|
||||||
|
{{ form.show_my_stats.help_text }}
|
||||||
|
</span>
|
||||||
|
{{ form.show_my_stats.errors }}
|
||||||
|
</fieldset>
|
||||||
|
<input type="submit" class="btn btn-blue" value="{% trans %}Save{% endtrans %}">
|
||||||
|
</form>
|
||||||
@@ -147,18 +147,7 @@
|
|||||||
{%- endfor -%}
|
{%- endfor -%}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# Checkboxes #}
|
|
||||||
<div class="profile-visible">
|
|
||||||
<div class="row">
|
|
||||||
{{ form.is_viewable }}
|
|
||||||
{{ form.is_viewable.label_tag() }}
|
|
||||||
</div>
|
|
||||||
<span class="helptext">
|
|
||||||
{{ form.is_viewable.help_text }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="final-actions">
|
<div class="final-actions">
|
||||||
|
|
||||||
{%- if form.instance == user -%}
|
{%- if form.instance == user -%}
|
||||||
<p>
|
<p>
|
||||||
<a href="{{ url('core:password_change') }}">{%- trans -%}Change my password{%- endtrans -%}</a>
|
<a href="{{ url('core:password_change') }}">{%- trans -%}Change my password{%- endtrans -%}</a>
|
||||||
@@ -170,7 +159,6 @@
|
|||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
{%- endif -%}
|
{%- endif -%}
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
<input type="submit" value="{%- trans -%}Update{%- endtrans -%}" />
|
<input type="submit" value="{%- trans -%}Update{%- endtrans -%}" />
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
{% extends "core/base.jinja" %}
|
{% extends "core/base.jinja" %}
|
||||||
|
|
||||||
|
{%- block additional_js -%}
|
||||||
|
<script type="module" src="{{ static("bundled/core/components/ajax-select-index.ts") }}"></script>
|
||||||
|
{%- endblock -%}
|
||||||
|
|
||||||
{%- block additional_css -%}
|
{%- block additional_css -%}
|
||||||
<link rel="stylesheet" href="{{ static('user/user_preferences.scss') }}">
|
<link rel="stylesheet" href="{{ static('user/user_preferences.scss') }}">
|
||||||
|
<link rel="stylesheet" href="{{ static("bundled/core/components/ajax-select-index.css") }}">
|
||||||
|
<link rel="stylesheet" href="{{ static("core/components/ajax-select.scss") }}">
|
||||||
{%- endblock -%}
|
{%- endblock -%}
|
||||||
|
|
||||||
{% block title %}
|
{% block title %}
|
||||||
@@ -11,30 +17,22 @@
|
|||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="main">
|
<div class="main">
|
||||||
<h2>{% trans %}Preferences{% endtrans %}</h2>
|
<h2>{% trans %}Preferences{% endtrans %}</h2>
|
||||||
<h3>{% trans %}General{% endtrans %}</h3>
|
|
||||||
<form class="form form-general" action="" method="post" enctype="multipart/form-data">
|
|
||||||
{% csrf_token %}
|
|
||||||
{{ form.as_p() }}
|
|
||||||
<input class="form-submit-btn" type="submit" value="{% trans %}Save{% endtrans %}" />
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<h3>{% trans %}Trombi{% endtrans %}</h3>
|
|
||||||
|
|
||||||
{% if trombi_form %}
|
|
||||||
<form class="form form-trombi" action="{{ url('trombi:user_tools') }}" method="post" enctype="multipart/form-data">
|
|
||||||
{% csrf_token %}
|
|
||||||
{{ trombi_form.as_p() }}
|
|
||||||
<input class="form-submit-btn" type="submit" value="{% trans %}Save{% endtrans %}" />
|
|
||||||
</form>
|
|
||||||
|
|
||||||
{% else %}
|
|
||||||
<p>{% trans trombi=profile.trombi_user.trombi %}You already choose to be in that Trombi: {{ trombi }}.{% endtrans %}
|
|
||||||
<br />
|
<br />
|
||||||
<a href="{{ url('trombi:user_tools') }}">{% trans %}Go to my Trombi tools{% endtrans %}</a>
|
<h3>{% trans %}Notifications{% endtrans %}</h3>
|
||||||
</p>
|
<form action="" method="post" enctype="multipart/form-data">
|
||||||
{% endif %}
|
{% csrf_token %}
|
||||||
|
<div class="form form-general">
|
||||||
|
{{ form.as_p() }}
|
||||||
|
</div>
|
||||||
|
<input class="btn btn-blue" type="submit" value="{% trans %}Save{% endtrans %}" />
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<h3>{% trans %}Visibility{% endtrans %}</h3>
|
||||||
|
|
||||||
|
{{ user_visibility_fragment }}
|
||||||
|
|
||||||
|
<br />
|
||||||
{% if student_card_fragment %}
|
{% if student_card_fragment %}
|
||||||
<h3>{% trans %}Student card{% endtrans %}</h3>
|
<h3>{% trans %}Student card{% endtrans %}</h3>
|
||||||
{{ student_card_fragment }}
|
{{ 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 %}
|
add a student card yourself, you'll need a NFC reader. We store the UID of the card which is 14 characters long.{% endtrans %}
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<h3>{% trans %}Trombi{% endtrans %}</h3>
|
||||||
|
|
||||||
|
{% if trombi_form %}
|
||||||
|
<form action="{{ url('trombi:user_tools') }}" method="post" enctype="multipart/form-data">
|
||||||
|
{% csrf_token %}
|
||||||
|
{{ trombi_form.as_p() }}
|
||||||
|
<input class="btn btn-blue" type="submit" value="{% trans %}Save{% endtrans %}" />
|
||||||
|
</form>
|
||||||
|
{% else %}
|
||||||
|
<p>{% trans trombi=profile.trombi_user.trombi %}You already choose to be in that Trombi: {{ trombi }}.{% endtrans %}
|
||||||
|
<br />
|
||||||
|
<a href="{{ url('trombi:user_tools') }}">{% trans %}Go to my Trombi tools{% endtrans %}</a>
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -77,6 +77,7 @@ from core.views import (
|
|||||||
UserUpdateGroupView,
|
UserUpdateGroupView,
|
||||||
UserUpdateProfileView,
|
UserUpdateProfileView,
|
||||||
UserView,
|
UserView,
|
||||||
|
UserVisibilityFormFragment,
|
||||||
delete_user_godfather,
|
delete_user_godfather,
|
||||||
logout,
|
logout,
|
||||||
notification,
|
notification,
|
||||||
@@ -135,6 +136,11 @@ urlpatterns = [
|
|||||||
"group/<int:group_id>/detail/", GroupTemplateView.as_view(), name="group_detail"
|
"group/<int:group_id>/detail/", GroupTemplateView.as_view(), name="group_detail"
|
||||||
),
|
),
|
||||||
# User views
|
# User views
|
||||||
|
path(
|
||||||
|
"fragment/user/<int:user_id>/",
|
||||||
|
UserVisibilityFormFragment.as_view(),
|
||||||
|
name="user_visibility_fragment",
|
||||||
|
),
|
||||||
path(
|
path(
|
||||||
"user/me/<path:remaining_path>/",
|
"user/me/<path:remaining_path>/",
|
||||||
UserMeRedirect.as_view(),
|
UserMeRedirect.as_view(),
|
||||||
|
|||||||
@@ -48,12 +48,13 @@ from phonenumber_field.widgets import RegionalPhoneNumberWidget
|
|||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
from antispam.forms import AntiSpamEmailField
|
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.utils import resize_image
|
||||||
from core.views.widgets.ajax_select import (
|
from core.views.widgets.ajax_select import (
|
||||||
AutoCompleteSelect,
|
AutoCompleteSelect,
|
||||||
AutoCompleteSelectGroup,
|
AutoCompleteSelectGroup,
|
||||||
AutoCompleteSelectMultipleGroup,
|
AutoCompleteSelectMultipleGroup,
|
||||||
|
AutoCompleteSelectMultipleUser,
|
||||||
AutoCompleteSelectUser,
|
AutoCompleteSelectUser,
|
||||||
)
|
)
|
||||||
from core.views.widgets.markdown import MarkdownInput
|
from core.views.widgets.markdown import MarkdownInput
|
||||||
@@ -179,7 +180,6 @@ class UserProfileForm(forms.ModelForm):
|
|||||||
"school",
|
"school",
|
||||||
"promo",
|
"promo",
|
||||||
"forum_signature",
|
"forum_signature",
|
||||||
"is_viewable",
|
|
||||||
]
|
]
|
||||||
widgets = {
|
widgets = {
|
||||||
"date_of_birth": SelectDate,
|
"date_of_birth": SelectDate,
|
||||||
@@ -264,6 +264,38 @@ class UserProfileForm(forms.ModelForm):
|
|||||||
self._post_clean()
|
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):
|
class UserGroupsForm(forms.ModelForm):
|
||||||
error_css_class = "error"
|
error_css_class = "error"
|
||||||
required_css_class = "required"
|
required_css_class = "required"
|
||||||
|
|||||||
@@ -28,10 +28,12 @@ from datetime import timedelta
|
|||||||
from operator import itemgetter
|
from operator import itemgetter
|
||||||
from smtplib import SMTPException
|
from smtplib import SMTPException
|
||||||
|
|
||||||
|
from django.contrib import messages
|
||||||
from django.contrib.auth import login, views
|
from django.contrib.auth import login, views
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.contrib.auth.forms import PasswordChangeForm, SetPasswordForm
|
from django.contrib.auth.forms import PasswordChangeForm, SetPasswordForm
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
|
||||||
|
from django.contrib.messages.views import SuccessMessageMixin
|
||||||
from django.core.exceptions import PermissionDenied
|
from django.core.exceptions import PermissionDenied
|
||||||
from django.db.models import DateField, F, QuerySet, Sum
|
from django.db.models import DateField, F, QuerySet, Sum
|
||||||
from django.db.models.functions import Trunc
|
from django.db.models.functions import Trunc
|
||||||
@@ -64,8 +66,9 @@ from core.views.forms import (
|
|||||||
UserGodfathersForm,
|
UserGodfathersForm,
|
||||||
UserGroupsForm,
|
UserGroupsForm,
|
||||||
UserProfileForm,
|
UserProfileForm,
|
||||||
|
UserVisibilityForm,
|
||||||
)
|
)
|
||||||
from core.views.mixins import TabedViewMixin, UseFragmentsMixin
|
from core.views.mixins import FragmentMixin, TabedViewMixin, UseFragmentsMixin
|
||||||
from counter.models import Refilling, Selling
|
from counter.models import Refilling, Selling
|
||||||
from eboutic.models import Invoice
|
from eboutic.models import Invoice
|
||||||
from trombi.views import UserTrombiForm
|
from trombi.views import UserTrombiForm
|
||||||
@@ -460,6 +463,30 @@ class UserClubView(UserTabsMixin, CanViewMixin, DetailView):
|
|||||||
current_tab = "clubs"
|
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):
|
class UserPreferencesView(UserTabsMixin, UseFragmentsMixin, CanEditMixin, UpdateView):
|
||||||
"""Edit a user's preferences."""
|
"""Edit a user's preferences."""
|
||||||
|
|
||||||
@@ -473,7 +500,10 @@ class UserPreferencesView(UserTabsMixin, UseFragmentsMixin, CanEditMixin, Update
|
|||||||
current_tab = "prefs"
|
current_tab = "prefs"
|
||||||
|
|
||||||
def get_form_kwargs(self):
|
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):
|
def get_success_url(self):
|
||||||
return self.request.path
|
return self.request.path
|
||||||
@@ -483,6 +513,9 @@ class UserPreferencesView(UserTabsMixin, UseFragmentsMixin, CanEditMixin, Update
|
|||||||
from counter.views.student_card import StudentCardFormFragment
|
from counter.views.student_card import StudentCardFormFragment
|
||||||
|
|
||||||
res = super().get_fragment_context_data()
|
res = super().get_fragment_context_data()
|
||||||
|
res["user_visibility_fragment"] = UserVisibilityFormFragment.as_fragment()(
|
||||||
|
self.request, user=self.object
|
||||||
|
)
|
||||||
if hasattr(self.object, "customer"):
|
if hasattr(self.object, "customer"):
|
||||||
res["student_card_fragment"] = StudentCardFormFragment.as_fragment()(
|
res["student_card_fragment"] = StudentCardFormFragment.as_fragment()(
|
||||||
self.request, customer=self.object.customer
|
self.request, customer=self.object.customer
|
||||||
|
|||||||
Reference in New Issue
Block a user