mirror of
https://github.com/ae-utbm/sith.git
synced 2026-03-25 04:55:01 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f08e343e17 |
@@ -244,9 +244,8 @@ class NewsListView(TemplateView):
|
||||
.filter(
|
||||
date_of_birth__month=localdate().month,
|
||||
date_of_birth__day=localdate().day,
|
||||
is_viewable=True,
|
||||
role__in=["STUDENT", "FORMER STUDENT"],
|
||||
)
|
||||
.filter(role__in=["STUDENT", "FORMER STUDENT"])
|
||||
.order_by("-date_of_birth"),
|
||||
key=lambda u: u.date_of_birth.year,
|
||||
)
|
||||
|
||||
@@ -63,6 +63,7 @@ class UserAdmin(admin.ModelAdmin):
|
||||
"scrub_pict",
|
||||
"user_permissions",
|
||||
"groups",
|
||||
"whitelisted_users",
|
||||
)
|
||||
inlines = (UserBanInline,)
|
||||
search_fields = ["first_name", "last_name", "username"]
|
||||
|
||||
24
core/migrations/0049_user_whitelisted_users.py
Normal file
24
core/migrations/0049_user_whitelisted_users.py
Normal file
@@ -0,0 +1,24 @@
|
||||
# Generated by Django 5.2.12 on 2026-03-14 08:39
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [("core", "0048_alter_user_options")]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="user",
|
||||
name="whitelisted_users",
|
||||
field=models.ManyToManyField(
|
||||
help_text=(
|
||||
"If this profile is hidden, "
|
||||
"the users in this list will still be able to see it."
|
||||
),
|
||||
related_name="visible_by_whitelist",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
verbose_name="whitelisted users",
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -131,7 +131,7 @@ class UserQuerySet(models.QuerySet):
|
||||
if user.has_perm("core.view_hidden_user"):
|
||||
return self
|
||||
if user.has_perm("core.view_user"):
|
||||
return self.filter(is_viewable=True)
|
||||
return self.filter(Q(is_viewable=True) | Q(whitelisted_users=user))
|
||||
if user.is_anonymous:
|
||||
return self.none()
|
||||
return self.filter(id=user.id)
|
||||
@@ -279,6 +279,15 @@ class User(AbstractUser):
|
||||
),
|
||||
default=True,
|
||||
)
|
||||
whitelisted_users = models.ManyToManyField(
|
||||
"User",
|
||||
related_name="visible_by_whitelist",
|
||||
verbose_name=_("whitelisted users"),
|
||||
help_text=_(
|
||||
"If this profile is hidden, "
|
||||
"the users in this list will still be able to see it."
|
||||
),
|
||||
)
|
||||
godfathers = models.ManyToManyField("User", related_name="godchildren", blank=True)
|
||||
|
||||
objects = CustomUserManager()
|
||||
@@ -567,10 +576,31 @@ class User(AbstractUser):
|
||||
return user.is_root or user.is_board_member
|
||||
|
||||
def can_be_viewed_by(self, user: User) -> bool:
|
||||
"""Check if the given user can be viewed by this user.
|
||||
|
||||
Given users A and B. A can be viewed by B if :
|
||||
|
||||
- A and B are the same user
|
||||
- or B has the permission to view hidden users
|
||||
- or B can view users in general and A didn't hide its profile
|
||||
- or B is in A's whitelist.
|
||||
"""
|
||||
|
||||
def is_in_whitelist(u: User):
|
||||
if (
|
||||
hasattr(self, "_prefetched_objects_cache")
|
||||
and "whitelisted_users" in self._prefetched_objects_cache
|
||||
):
|
||||
return u in self.whitelisted_users.all()
|
||||
return self.whitelisted_users.contains(u)
|
||||
|
||||
return (
|
||||
user.id == self.id
|
||||
or user.has_perm("core.view_hidden_user")
|
||||
or (user.has_perm("core.view_user") and self.is_viewable)
|
||||
or (
|
||||
user.has_perm("core.view_user")
|
||||
and (self.is_viewable or is_in_whitelist(user))
|
||||
)
|
||||
)
|
||||
|
||||
def get_mini_item(self):
|
||||
|
||||
@@ -399,13 +399,12 @@ class TestUserQuerySetViewableBy:
|
||||
return [
|
||||
baker.make(User),
|
||||
subscriber_user.make(),
|
||||
subscriber_user.make(is_viewable=False),
|
||||
*subscriber_user.make(is_viewable=False, _quantity=2),
|
||||
]
|
||||
|
||||
def test_admin_user(self, users: list[User]):
|
||||
user = baker.make(
|
||||
User,
|
||||
user_permissions=[Permission.objects.get(codename="view_hidden_user")],
|
||||
User, user_permissions=[Permission.objects.get(codename="view_hidden_user")]
|
||||
)
|
||||
viewable = User.objects.filter(id__in=[u.id for u in users]).viewable_by(user)
|
||||
assert set(viewable) == set(users)
|
||||
@@ -418,6 +417,12 @@ class TestUserQuerySetViewableBy:
|
||||
viewable = User.objects.filter(id__in=[u.id for u in users]).viewable_by(user)
|
||||
assert set(viewable) == {users[0], users[1]}
|
||||
|
||||
def test_whitelist(self, users: list[User]):
|
||||
user = subscriber_user.make()
|
||||
users[3].whitelisted_users.add(user)
|
||||
viewable = User.objects.filter(id__in=[u.id for u in users]).viewable_by(user)
|
||||
assert set(viewable) == {users[0], users[1], users[3]}
|
||||
|
||||
@pytest.mark.parametrize("user_factory", [lambda: baker.make(User), AnonymousUser])
|
||||
def test_not_subscriber(self, users: list[User], user_factory):
|
||||
user = user_factory()
|
||||
|
||||
@@ -69,7 +69,6 @@ from core.views import (
|
||||
UserCreationView,
|
||||
UserGodfathersTreeView,
|
||||
UserGodfathersView,
|
||||
UserListView,
|
||||
UserMeRedirect,
|
||||
UserMiniView,
|
||||
UserPreferencesView,
|
||||
@@ -136,7 +135,6 @@ urlpatterns = [
|
||||
"group/<int:group_id>/detail/", GroupTemplateView.as_view(), name="group_detail"
|
||||
),
|
||||
# User views
|
||||
path("user/", UserListView.as_view(), name="user_list"),
|
||||
path(
|
||||
"user/me/<path:remaining_path>/",
|
||||
UserMeRedirect.as_view(),
|
||||
|
||||
@@ -48,7 +48,6 @@ from django.views.generic import (
|
||||
CreateView,
|
||||
DeleteView,
|
||||
DetailView,
|
||||
ListView,
|
||||
RedirectView,
|
||||
TemplateView,
|
||||
)
|
||||
@@ -404,13 +403,6 @@ class UserMiniView(CanViewMixin, DetailView):
|
||||
template_name = "core/user_mini.jinja"
|
||||
|
||||
|
||||
class UserListView(ListView, CanEditPropMixin):
|
||||
"""Displays the user list."""
|
||||
|
||||
model = User
|
||||
template_name = "core/user_list.jinja"
|
||||
|
||||
|
||||
# FIXME: the edit_once fields aren't displayed to the user (as expected).
|
||||
# However, if the user re-add them manually in the form, they are saved.
|
||||
class UserUpdateProfileView(UserTabsMixin, CanEditMixin, UpdateView):
|
||||
|
||||
@@ -116,56 +116,6 @@
|
||||
</span>
|
||||
</div>
|
||||
{% endif %}
|
||||
<section>
|
||||
<div class="category-header">
|
||||
<h3 class="margin-bottom">{% trans %}Eurockéennes 2025 partnership{% endtrans %}</h3>
|
||||
{% if user.is_subscribed %}
|
||||
<div id="eurock-partner" style="
|
||||
min-height: 600px;
|
||||
background-color: lightgrey;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
">
|
||||
<p style="text-align: center;">
|
||||
{% trans trimmed %}
|
||||
Our partner uses Weezevent to sell tickets.
|
||||
Weezevent may collect user info according to
|
||||
its own privacy policy.
|
||||
By clicking the accept button you consent to
|
||||
their terms of services.
|
||||
{% endtrans %}
|
||||
</p>
|
||||
|
||||
<a href="https://weezevent.com/fr/politique-de-confidentialite/">{% trans %}Privacy policy{% endtrans %}</a>
|
||||
|
||||
<button
|
||||
hx-get="{{ url("eboutic:eurock") }}"
|
||||
hx-target="#eurock-partner"
|
||||
hx-swap="outerHTML"
|
||||
hx-trigger="click, load[document.cookie.includes('weezevent_accept=true')]"
|
||||
@htmx:after-request="document.cookie = 'weezevent_accept=true'"
|
||||
>{% trans %}Accept{% endtrans %}
|
||||
</button>
|
||||
</div>
|
||||
{% else %}
|
||||
<p>
|
||||
{%- trans trimmed %}
|
||||
You must be subscribed to benefit from the partnership with the Eurockéennes.
|
||||
{% endtrans -%}
|
||||
</p>
|
||||
<p>
|
||||
{%- trans trimmed %}
|
||||
This partnership offers a discount of up to 33%
|
||||
on tickets for Friday, Saturday and Sunday,
|
||||
as well as the 3-day package from Friday to Sunday.
|
||||
{% endtrans -%}
|
||||
</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</section>
|
||||
{% for priority_groups in products|groupby('order') %}
|
||||
{% for category, items in priority_groups.list|groupby('category') %}
|
||||
{% if items|count > 0 %}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
<a title="Logiciel billetterie en ligne"
|
||||
href="https://www.weezevent.com?c=sys_widget"
|
||||
class="weezevent-widget-integration"
|
||||
target="_blank"
|
||||
data-src="https://widget.weezevent.com/ticket/8aaba226-f7a3-4192-a64e-72ff8f5b35b7?id_evenement=1419869&locale=fr-FR&code=28747"
|
||||
data-width="650"
|
||||
data-height="600"
|
||||
data-resize="1"
|
||||
data-nopb="0"
|
||||
data-type="neo"
|
||||
data-width_auto="1"
|
||||
data-noscroll="0"
|
||||
data-id="1419869">
|
||||
Billetterie Weezevent
|
||||
</a>
|
||||
<script type="text/javascript" src="https://widget.weezevent.com/weez.js" async defer></script>
|
||||
17
eboutic/templates/eboutic/eurok_fragment.jinja
Normal file
17
eboutic/templates/eboutic/eurok_fragment.jinja
Normal file
@@ -0,0 +1,17 @@
|
||||
<a
|
||||
title="Logiciel billetterie en ligne"
|
||||
href="https://widget.weezevent.com/ticket/6ef65533-f5b0-4571-9d21-1f1bc63921f0?id_evenement=1211855&locale=fr-FR&code=34146"
|
||||
class="weezevent-widget-integration"
|
||||
target="_blank"
|
||||
data-src="https://widget.weezevent.com/ticket/6ef65533-f5b0-4571-9d21-1f1bc63921f0?id_evenement=1211855&locale=fr-FR&code=34146"
|
||||
data-width="650"
|
||||
data-height="600"
|
||||
data-resize="1"
|
||||
data-nopb="0"
|
||||
data-type="neo"
|
||||
data-width_auto="1"
|
||||
data-noscroll="0"
|
||||
data-id="1211855">
|
||||
Billetterie Weezevent
|
||||
</a>
|
||||
<script type="text/javascript" src="https://widget.weezevent.com/weez.js" async defer></script>
|
||||
@@ -31,7 +31,6 @@ from eboutic.views import (
|
||||
EbouticMainView,
|
||||
EbouticPayWithSith,
|
||||
EtransactionAutoAnswer,
|
||||
EurockPartnerFragment,
|
||||
payment_result,
|
||||
)
|
||||
|
||||
@@ -51,5 +50,4 @@ urlpatterns = [
|
||||
EtransactionAutoAnswer.as_view(),
|
||||
name="etransation_autoanswer",
|
||||
),
|
||||
path("eurock/", EurockPartnerFragment.as_view(), name="eurock"),
|
||||
]
|
||||
|
||||
@@ -42,11 +42,11 @@ from django.shortcuts import redirect, render
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.views.decorators.http import require_GET
|
||||
from django.views.generic import DetailView, FormView, TemplateView, UpdateView, View
|
||||
from django.views.generic import DetailView, FormView, UpdateView, View
|
||||
from django.views.generic.edit import SingleObjectMixin
|
||||
from django_countries.fields import Country
|
||||
|
||||
from core.auth.mixins import CanViewMixin, IsSubscriberMixin
|
||||
from core.auth.mixins import CanViewMixin
|
||||
from core.views.mixins import FragmentMixin, UseFragmentsMixin
|
||||
from counter.forms import BaseBasketForm, BasketProductForm, BillingInfoForm
|
||||
from counter.models import (
|
||||
@@ -350,7 +350,3 @@ class EtransactionAutoAnswer(View):
|
||||
return HttpResponse(
|
||||
"Payment failed with error: " + request.GET["Error"], status=202
|
||||
)
|
||||
|
||||
|
||||
class EurockPartnerFragment(IsSubscriberMixin, TemplateView):
|
||||
template_name = "eboutic/eurock_fragment.jinja"
|
||||
|
||||
@@ -146,7 +146,7 @@
|
||||
<label for="{{ input_id }}">
|
||||
{%- endif %}
|
||||
<figure>
|
||||
{%- if user.is_viewable %}
|
||||
{%- if user.can_view(candidature.user) %}
|
||||
{% if candidature.user.profile_pict %}
|
||||
<img class="candidate__picture" src="{{ candidature.user.profile_pict.get_download_url() }}" alt="{% trans %}Profile{% endtrans %}">
|
||||
{% else %}
|
||||
|
||||
@@ -5838,39 +5838,3 @@ 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"
|
||||
|
||||
#: eboutic/templates/eboutic/eboutic_main.jinja
|
||||
msgid "Eurockéennes 2025 partnership"
|
||||
msgstr "Partenariat Eurockéennes 2025"
|
||||
|
||||
#: eboutic/templates/eboutic/eboutic_main.jinja
|
||||
msgid ""
|
||||
"Our partner uses Weezevent to sell tickets. Weezevent may collect user info "
|
||||
"according to its own privacy policy. By clicking the accept button you "
|
||||
"consent to their terms of services."
|
||||
msgstr ""
|
||||
"Notre partenaire utilises Wezevent pour vendre ses billets. Weezevent peut "
|
||||
"collecter des informations utilisateur conformément à sa propre politique de "
|
||||
"confidentialité. En cliquant sur le bouton d'acceptation vous consentez à "
|
||||
"leurs termes de service."
|
||||
|
||||
#: eboutic/templates/eboutic/eboutic_main.jinja
|
||||
msgid "Privacy policy"
|
||||
msgstr "Politique de confidentialité"
|
||||
|
||||
#: eboutic/templates/eboutic/eboutic_main.jinja
|
||||
msgid ""
|
||||
"You must be subscribed to benefit from the partnership with the Eurockéennes."
|
||||
msgstr ""
|
||||
"Vous devez être cotisant pour bénéficier du partenariat avec les "
|
||||
"Eurockéennes."
|
||||
|
||||
#: eboutic/templates/eboutic/eboutic_main.jinja
|
||||
#, python-format
|
||||
msgid ""
|
||||
"This partnership offers a discount of up to 33%% on tickets for Friday, "
|
||||
"Saturday and Sunday, as well as the 3-day package from Friday to Sunday."
|
||||
msgstr ""
|
||||
"Ce partenariat permet de profiter d'une réduction jusqu'à 33%% sur les "
|
||||
"billets du vendredi, du samedi et du dimanche, ainsi qu'au forfait 3 jours, "
|
||||
"du vendredi au dimanche."
|
||||
|
||||
Reference in New Issue
Block a user