mirror of
https://github.com/ae-utbm/sith.git
synced 2025-09-23 00:23:52 +00:00
feat: make poster views available to club board members
This commit is contained in:
@@ -42,6 +42,13 @@ from core.fields import ResizedImageField
|
||||
from core.models import Group, Notification, Page, SithFile, User
|
||||
|
||||
|
||||
class ClubQuerySet(models.QuerySet):
|
||||
def having_board_member(self, user: User) -> Self:
|
||||
"""Filter all club in which the given user is a board member."""
|
||||
active_memberships = user.memberships.board().ongoing()
|
||||
return self.filter(Exists(active_memberships.filter(club=OuterRef("pk"))))
|
||||
|
||||
|
||||
class Club(models.Model):
|
||||
"""The Club class, made as a tree to allow nice tidy organization."""
|
||||
|
||||
@@ -91,6 +98,8 @@ class Club(models.Model):
|
||||
Group, related_name="club_board", on_delete=models.PROTECT
|
||||
)
|
||||
|
||||
objects = ClubQuerySet.as_manager()
|
||||
|
||||
class Meta:
|
||||
ordering = ["name"]
|
||||
|
||||
|
27
club/tests/test_club.py
Normal file
27
club/tests/test_club.py
Normal file
@@ -0,0 +1,27 @@
|
||||
from datetime import timedelta
|
||||
|
||||
import pytest
|
||||
from django.utils.timezone import localdate
|
||||
from model_bakery import baker
|
||||
from model_bakery.recipe import Recipe
|
||||
|
||||
from club.models import Club, Membership
|
||||
from core.baker_recipes import subscriber_user
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_club_queryset_having_board_member():
|
||||
clubs = baker.make(Club, _quantity=5)
|
||||
user = subscriber_user.make()
|
||||
membership_recipe = Recipe(
|
||||
Membership, user=user, start_date=localdate() - timedelta(days=3)
|
||||
)
|
||||
membership_recipe.make(club=clubs[0], role=1)
|
||||
membership_recipe.make(club=clubs[1], role=3)
|
||||
membership_recipe.make(club=clubs[2], role=7)
|
||||
membership_recipe.make(
|
||||
club=clubs[3], role=3, end_date=localdate() - timedelta(days=1)
|
||||
)
|
||||
|
||||
club_ids = Club.objects.having_board_member(user).values_list("id", flat=True)
|
||||
assert set(club_ids) == {clubs[1].id, clubs[2].id}
|
35
club/tests/test_posters.py
Normal file
35
club/tests/test_posters.py
Normal file
@@ -0,0 +1,35 @@
|
||||
import pytest
|
||||
from django.test import Client
|
||||
from django.urls import reverse
|
||||
from model_bakery import baker
|
||||
|
||||
from club.models import Club
|
||||
from com.models import Poster
|
||||
from core.baker_recipes import subscriber_user
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize("route_url", ["club:poster_list", "club:poster_create"])
|
||||
def test_access(client: Client, route_url):
|
||||
club = baker.make(Club)
|
||||
user = subscriber_user.make()
|
||||
url = reverse(route_url, kwargs={"club_id": club.id})
|
||||
|
||||
client.force_login(user)
|
||||
assert client.get(url).status_code == 403
|
||||
club.board_group.users.add(user)
|
||||
assert client.get(url).status_code == 200
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize("route_url", ["club:poster_edit", "club:poster_delete"])
|
||||
def test_access_specific_poster(client: Client, route_url):
|
||||
club = baker.make(Club)
|
||||
user = subscriber_user.make()
|
||||
poster = baker.make(Poster)
|
||||
url = reverse(route_url, kwargs={"club_id": club.id, "poster_id": poster.id})
|
||||
|
||||
client.force_login(user)
|
||||
assert client.get(url).status_code == 403
|
||||
club.board_group.users.add(user)
|
||||
assert client.get(url).status_code == 200
|
@@ -51,13 +51,17 @@ from club.forms import (
|
||||
SellingsForm,
|
||||
)
|
||||
from club.models import Club, Mailing, MailingSubscription, Membership
|
||||
from com.models import Poster
|
||||
from com.views import (
|
||||
PosterCreateBaseView,
|
||||
PosterDeleteBaseView,
|
||||
PosterEditBaseView,
|
||||
PosterListBaseView,
|
||||
)
|
||||
from core.auth.mixins import CanCreateMixin, CanEditMixin, CanViewMixin
|
||||
from core.auth.mixins import (
|
||||
CanEditMixin,
|
||||
CanViewMixin,
|
||||
)
|
||||
from core.models import PageRev
|
||||
from core.views import DetailFormView, PageEditViewBase
|
||||
from core.views.mixins import TabedViewMixin
|
||||
@@ -66,9 +70,12 @@ from counter.models import Selling
|
||||
|
||||
class ClubTabsMixin(TabedViewMixin):
|
||||
def get_tabs_title(self):
|
||||
obj = self.get_object()
|
||||
if isinstance(obj, PageRev):
|
||||
self.object = obj.page.club
|
||||
if not hasattr(self, "object") or not self.object:
|
||||
self.object = self.get_object()
|
||||
if isinstance(self.object, PageRev):
|
||||
self.object = self.object.page.club
|
||||
elif isinstance(self.object, Poster):
|
||||
self.object = self.object.club
|
||||
return self.object.get_display_name()
|
||||
|
||||
def get_list_of_tabs(self):
|
||||
@@ -159,7 +166,7 @@ class ClubTabsMixin(TabedViewMixin):
|
||||
"club:poster_list", kwargs={"club_id": self.object.id}
|
||||
),
|
||||
"slug": "posters",
|
||||
"name": _("Posters list"),
|
||||
"name": _("Posters"),
|
||||
},
|
||||
]
|
||||
)
|
||||
@@ -686,48 +693,45 @@ class MailingAutoGenerationView(View):
|
||||
return redirect("club:mailing", club_id=club.id)
|
||||
|
||||
|
||||
class PosterListView(ClubTabsMixin, PosterListBaseView, CanViewMixin):
|
||||
class PosterListView(ClubTabsMixin, PosterListBaseView):
|
||||
"""List communication posters."""
|
||||
|
||||
current_tab = "posters"
|
||||
extra_context = {"app": "club"}
|
||||
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().filter(club=self.club.id)
|
||||
|
||||
def get_object(self):
|
||||
return self.club
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs = super().get_context_data(**kwargs)
|
||||
kwargs["app"] = "club"
|
||||
kwargs["club"] = self.club
|
||||
return kwargs
|
||||
|
||||
|
||||
class PosterCreateView(PosterCreateBaseView, CanCreateMixin):
|
||||
class PosterCreateView(ClubTabsMixin, PosterCreateBaseView):
|
||||
"""Create communication poster."""
|
||||
|
||||
pk_url_kwarg = "club_id"
|
||||
|
||||
def get_object(self):
|
||||
obj = super().get_object()
|
||||
if not obj:
|
||||
return self.club
|
||||
return obj
|
||||
current_tab = "posters"
|
||||
|
||||
def get_success_url(self, **kwargs):
|
||||
return reverse_lazy("club:poster_list", kwargs={"club_id": self.club.id})
|
||||
|
||||
def get_object(self, *args, **kwargs):
|
||||
return self.club
|
||||
|
||||
class PosterEditView(ClubTabsMixin, PosterEditBaseView, CanEditMixin):
|
||||
|
||||
class PosterEditView(ClubTabsMixin, PosterEditBaseView):
|
||||
"""Edit communication poster."""
|
||||
|
||||
current_tab = "posters"
|
||||
extra_context = {"app": "club"}
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse_lazy("club:poster_list", kwargs={"club_id": self.club.id})
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs = super().get_context_data(**kwargs)
|
||||
kwargs["app"] = "club"
|
||||
return kwargs
|
||||
|
||||
|
||||
class PosterDeleteView(PosterDeleteBaseView, ClubTabsMixin, CanEditMixin):
|
||||
class PosterDeleteView(ClubTabsMixin, PosterDeleteBaseView):
|
||||
"""Delete communication poster."""
|
||||
|
||||
current_tab = "posters"
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse_lazy("club:poster_list", kwargs={"club_id": self.club.id})
|
||||
|
26
com/forms.py
26
com/forms.py
@@ -2,7 +2,6 @@ from datetime import date
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from django import forms
|
||||
from django.db.models import Exists, OuterRef
|
||||
from django.forms import CheckboxInput
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
@@ -35,20 +34,18 @@ class PosterForm(forms.ModelForm):
|
||||
label=_("Start date"),
|
||||
widget=SelectDateTime,
|
||||
required=True,
|
||||
initial=timezone.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
initial=timezone.now(),
|
||||
)
|
||||
date_end = forms.DateTimeField(
|
||||
label=_("End date"), widget=SelectDateTime, required=False
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.user = kwargs.pop("user", None)
|
||||
def __init__(self, *args, user: User, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
if self.user and not self.user.is_com_admin:
|
||||
self.fields["club"].queryset = Club.objects.filter(
|
||||
id__in=self.user.clubs_with_rights
|
||||
)
|
||||
self.fields.pop("display_time")
|
||||
if user.is_root or user.is_com_admin:
|
||||
self.fields["club"].widget = AutoCompleteSelectClub()
|
||||
else:
|
||||
self.fields["club"].queryset = Club.objects.having_board_member(user)
|
||||
|
||||
|
||||
class NewsDateForm(forms.ModelForm):
|
||||
@@ -161,16 +158,9 @@ class NewsForm(forms.ModelForm):
|
||||
# if the author is an admin, he/she can choose any club,
|
||||
# otherwise, only clubs for which he/she is a board member can be selected
|
||||
if author.is_root or author.is_com_admin:
|
||||
self.fields["club"] = forms.ModelChoiceField(
|
||||
queryset=Club.objects.all(), widget=AutoCompleteSelectClub
|
||||
)
|
||||
self.fields["club"].widget = AutoCompleteSelectClub()
|
||||
else:
|
||||
active_memberships = author.memberships.board().ongoing()
|
||||
self.fields["club"] = forms.ModelChoiceField(
|
||||
queryset=Club.objects.filter(
|
||||
Exists(active_memberships.filter(club=OuterRef("pk")))
|
||||
)
|
||||
)
|
||||
self.fields["club"].queryset = Club.objects.having_board_member(author)
|
||||
|
||||
def is_valid(self):
|
||||
return super().is_valid() and self.date_form.is_valid()
|
||||
|
@@ -412,17 +412,5 @@ class Poster(models.Model):
|
||||
if self.date_end and self.date_begin > self.date_end:
|
||||
raise ValidationError(_("Begin date should be before end date"))
|
||||
|
||||
def is_owned_by(self, user):
|
||||
if user.is_anonymous:
|
||||
return False
|
||||
return user.is_com_admin or len(user.clubs_with_rights) > 0
|
||||
|
||||
def can_be_moderated_by(self, user):
|
||||
return user.is_com_admin
|
||||
|
||||
def get_display_name(self):
|
||||
return self.club.get_display_name()
|
||||
|
||||
@property
|
||||
def page(self):
|
||||
return self.club.page
|
||||
|
@@ -18,17 +18,16 @@ from unittest.mock import patch
|
||||
import pytest
|
||||
from django.conf import settings
|
||||
from django.contrib.sites.models import Site
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
from django.test import Client, TestCase
|
||||
from django.urls import reverse
|
||||
from django.utils import html
|
||||
from django.utils.timezone import localtime, now
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext as _
|
||||
from model_bakery import baker
|
||||
from pytest_django.asserts import assertNumQueries, assertRedirects
|
||||
|
||||
from club.models import Club, Membership
|
||||
from com.models import News, NewsDate, Poster, Sith, Weekmail, WeekmailArticle
|
||||
from com.models import News, NewsDate, Sith, Weekmail, WeekmailArticle
|
||||
from core.baker_recipes import subscriber_user
|
||||
from core.models import AnonymousUser, Group, User
|
||||
|
||||
@@ -207,31 +206,6 @@ class TestWeekmailArticle(TestCase):
|
||||
assert not self.article.is_owned_by(self.sli)
|
||||
|
||||
|
||||
class TestPoster(TestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
cls.com_admin = User.objects.get(username="comunity")
|
||||
cls.poster = Poster.objects.create(
|
||||
name="dummy",
|
||||
file=SimpleUploadedFile("dummy.jpg", b"azertyuiop"),
|
||||
club=Club.objects.first(),
|
||||
date_begin=localtime(now()),
|
||||
)
|
||||
cls.sli = User.objects.get(username="sli")
|
||||
cls.sli.memberships.all().delete()
|
||||
Membership(user=cls.sli, club=Club.objects.first(), role=5).save()
|
||||
cls.susbcriber = User.objects.get(username="subscriber")
|
||||
cls.anonymous = AnonymousUser()
|
||||
|
||||
def test_poster_owner(self):
|
||||
"""Test that poster are owned by com admins and board members in clubs."""
|
||||
assert self.poster.is_owned_by(self.com_admin)
|
||||
assert not self.poster.is_owned_by(self.anonymous)
|
||||
|
||||
assert not self.poster.is_owned_by(self.susbcriber)
|
||||
assert self.poster.is_owned_by(self.sli)
|
||||
|
||||
|
||||
class TestNewsCreation(TestCase):
|
||||
@classmethod
|
||||
def setUpTestData(cls):
|
||||
|
176
com/views.py
176
com/views.py
@@ -28,7 +28,9 @@ from typing import Any
|
||||
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.mixins import AccessMixin, PermissionRequiredMixin
|
||||
from django.contrib.auth.mixins import (
|
||||
PermissionRequiredMixin,
|
||||
)
|
||||
from django.contrib.syndication.views import Feed
|
||||
from django.core.exceptions import PermissionDenied, ValidationError
|
||||
from django.db.models import Max
|
||||
@@ -50,6 +52,7 @@ from core.auth.mixins import (
|
||||
CanEditPropMixin,
|
||||
CanViewMixin,
|
||||
PermissionOrAuthorRequiredMixin,
|
||||
PermissionOrClubBoardRequiredMixin,
|
||||
)
|
||||
from core.models import User
|
||||
from core.views.mixins import QuickNotifMixin, TabedViewMixin
|
||||
@@ -99,13 +102,6 @@ class ComTabsMixin(TabedViewMixin):
|
||||
]
|
||||
|
||||
|
||||
class IsComAdminMixin(AccessMixin):
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if not request.user.is_com_admin:
|
||||
raise PermissionDenied
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
|
||||
class ComEditView(ComTabsMixin, CanEditPropMixin, UpdateView):
|
||||
model = Sith
|
||||
template_name = "core/edit.jinja"
|
||||
@@ -558,161 +554,109 @@ class MailingModerateView(View):
|
||||
raise PermissionDenied
|
||||
|
||||
|
||||
class PosterAdminViewMixin(IsComAdminMixin, ComTabsMixin):
|
||||
current_tab = "posters"
|
||||
|
||||
|
||||
class PosterListBaseView(PosterAdminViewMixin, ListView):
|
||||
class PosterListBaseView(PermissionOrClubBoardRequiredMixin, ListView):
|
||||
"""List communication posters."""
|
||||
|
||||
current_tab = "posters"
|
||||
model = Poster
|
||||
template_name = "com/poster_list.jinja"
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
club_id = kwargs.pop("club_id", None)
|
||||
self.club = None
|
||||
if club_id:
|
||||
self.club = get_object_or_404(Club, pk=club_id)
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_queryset(self):
|
||||
if self.request.user.is_com_admin:
|
||||
return Poster.objects.all().order_by("-date_begin")
|
||||
else:
|
||||
return Poster.objects.filter(club=self.club.id)
|
||||
permission_required = "com.view_poster"
|
||||
ordering = ["-date_begin"]
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs = super().get_context_data(**kwargs)
|
||||
if not self.request.user.is_com_admin:
|
||||
kwargs["club"] = self.club
|
||||
return kwargs
|
||||
return super().get_context_data(**kwargs) | {"club": self.club}
|
||||
|
||||
|
||||
class PosterCreateBaseView(PosterAdminViewMixin, CreateView):
|
||||
class PosterCreateBaseView(PermissionOrClubBoardRequiredMixin, CreateView):
|
||||
"""Create communication poster."""
|
||||
|
||||
current_tab = "posters"
|
||||
form_class = PosterForm
|
||||
template_name = "core/create.jinja"
|
||||
permission_required = "com.add_poster"
|
||||
|
||||
def get_queryset(self):
|
||||
return Poster.objects.all()
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if "club_id" in kwargs:
|
||||
self.club = get_object_or_404(Club, pk=kwargs["club_id"])
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super().get_form_kwargs()
|
||||
kwargs.update({"user": self.request.user})
|
||||
return kwargs
|
||||
return super().get_form_kwargs() | {"user": self.request.user}
|
||||
|
||||
def get_initial(self):
|
||||
return {"club": self.club}
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs = super().get_context_data(**kwargs)
|
||||
if not self.request.user.is_com_admin:
|
||||
kwargs["club"] = self.club
|
||||
return kwargs
|
||||
return super().get_context_data(**kwargs) | {"club": self.club}
|
||||
|
||||
def form_valid(self, form):
|
||||
if self.request.user.is_com_admin:
|
||||
if self.request.user.has_perm("com.moderate_poster"):
|
||||
form.instance.is_moderated = True
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
class PosterEditBaseView(PosterAdminViewMixin, UpdateView):
|
||||
class PosterEditBaseView(PermissionOrClubBoardRequiredMixin, UpdateView):
|
||||
"""Edit communication poster."""
|
||||
|
||||
pk_url_kwarg = "poster_id"
|
||||
current_tab = "posters"
|
||||
form_class = PosterForm
|
||||
template_name = "com/poster_edit.jinja"
|
||||
|
||||
def get_initial(self):
|
||||
return {
|
||||
"date_begin": self.object.date_begin.strftime("%Y-%m-%d %H:%M:%S")
|
||||
if self.object.date_begin
|
||||
else None,
|
||||
"date_end": self.object.date_end.strftime("%Y-%m-%d %H:%M:%S")
|
||||
if self.object.date_end
|
||||
else None,
|
||||
}
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if kwargs.get("club_id"):
|
||||
try:
|
||||
self.club = Club.objects.get(pk=kwargs["club_id"])
|
||||
except Club.DoesNotExist as e:
|
||||
raise PermissionDenied from e
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
permission_required = "com.change_poster"
|
||||
|
||||
def get_queryset(self):
|
||||
return Poster.objects.all()
|
||||
|
||||
def get_form_kwargs(self):
|
||||
kwargs = super().get_form_kwargs()
|
||||
kwargs.update({"user": self.request.user})
|
||||
return kwargs
|
||||
return super().get_form_kwargs() | {"user": self.request.user}
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs = super().get_context_data(**kwargs)
|
||||
if hasattr(self, "club"):
|
||||
kwargs["club"] = self.club
|
||||
return kwargs
|
||||
return super().get_context_data(**kwargs) | {"club": self.club}
|
||||
|
||||
def form_valid(self, form):
|
||||
if self.request.user.is_com_admin:
|
||||
if not self.request.user.has_perm("com.moderate_poster"):
|
||||
form.instance.is_moderated = False
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
class PosterDeleteBaseView(PosterAdminViewMixin, DeleteView):
|
||||
class PosterDeleteBaseView(
|
||||
PermissionOrClubBoardRequiredMixin, ComTabsMixin, DeleteView
|
||||
):
|
||||
"""Edit communication poster."""
|
||||
|
||||
pk_url_kwarg = "poster_id"
|
||||
current_tab = "posters"
|
||||
model = Poster
|
||||
template_name = "core/delete_confirm.jinja"
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if kwargs.get("club_id"):
|
||||
try:
|
||||
self.club = Club.objects.get(pk=kwargs["club_id"])
|
||||
except Club.DoesNotExist as e:
|
||||
raise PermissionDenied from e
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
permission_required = "com.delete_poster"
|
||||
|
||||
|
||||
class PosterListView(PosterListBaseView):
|
||||
class PosterListView(ComTabsMixin, PosterListBaseView):
|
||||
"""List communication posters."""
|
||||
|
||||
current_tab = "posters"
|
||||
|
||||
def get_queryset(self):
|
||||
qs = super().get_queryset()
|
||||
if self.request.user.has_perm("com.view_poster"):
|
||||
return qs
|
||||
return qs.filter(club=self.club.id)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs = super().get_context_data(**kwargs)
|
||||
kwargs["app"] = "com"
|
||||
return kwargs
|
||||
|
||||
|
||||
class PosterCreateView(PosterCreateBaseView):
|
||||
class PosterCreateView(ComTabsMixin, PosterCreateBaseView):
|
||||
"""Create communication poster."""
|
||||
|
||||
current_tab = "posters"
|
||||
success_url = reverse_lazy("com:poster_list")
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs = super().get_context_data(**kwargs)
|
||||
kwargs["app"] = "com"
|
||||
return kwargs
|
||||
extra_context = {"app": "com"}
|
||||
|
||||
|
||||
class PosterEditView(PosterEditBaseView):
|
||||
class PosterEditView(ComTabsMixin, PosterEditBaseView):
|
||||
"""Edit communication poster."""
|
||||
|
||||
current_tab = "posters"
|
||||
success_url = reverse_lazy("com:poster_list")
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs = super().get_context_data(**kwargs)
|
||||
kwargs["app"] = "com"
|
||||
return kwargs
|
||||
extra_context = {"app": "com"}
|
||||
|
||||
|
||||
class PosterDeleteView(PosterDeleteBaseView):
|
||||
@@ -721,44 +665,39 @@ class PosterDeleteView(PosterDeleteBaseView):
|
||||
success_url = reverse_lazy("com:poster_list")
|
||||
|
||||
|
||||
class PosterModerateListView(PosterAdminViewMixin, ListView):
|
||||
class PosterModerateListView(PermissionRequiredMixin, ComTabsMixin, ListView):
|
||||
"""Moderate list communication poster."""
|
||||
|
||||
current_tab = "posters"
|
||||
model = Poster
|
||||
template_name = "com/poster_moderate.jinja"
|
||||
queryset = Poster.objects.filter(is_moderated=False).all()
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs = super().get_context_data(**kwargs)
|
||||
kwargs["app"] = "com"
|
||||
return kwargs
|
||||
permission_required = "com.moderate_poster"
|
||||
extra_context = {"app": "com"}
|
||||
|
||||
|
||||
class PosterModerateView(PosterAdminViewMixin, View):
|
||||
class PosterModerateView(PermissionRequiredMixin, ComTabsMixin, View):
|
||||
"""Moderate communication poster."""
|
||||
|
||||
current_tab = "posters"
|
||||
permission_required = "com.moderate_poster"
|
||||
extra_context = {"app": "com"}
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
obj = get_object_or_404(Poster, pk=kwargs["object_id"])
|
||||
if obj.can_be_moderated_by(request.user):
|
||||
obj.is_moderated = True
|
||||
obj.moderator = request.user
|
||||
obj.save()
|
||||
return redirect("com:poster_moderate_list")
|
||||
raise PermissionDenied
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs = super(PosterModerateListView, self).get_context_data(**kwargs)
|
||||
kwargs["app"] = "com"
|
||||
return kwargs
|
||||
|
||||
|
||||
class ScreenListView(IsComAdminMixin, ComTabsMixin, ListView):
|
||||
class ScreenListView(PermissionRequiredMixin, ComTabsMixin, ListView):
|
||||
"""List communication screens."""
|
||||
|
||||
current_tab = "screens"
|
||||
model = Screen
|
||||
template_name = "com/screen_list.jinja"
|
||||
permission_required = "com.view_screen"
|
||||
|
||||
|
||||
class ScreenSlideshowView(DetailView):
|
||||
@@ -769,12 +708,12 @@ class ScreenSlideshowView(DetailView):
|
||||
template_name = "com/screen_slideshow.jinja"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs = super().get_context_data(**kwargs)
|
||||
kwargs["posters"] = self.object.active_posters()
|
||||
return kwargs
|
||||
return super().get_context_data(**kwargs) | {
|
||||
"posters": self.object.active_posters()
|
||||
}
|
||||
|
||||
|
||||
class ScreenCreateView(IsComAdminMixin, ComTabsMixin, CreateView):
|
||||
class ScreenCreateView(PermissionRequiredMixin, ComTabsMixin, CreateView):
|
||||
"""Create communication screen."""
|
||||
|
||||
current_tab = "screens"
|
||||
@@ -782,9 +721,10 @@ class ScreenCreateView(IsComAdminMixin, ComTabsMixin, CreateView):
|
||||
fields = ["name"]
|
||||
template_name = "core/create.jinja"
|
||||
success_url = reverse_lazy("com:screen_list")
|
||||
permission_required = "com.add_screen"
|
||||
|
||||
|
||||
class ScreenEditView(IsComAdminMixin, ComTabsMixin, UpdateView):
|
||||
class ScreenEditView(PermissionRequiredMixin, ComTabsMixin, UpdateView):
|
||||
"""Edit communication screen."""
|
||||
|
||||
pk_url_kwarg = "screen_id"
|
||||
@@ -793,9 +733,10 @@ class ScreenEditView(IsComAdminMixin, ComTabsMixin, UpdateView):
|
||||
fields = ["name"]
|
||||
template_name = "com/screen_edit.jinja"
|
||||
success_url = reverse_lazy("com:screen_list")
|
||||
permission_required = "com.change_screen"
|
||||
|
||||
|
||||
class ScreenDeleteView(IsComAdminMixin, ComTabsMixin, DeleteView):
|
||||
class ScreenDeleteView(PermissionRequiredMixin, ComTabsMixin, DeleteView):
|
||||
"""Delete communication screen."""
|
||||
|
||||
pk_url_kwarg = "screen_id"
|
||||
@@ -803,3 +744,4 @@ class ScreenDeleteView(IsComAdminMixin, ComTabsMixin, DeleteView):
|
||||
model = Screen
|
||||
template_name = "core/delete_confirm.jinja"
|
||||
success_url = reverse_lazy("com:screen_list")
|
||||
permission_required = "com.delete_screen"
|
||||
|
@@ -29,8 +29,14 @@ from typing import TYPE_CHECKING, Any, LiteralString
|
||||
|
||||
from django.contrib.auth.mixins import AccessMixin, PermissionRequiredMixin
|
||||
from django.core.exceptions import ImproperlyConfigured, PermissionDenied
|
||||
from django.http import Http404
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views.generic.base import View
|
||||
|
||||
from club.models import Club
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from django.db.models import Model
|
||||
|
||||
@@ -297,3 +303,50 @@ class PermissionOrAuthorRequiredMixin(PermissionRequiredMixin):
|
||||
self.author_field += "_id"
|
||||
author_id = getattr(obj, self.author_field, None)
|
||||
return author_id == self.request.user.id
|
||||
|
||||
|
||||
class PermissionOrClubBoardRequiredMixin(PermissionRequiredMixin):
|
||||
"""Require that the user has the required perm or is the board of the club.
|
||||
|
||||
This mixin can be used in any view that is called from a url
|
||||
having a `club_id` kwarg.
|
||||
|
||||
Example:
|
||||
|
||||
In `urls.py` :
|
||||
```python
|
||||
urlpatterns = [
|
||||
path("foo/<int:club_id>/bar/", FooView.as_view())
|
||||
]
|
||||
```
|
||||
|
||||
In `views.py` :
|
||||
|
||||
```python
|
||||
# this view is available to users that either have the
|
||||
# "foo.view_foo" permission or are in the board of the club
|
||||
# which id was given in the url
|
||||
class FooView(PermissionOrClubBoardRequiredMixin, View):
|
||||
permission_required = "foo.view_foo"
|
||||
```
|
||||
"""
|
||||
|
||||
club_pk_url_kwarg = "club_id"
|
||||
|
||||
@cached_property
|
||||
def club(self):
|
||||
club_id: str | int = self.kwargs.pop(self.club_pk_url_kwarg, None)
|
||||
if club_id is None:
|
||||
return None
|
||||
if isinstance(club_id, int) or club_id.isdigit():
|
||||
return get_object_or_404(Club, pk=club_id)
|
||||
raise Http404(_("No club found with id %(id)s") % {"id": club_id})
|
||||
|
||||
def has_permission(self):
|
||||
if self.request.user.is_anonymous:
|
||||
return False
|
||||
if super().has_permission():
|
||||
return True
|
||||
return self.club is not None and any(
|
||||
g.id == self.club.board_group_id for g in self.request.user.cached_groups
|
||||
)
|
||||
|
@@ -6,7 +6,7 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-09-02 15:56+0200\n"
|
||||
"POT-Creation-Date: 2025-09-19 17:22+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"
|
||||
@@ -514,8 +514,8 @@ msgstr "Éditer le Trombi"
|
||||
msgid "New Trombi"
|
||||
msgstr "Nouveau Trombi"
|
||||
|
||||
#: club/templates/club/club_tools.jinja com/templates/com/poster_list.jinja
|
||||
#: core/templates/core/user_tools.jinja
|
||||
#: club/templates/club/club_tools.jinja club/views.py
|
||||
#: com/templates/com/poster_list.jinja core/templates/core/user_tools.jinja
|
||||
msgid "Posters"
|
||||
msgstr "Affiches"
|
||||
|
||||
@@ -675,10 +675,6 @@ msgstr "Vente"
|
||||
msgid "Mailing list"
|
||||
msgstr "Listes de diffusion"
|
||||
|
||||
#: club/views.py com/views.py
|
||||
msgid "Posters list"
|
||||
msgstr "Liste d'affiches"
|
||||
|
||||
#: com/forms.py
|
||||
msgid "Format: 16:9 | Resolution: 1920x1080"
|
||||
msgstr "Format : 16:9 | Résolution : 1920x1080"
|
||||
@@ -1249,6 +1245,10 @@ msgstr "Message d'info"
|
||||
msgid "Alert message"
|
||||
msgstr "Message d'alerte"
|
||||
|
||||
#: com/views.py
|
||||
msgid "Posters list"
|
||||
msgstr "Liste d'affiches"
|
||||
|
||||
#: com/views.py
|
||||
msgid "Screens list"
|
||||
msgstr "Liste d'écrans"
|
||||
@@ -1272,6 +1272,11 @@ msgstr ""
|
||||
"Vous devez êtres un membre du bureau du club sélectionné pour poster dans le "
|
||||
"Weekmail."
|
||||
|
||||
#: core/auth/mixins.py
|
||||
#, python-format
|
||||
msgid "No club found with id %(id)s"
|
||||
msgstr "Pas de club avec l'id %(id)s trouvé"
|
||||
|
||||
#: core/models.py
|
||||
msgid "Is manually manageable"
|
||||
msgstr "Est gérable manuellement"
|
||||
@@ -1713,8 +1718,8 @@ msgid ""
|
||||
"AE UTBM is a voluntary organisation run by UTBM students. It organises "
|
||||
"student life at UTBM and manages its student facilities."
|
||||
msgstr ""
|
||||
"L'AE UTBM est une association bénévole gérée par les étudiants de "
|
||||
"l'UTBM. Elle organise la vie étudiante de l'UTBM et gère ses lieux de vie."
|
||||
"L'AE UTBM est une association bénévole gérée par les étudiants de l'UTBM. "
|
||||
"Elle organise la vie étudiante de l'UTBM et gère ses lieux de vie."
|
||||
|
||||
#: core/templates/core/base/footer.jinja core/templates/core/base/navbar.jinja
|
||||
msgid "Contacts"
|
||||
@@ -2157,10 +2162,6 @@ msgstr ""
|
||||
msgid "Page history"
|
||||
msgstr "Historique de la page"
|
||||
|
||||
#: core/templates/core/page_list.jinja
|
||||
msgid "There is no page in this website."
|
||||
msgstr "Il n'y a pas de page sur ce site web."
|
||||
|
||||
#: core/templates/core/page_prop.jinja
|
||||
msgid "Page properties"
|
||||
msgstr "Propriétés de la page"
|
||||
|
Reference in New Issue
Block a user