diff --git a/src/api/views/schedule.py b/src/api/views/schedule.py index 3fd9bba054813a374fb2c50154be195a3fde42ba..d9c2c433d19ec4fcf9886839942d900244fd8676 100644 --- a/src/api/views/schedule.py +++ b/src/api/views/schedule.py @@ -127,7 +127,7 @@ class EventSchedule(ConferenceSlugMixin, APIView): permission_classes = [IsApiUserOrReadOnly] def get(self, request, pk, format=None, **kwargs): - event = Event.objects.accessible_by_user(conference=self.conference, user=self.request.user).get(pk=pk) + event = Event.objects.associated_with_user(conference=self.conference, user=self.request.user, show_public=True).get(pk=pk) return Response(ScheduleEncoder().encode_event(event, self.conference.timezone)) def post(self, request, pk, format=None, **kwargs): diff --git a/src/core/models/events.py b/src/core/models/events.py index 2ae99c5c7132ea747d1cbc0c66d991261aad8e9d..bc1439f10660859c8d9f5911f2003e29d0e558ae 100644 --- a/src/core/models/events.py +++ b/src/core/models/events.py @@ -1,7 +1,7 @@ import logging import random from datetime import timedelta -from re import compile +from re import compile as re_compile from typing import Any from uuid import UUID, uuid4 @@ -11,7 +11,7 @@ from django.conf import settings from django.contrib.contenttypes.fields import GenericRelation from django.core.exceptions import NON_FIELD_ERRORS, ValidationError from django.db import models -from django.db.models import Q +from django.db.models import Q, QuerySet from django.forms import DurationField from django.utils import timezone from django.utils.dateparse import parse_datetime @@ -20,19 +20,18 @@ from django.utils.text import slugify from django.utils.timezone import is_aware, make_aware from django.utils.translation import gettext_lazy as _ -from core.models.tags import TagItem +from core.fields import ConferenceReference +from core.markdown import compile_translated_markdown_fields, store_relationships +from core.models.assemblies import Assembly +from core.models.base_managers import ConferenceManagerMixin +from core.models.conference import Conference, ConferenceMember, ConferenceTrack +from core.models.rooms import Room +from core.models.shared import BackendMixin +from core.models.tags import TaggedItemMixin, TagItem +from core.models.users import PlatformUser +from core.utils import str2bool, str2timedelta -from ..fields import ConferenceReference -from ..markdown import compile_translated_markdown_fields, store_relationships -from ..utils import str2bool, str2timedelta -from .assemblies import Assembly -from .conference import Conference, ConferenceMember, ConferenceTrack -from .rooms import Room -from .shared import BackendMixin -from .tags import TaggedItemMixin -from .users import PlatformUser - -SIMPLE_TIME_RE = compile(r'(.*\s)?(\d+:\d+)$') +SIMPLE_TIME_RE = re_compile(r'(.*\s)?(\d+:\d+)$') logger = logging.getLogger(__name__) @@ -60,76 +59,21 @@ class EventDurationField(models.DurationField): return super().formfield(**defaults) -class EventManager(models.Manager): - def accessible_by_user(self, user: PlatformUser, conference: Conference): - assert user is not None - assert conference is not None - - qs = self.get_queryset().filter(conference=conference) - - # crude hack because Django makes it that hard to customize AnonymousUser - if user is None or not user.is_authenticated: - user = PlatformUser.get_anonymous_user() - - # for global staff apply no more limits - if user.is_staff or user.is_superuser: - return qs - - # see if we can return all events (i.e. the user is marked as "staff") - if user.is_authenticated: - try: - member = ConferenceMember.objects.get(conference=conference, user=user) - if member.is_staff: - return qs - except ConferenceMember.DoesNotExist: - pass - - # for everybody else, only show public events - return qs.filter(is_public=True) - - def conference_accessible(self, conference: Conference): - return ( - self.get_queryset() - .filter(conference=conference, is_public=True) - .filter( - Q(assembly__state_assembly__in=Assembly.PUBLIC_STATES) - | Q(assembly__state_channel__in=Assembly.PUBLIC_STATES) - | Q(kind=Event.Kind.SELF_ORGANIZED) - ) - ) - - def manageable_by_user(self, user: PlatformUser, conference: Conference, staff_can_manage: bool = True): - assert user is not None - assert conference is not None - - qs = self.get_queryset().filter(conference=conference) - - # crude hack because Django makes it that hard to customize AnonymousUser - if user is None or not user.is_authenticated: - return qs.none() +class EventManager(ConferenceManagerMixin['Event']): + staff_permissions = ['assembly_team', 'scheduleadmin'] + conference_filter = 'assembly__conference' + assembly_filter = 'assembly' - # for global staff apply no more limits - if staff_can_manage and (user.is_staff or user.is_superuser): - return qs - - # see if we can return all events (i.e. the user is marked as "staff") - if staff_can_manage: - try: - member = ConferenceMember.objects.get(conference=conference, user=user) - if member.is_staff: - return qs - except ConferenceMember.DoesNotExist: - pass + def apply_public_filter(self, queryset: 'QuerySet[Event]', member: ConferenceMember | None = None) -> 'QuerySet[Event]': + return queryset.filter( + (Q(assembly__state_assembly__in=Assembly.PUBLIC_STATES) | Q(assembly__state_channel__in=Assembly.PUBLIC_STATES) | Q(kind=Event.Kind.SELF_ORGANIZED)) + & Q(is_public=True) + ) - # for everybody else, only show public events - manageable_assemblies_qs = Assembly.objects.manageable_by_user(conference=conference, user=user).filter( - members__member=user, members__can_manage_assembly=True + def apply_manage_filter(self, queryset: 'QuerySet[Event]', member: ConferenceMember) -> 'QuerySet[Event]': + return queryset.filter( + Q(assembly__members__member=member, assembly__members__can_manage_assembly=True) | Q(owner=member.user, kind=Event.Kind.SELF_ORGANIZED) ) - # TODO: remove work around when implementing ConferenceManagerMixin - # Filter out events belonging to the SoS Assembly - if not staff_can_manage: - manageable_assemblies_qs.exclude(id=conference.self_organized_sessions_assembly_id) - return qs.filter(Q(kind=Event.Kind.SELF_ORGANIZED, owner=user) | Q(assembly_id__in=manageable_assemblies_qs.values_list('id', flat=True))) class Event(TaggedItemMixin, BackendMixin, models.Model): diff --git a/src/plainui/views/events.py b/src/plainui/views/events.py index 0b1535e385925ebe14240a34e57923300e1e82a0..962b253a153d235c85a991bc585d796081dc9d20 100644 --- a/src/plainui/views/events.py +++ b/src/plainui/views/events.py @@ -188,7 +188,7 @@ class SosList(ConferenceRequiredMixin, FilteredListView): context['events_upcoming'] = event_filter(self.request.user, self.conf, kinds=[Event.Kind.SELF_ORGANIZED], calendar_mode=False, upcoming=True) context['events'] = event_filter(self.request.user, self.conf, kinds=[Event.Kind.SELF_ORGANIZED], calendar_mode=False) context['is_favorite_events'] = session_get_favorite_events(self.request.session, self.request.user) - context['manageable_events'] = Event.objects.manageable_by_user(self.request.user, self.conf).only('id') + context['manageable_events'] = Event.objects.manageable_by_user(self.conf, user=self.request.user).only('id') context['report_info'] = {'enabled': True}