diff --git a/src/api/serializers.py b/src/api/serializers.py index 2434911e88e10b6c9caf6e33a4e7cb9292c95941..2ac02e23e81d31d04572c295787c421cc36da605 100644 --- a/src/api/serializers.py +++ b/src/api/serializers.py @@ -11,9 +11,10 @@ from core.models.assemblies import Assembly from core.models.badges import Badge, BadgeToken, BadgeTokenTimeConstraint from core.models.conference import Conference, ConferenceMember, ConferenceTrack from core.models.events import Event +from core.models.messages import DirectMessage from core.models.metanavi import MetaNavItem from core.models.rooms import Room -from core.models.users import UserTimelineEntry +from core.models.users import PlatformUser, UserTimelineEntry class ParameterisedHyperlinkedIdentityField(HyperlinkedIdentityField): @@ -77,14 +78,20 @@ class HubModelSerializer(ValidatingModelSerializer): staff_only_fields = None # now check the user's conference membership based on the request - self.request_user = request_user = self.context['request'].user - self.conference_member = None - if request_user.is_authenticated: - with contextlib.suppress(ConferenceMember.DoesNotExist): - self.conference_member = ConferenceMember.objects.select_related('conference').get( - conference=self.conference, - user=request_user, - ) + if 'request' not in self.context: + from django.contrib.auth.models import AnonymousUser + + self.request_user = request_user = AnonymousUser() + self.conference_member = None + else: + self.request_user = request_user = self.context['request'].user + self.conference_member = None + if request_user.is_authenticated: + with contextlib.suppress(ConferenceMember.DoesNotExist): + self.conference_member = ConferenceMember.objects.select_related('conference').get( + conference=self.conference, + user=request_user, + ) # store if the request's user has staff permissions in the conference (either direct or globally) self.is_staff = request_user.is_superuser or request_user.is_staff or (self.conference_member is not None and self.conference_member.is_staff) @@ -297,3 +304,68 @@ class MetaNavItemSerializer(HubModelSerializer): 'graphic_light', 'graphic_dark', ] + + +class PlatformUserVisibleSerializer(serializers.ModelSerializer): + class Meta: + model = PlatformUser + fields = ['username', 'display_name'] + read_only_fields = fields + + +class PlatformUserByUsernameFieldSerializer(serializers.SlugRelatedField): + def __init__(self, *args, **kwargs): + super().__init__(*args, slug_field='username', **kwargs) + + def get_queryset(self): + return PlatformUser.objects.all() + + +class DirectMessageSerializerSentShort(HubModelSerializer): + class Meta: + model = DirectMessage + fields = ['id', 'sender', 'recipient', 'timestamp', 'in_reply_to', 'subject'] + read_only_fields = fields + + sender = PlatformUserVisibleSerializer(read_only=True) + recipient = PlatformUserVisibleSerializer(read_only=True) + + +class DirectMessageSerializerReceivedShort(HubModelSerializer): + class Meta: + model = DirectMessage + fields = [*DirectMessageSerializerSentShort.Meta.fields, 'has_responded'] + read_only_fields = fields + + sender = PlatformUserVisibleSerializer(read_only=True) + recipient = PlatformUserVisibleSerializer(read_only=True) + + +class DirectMessageSerializerSent(HubModelSerializer): + class Meta: + model = DirectMessage + fields = [*DirectMessageSerializerSentShort.Meta.fields, 'body'] + read_only_fields = fields + + sender = PlatformUserVisibleSerializer(read_only=True) + recipient = PlatformUserVisibleSerializer(read_only=True) + + +class DirectMessageSerializerReceived(HubModelSerializer): + class Meta: + model = DirectMessage + fields = [*DirectMessageSerializerReceivedShort.Meta.fields, 'body'] + read_only_fields = [f for f in fields if f != 'was_read'] + + sender = PlatformUserVisibleSerializer(read_only=True) + recipient = PlatformUserVisibleSerializer(read_only=True) + + +class DirectMessageSendSerializer(HubModelSerializer): + class Meta: + model = DirectMessage + fields = ['id', 'recipient', 'subject', 'body'] + read_only_fields = ['id'] + write_only_fields = [f for f in fields if f != 'id'] + + recipient = PlatformUserByUsernameFieldSerializer() diff --git a/src/api/tests/__init__.py b/src/api/tests/__init__.py index 8fb7973a17a79486bf8775d3b886df46eb305234..5f3d708f7a0cb7923a0e711001468b5e408cf603 100644 --- a/src/api/tests/__init__.py +++ b/src/api/tests/__init__.py @@ -1,7 +1,9 @@ from .badges import * # noqa: F401, F403 from .bbb import * # noqa: F401, F403 from .engelsystem import * # noqa: F401, F403 +from .events import * # noqa: F401, F403 from .map import * # noqa: F401, F403 +from .messages import * # noqa: F401, F403 from .metrics import * # noqa: F401, F403 from .schedule import * # noqa: F401, F403 from .workadventure import * # noqa: F401, F403 diff --git a/src/api/tests/events.py b/src/api/tests/events.py new file mode 100644 index 0000000000000000000000000000000000000000..581e8f958a64f6ad37531bd4dfaf47ee045746b4 --- /dev/null +++ b/src/api/tests/events.py @@ -0,0 +1,73 @@ +import uuid +from datetime import datetime, timedelta + +from pytz import UTC + +from django.test import Client, TestCase, override_settings +from django.urls import reverse + +from core.models import Assembly, Conference, ConferenceMember, Event, PlatformUser + +TEST_CONF_ID = uuid.uuid4() + + +@override_settings(SELECTED_CONFERENCE_ID=TEST_CONF_ID) +class EventsTestCase(TestCase): + def setUp(self): + self.conference1 = Conference(id=TEST_CONF_ID, slug='foo', name='Foo Conference', is_public=True) + self.conference1.save() + + self.human_user = PlatformUser(username='bernd', user_type=PlatformUser.Type.HUMAN) + self.human_user.save() + self.human_cm = ConferenceMember(conference=self.conference1, user=self.human_user) + self.human_cm.save() + + assembly = Assembly(conference=self.conference1, name='DUMMY', slug='dummy', state_assembly=Assembly.State.ACCEPTED, is_official=True) + assembly.save() + + self.event = Event( + conference=self.conference1, + slug='event1', + assembly=assembly, + name='Event1_1', + is_public=True, + schedule_start=datetime(2020, 1, 2, 0, 0, 0, tzinfo=UTC), + schedule_duration=timedelta(minutes=45), + kind=Event.Kind.OFFICIAL, + ) + self.event.save() + + def test_EventMyFavorites(self): + c = Client() + resp = c.get(reverse('api:my-events')) + self.assertEqual(resp.status_code, 403) + c.force_login(self.human_user) + + resp = c.get(reverse('api:my-events')) + self.assertEqual(resp.json(), []) + + self.human_user.favorite_events.add(self.event) + resp = c.get(reverse('api:my-events')) + self.assertEqual(resp.json(), [str(self.event.pk)]) + + def test_EventMyFavoritesSet_put(self): + c = Client() + resp = c.put(reverse('api:my-events-set', kwargs={'pk': str(uuid.uuid4())})) + self.assertEqual(resp.status_code, 403) + c.force_login(self.human_user) + + self.assertEqual(list(self.human_user.favorite_events.all()), []) + resp = c.put(reverse('api:my-events-set', kwargs={'pk': str(self.event.pk)})) + self.assertEqual(list(self.human_user.favorite_events.all()), [self.event]) + + def test_EventMyFavoritesSet_delete(self): + c = Client() + resp = c.delete(reverse('api:my-events-set', kwargs={'pk': str(uuid.uuid4())})) + self.assertEqual(resp.status_code, 403) + c.force_login(self.human_user) + + self.human_user.favorite_events.add(self.event) + + self.assertEqual(list(self.human_user.favorite_events.all()), [self.event]) + resp = c.delete(reverse('api:my-events-set', kwargs={'pk': str(self.event.pk)})) + self.assertEqual(list(self.human_user.favorite_events.all()), []) diff --git a/src/api/tests/messages.py b/src/api/tests/messages.py new file mode 100644 index 0000000000000000000000000000000000000000..fdde3af340a281b68309a49f2c4e4ac42a1c5927 --- /dev/null +++ b/src/api/tests/messages.py @@ -0,0 +1,371 @@ +import uuid +from datetime import datetime + +from freezegun import freeze_time +from pytz import UTC + +from django.test import Client, TestCase, override_settings +from django.urls import reverse +from django.utils.timezone import localtime + +from core.models import Conference, ConferenceMember, DirectMessage, PlatformUser + +TEST_CONF_ID = uuid.uuid4() + + +@override_settings(SELECTED_CONFERENCE_ID=TEST_CONF_ID) +class MessagesTestCase(TestCase): + def setUp(self): + self.conference1 = Conference(id=TEST_CONF_ID, slug='foo', name='Foo Conference', is_public=True) + self.conference1.save() + + self.human_user1 = PlatformUser(username='bernd', user_type=PlatformUser.Type.HUMAN) + self.human_user1.save() + self.human_cm1 = ConferenceMember(conference=self.conference1, user=self.human_user1) + self.human_cm1.save() + + self.human_user2 = PlatformUser(username='björnd', user_type=PlatformUser.Type.HUMAN) + self.human_user2.save() + self.human_cm2 = ConferenceMember(conference=self.conference1, user=self.human_user2) + self.human_cm2.save() + + def test_DirectMessagesReceived(self): + c = Client() + resp = c.get(reverse('api:my-messages-received')) + self.assertEqual(resp.status_code, 403) + c.force_login(self.human_user1) + + resp = c.get(reverse('api:my-messages-received')) + self.assertEqual(resp.json(), []) + + dm = DirectMessage( + conference=self.conference1, + sender=self.human_user2, + recipient=self.human_user1, + timestamp=datetime(2020, 5, 4, 3, 2, 1, tzinfo=UTC), + subject='Test Message2', + body='Message Body2', + ) + dm.save() + resp = c.get(reverse('api:my-messages-received')) + self.assertEqual( + resp.json(), + [ + { + 'id': str(dm.pk), + 'sender': {'username': self.human_user2.username, 'display_name': self.human_user2.display_name}, + 'recipient': {'username': self.human_user1.username, 'display_name': self.human_user1.display_name}, + 'timestamp': localtime(dm.timestamp).isoformat(), + 'in_reply_to': None, + 'subject': dm.subject, + 'has_responded': False, + } + ], + ) + + dm.has_responded = True + dm.deleted_by_sender = True + dm.save() + resp = c.get(reverse('api:my-messages-received')) + self.assertEqual( + resp.json(), + [ + { + 'id': str(dm.pk), + 'sender': {'username': self.human_user2.username, 'display_name': self.human_user2.display_name}, + 'recipient': {'username': self.human_user1.username, 'display_name': self.human_user1.display_name}, + 'timestamp': localtime(dm.timestamp).isoformat(), + 'in_reply_to': None, + 'subject': dm.subject, + 'has_responded': True, + } + ], + ) + + dm.deleted_by_recipient = True + dm.save() + resp = c.get(reverse('api:my-messages-received')) + self.assertEqual(resp.json(), []) + + def test_DirectMessagesSent(self): + c = Client() + resp = c.get(reverse('api:my-messages-sent')) + self.assertEqual(resp.status_code, 403) + c.force_login(self.human_user1) + + resp = c.get(reverse('api:my-messages-sent')) + self.assertEqual(resp.json(), []) + + dm = DirectMessage( + conference=self.conference1, + sender=self.human_user1, + recipient=self.human_user2, + timestamp=datetime(2020, 1, 2, 3, 4, 5, tzinfo=UTC), + subject='Test Message', + body='Message Body', + ) + dm.save() + resp = c.get(reverse('api:my-messages-sent')) + self.assertEqual( + resp.json(), + [ + { + 'id': str(dm.pk), + 'sender': {'username': self.human_user1.username, 'display_name': self.human_user1.display_name}, + 'recipient': {'username': self.human_user2.username, 'display_name': self.human_user2.display_name}, + 'timestamp': localtime(dm.timestamp).isoformat(), + 'in_reply_to': None, + 'subject': dm.subject, + } + ], + ) + + dm.deleted_by_recipient = True + dm.save() + resp = c.get(reverse('api:my-messages-sent')) + self.assertEqual(len(resp.json()), 1) + + dm.deleted_by_sender = True + dm.save() + resp = c.get(reverse('api:my-messages-sent')) + self.assertEqual(resp.json(), []) + + def test_DirectMessageReceived(self): + dm = DirectMessage( + conference=self.conference1, + sender=self.human_user2, + recipient=self.human_user1, + timestamp=datetime(2020, 1, 2, 3, 4, 5, tzinfo=UTC), + subject='Test Message', + body='Message Body', + ) + dm.save() + + other_dm = DirectMessage( + conference=self.conference1, + sender=self.human_user2, + recipient=self.human_user2, + timestamp=datetime(2020, 1, 2, 3, 4, 5, tzinfo=UTC), + subject='Test Message', + body='Message Body', + ) + other_dm.save() + + c = Client() + resp = c.get(reverse('api:my-message-received', kwargs={'pk': str(dm.pk)})) + self.assertEqual(resp.status_code, 403) + c.force_login(self.human_user1) + + resp = c.get(reverse('api:my-message-received', kwargs={'pk': str(other_dm.pk)})) + self.assertEqual(resp.status_code, 404) + + resp = c.get(reverse('api:my-message-received', kwargs={'pk': str(dm.pk)})) + self.assertEqual( + resp.json(), + { + 'id': str(dm.pk), + 'sender': {'username': self.human_user2.username, 'display_name': self.human_user2.display_name}, + 'recipient': {'username': self.human_user1.username, 'display_name': self.human_user1.display_name}, + 'timestamp': localtime(dm.timestamp).isoformat(), + 'in_reply_to': None, + 'subject': dm.subject, + 'body': dm.body, + 'has_responded': False, + }, + ) + + dm.has_responded = True + dm.deleted_by_sender = True + dm.save() + resp = c.get(reverse('api:my-message-received', kwargs={'pk': str(dm.pk)})) + self.assertEqual( + resp.json(), + { + 'id': str(dm.pk), + 'sender': {'username': self.human_user2.username, 'display_name': self.human_user2.display_name}, + 'recipient': {'username': self.human_user1.username, 'display_name': self.human_user1.display_name}, + 'timestamp': localtime(dm.timestamp).isoformat(), + 'in_reply_to': None, + 'subject': dm.subject, + 'body': dm.body, + 'has_responded': True, + }, + ) + + dm.deleted_by_recipient = True + dm.save() + resp = c.get(reverse('api:my-message-received', kwargs={'pk': str(other_dm.pk)})) + self.assertEqual(resp.status_code, 404) + + def test_DirectMessageSent(self): + dm = DirectMessage( + conference=self.conference1, + sender=self.human_user1, + recipient=self.human_user2, + timestamp=datetime(2020, 1, 2, 3, 4, 5, tzinfo=UTC), + subject='Test Message', + body='Message Body', + ) + dm.save() + + other_dm = DirectMessage( + conference=self.conference1, + sender=self.human_user2, + recipient=self.human_user2, + timestamp=datetime(2020, 1, 2, 3, 4, 5, tzinfo=UTC), + subject='Test Message', + body='Message Body', + ) + other_dm.save() + + c = Client() + resp = c.get(reverse('api:my-message-sent', kwargs={'pk': str(dm.pk)})) + self.assertEqual(resp.status_code, 403) + c.force_login(self.human_user1) + + resp = c.get(reverse('api:my-message-sent', kwargs={'pk': str(other_dm.pk)})) + self.assertEqual(resp.status_code, 404) + + resp = c.get(reverse('api:my-message-sent', kwargs={'pk': str(dm.pk)})) + self.assertEqual( + resp.json(), + { + 'id': str(dm.pk), + 'sender': {'username': self.human_user1.username, 'display_name': self.human_user1.display_name}, + 'recipient': {'username': self.human_user2.username, 'display_name': self.human_user2.display_name}, + 'timestamp': localtime(dm.timestamp).isoformat(), + 'in_reply_to': None, + 'subject': dm.subject, + 'body': dm.body, + }, + ) + + dm.deleted_by_recipient = True + dm.save() + resp = c.get(reverse('api:my-message-sent', kwargs={'pk': str(dm.pk)})) + self.assertEqual( + resp.json(), + { + 'id': str(dm.pk), + 'sender': {'username': self.human_user1.username, 'display_name': self.human_user1.display_name}, + 'recipient': {'username': self.human_user2.username, 'display_name': self.human_user2.display_name}, + 'timestamp': localtime(dm.timestamp).isoformat(), + 'in_reply_to': None, + 'subject': dm.subject, + 'body': dm.body, + }, + ) + + dm.deleted_by_sender = True + dm.save() + resp = c.get(reverse('api:my-message-sent', kwargs={'pk': str(other_dm.pk)})) + self.assertEqual(resp.status_code, 404) + + def test_DirectMessagesSend(self): + c = Client() + resp = c.post(reverse('api:my-send-message')) + self.assertEqual(resp.status_code, 403) + c.force_login(self.human_user1) + + with freeze_time(datetime(2020, 1, 2, 3, 4, 5, tzinfo=UTC)): + resp = c.post( + reverse('api:my-send-message'), + { + 'recipient': self.human_user2.username, + 'subject': 'Message Subject', + 'body': 'Message Body', + }, + ) + self.assertEqual(resp.status_code, 201) + self.assertEqual(DirectMessage.objects.all().count(), 1) + dm = DirectMessage.objects.get() + self.assertEqual(dm.conference, self.conference1) + self.assertEqual(dm.sender, self.human_user1) + self.assertEqual(dm.recipient, self.human_user2) + self.assertEqual(dm.timestamp, datetime(2020, 1, 2, 3, 4, 5, tzinfo=UTC)) + self.assertEqual(dm.subject, 'Message Subject') + self.assertEqual(dm.body, 'Message Body') + self.assertFalse(dm.was_read) + self.assertFalse(dm.has_responded) + self.assertFalse(dm.deleted_by_recipient) + + with freeze_time(datetime(2020, 3, 5, 7, 9, 11, tzinfo=UTC)): + resp = c.post( + reverse('api:my-send-message'), + { + 'recipient': self.human_user2.username, + 'subject': 'Message Subject2', + 'body': 'Message Body2', + }, + ) + + self.assertEqual(DirectMessage.objects.all().count(), 2) + dm2 = DirectMessage.objects.get(pk=resp.json()['id']) + self.assertEqual(dm2.timestamp, datetime(2020, 3, 5, 7, 9, 11, tzinfo=UTC)) + + self.human_user1.shadow_banned = True + self.human_user1.save() + with freeze_time(datetime(2020, 3, 5, 7, 9, 11, tzinfo=UTC)): + resp = c.post( + reverse('api:my-send-message'), + { + 'recipient': self.human_user2.username, + 'subject': 'Message Subject, Sender is shadow banned', + 'body': 'Message Body, Sender is shadow banned', + }, + ) + + self.assertEqual(DirectMessage.objects.all().count(), 3) + dm3 = DirectMessage.objects.get(pk=resp.json()['id']) + self.assertTrue(dm3.deleted_by_recipient) + + def test_DirectMessageDelete(self): + c = Client() + resp = c.delete(reverse('api:my-delete-message', kwargs={'pk': str(uuid.uuid4())})) + self.assertEqual(resp.status_code, 403) + c.force_login(self.human_user1) + + dm = DirectMessage( + conference=self.conference1, + sender=self.human_user1, + recipient=self.human_user2, + timestamp=datetime(2020, 5, 4, 3, 2, 1, tzinfo=UTC), + subject='Test Message', + body='Message Body', + ) + dm.save() + + resp = c.delete(reverse('api:my-delete-message', kwargs={'pk': str(dm.pk)})) + self.assertEqual(resp.status_code, 204) + dm.refresh_from_db() + self.assertTrue(dm.deleted_by_sender) + self.assertFalse(dm.deleted_by_recipient) + + dm = DirectMessage( + conference=self.conference1, + sender=self.human_user2, + recipient=self.human_user1, + timestamp=datetime(2020, 1, 2, 3, 4, 5, tzinfo=UTC), + subject='Test Message2', + body='Message Body2', + ) + dm.save() + resp = c.delete(reverse('api:my-delete-message', kwargs={'pk': str(dm.pk)})) + self.assertEqual(resp.status_code, 204) + dm.refresh_from_db() + self.assertFalse(dm.deleted_by_sender) + self.assertTrue(dm.deleted_by_recipient) + + dm = DirectMessage( + conference=self.conference1, + sender=self.human_user2, + recipient=self.human_user1, + timestamp=datetime(2020, 2, 2, 2, 2, 2, tzinfo=UTC), + deleted_by_sender=True, + subject='Test Message3', + body='Message Body3', + ) + dm.save() + resp = c.delete(reverse('api:my-delete-message', kwargs={'pk': str(dm.pk)})) + self.assertEqual(resp.status_code, 204) + self.assertEqual(DirectMessage.objects.filter(pk=dm.pk).count(), 0) diff --git a/src/api/urls.py b/src/api/urls.py index d10c632e9ea06adbb8e5905bf619a5fbd03f60ee..8b618b9743729cffacb04f7193e364b5eb4cd352 100644 --- a/src/api/urls.py +++ b/src/api/urls.py @@ -4,7 +4,7 @@ from rest_framework.urlpatterns import format_suffix_patterns from django.conf import settings from django.urls import path -from .views import api_root, assemblies, badges, bbb, conferencemember, conferences, events, maps, metanav, rooms, schedule, users, workadventure +from .views import api_root, assemblies, badges, bbb, conferencemember, conferences, events, maps, messages, metanav, rooms, schedule, users, workadventure app_name = 'api' urlpatterns = [ @@ -13,6 +13,14 @@ urlpatterns = [ path('me', users.profile, name='profile'), path('me/badges.zip', users.BadgeExportView.as_view(), name='badge-export'), path('me/badges', users.badges, name='badges'), + path('me/events', events.EventMyFavorites.as_view(), name='my-events'), + path('me/events/<uuid:pk>', events.EventMyFavoritesSet.as_view(), name='my-events-set'), + path('me/received-messages/', messages.DirectMessagesReceived.as_view(), name='my-messages-received'), + path('me/sent-messages/', messages.DirectMessagesSent.as_view(), name='my-messages-sent'), + path('me/received-messages/<uuid:pk>', messages.DirectMessageReceived.as_view(), name='my-message-received'), + path('me/sent-messages/<uuid:pk>', messages.DirectMessageSent.as_view(), name='my-message-sent'), + path('me/send-message', messages.DirectMessageSend.as_view(), name='my-send-message'), + path('me/delete-message/<uuid:pk>', messages.DirectMessageDelete.as_view(), name='my-delete-message'), path('me/friends', users.friends, name='friends'), path('me/timeline', users.UserTimelineList.as_view(), name='timeline-list'), # conference-specific views diff --git a/src/api/views/events.py b/src/api/views/events.py index 44aec250388c25dbbe17f0e5cb5d2264e22730df..dccc9dd8ee1b305a599b6fb831013b0bc75714b9 100644 --- a/src/api/views/events.py +++ b/src/api/views/events.py @@ -1,4 +1,6 @@ -from rest_framework import generics +from rest_framework import generics, permissions +from rest_framework.response import Response +from rest_framework.views import APIView from django.shortcuts import get_object_or_404 @@ -21,3 +23,22 @@ class EventDetail(ConferenceSlugMixin, generics.RetrieveAPIView): def get_object(self, **kwargs): event_id = self.request.resolver_match.kwargs['pk'] return get_object_or_404(Event.objects.conference_accessible(conference=self.conference), pk=event_id) + + +class EventMyFavorites(ConferenceSlugMixin, generics.ListAPIView): + permission_classes = [permissions.IsAuthenticated] + + def list(self, request, *args, **kwargs): + return Response(self.request.user.favorite_events.values_list('pk', flat=True)) + + +class EventMyFavoritesSet(ConferenceSlugMixin, APIView): + permission_classes = [permissions.IsAuthenticated] + + def put(self, request, *args, pk=None, **kwargs): + self.request.user.favorite_events.add(*Event.objects.conference_accessible(conference=self.conference).filter(pk=pk)) + return Response(True) + + def delete(self, request, *args, pk=None, **kwargs): + self.request.user.favorite_events.remove(*Event.objects.conference_accessible(conference=self.conference).filter(pk=pk)) + return Response(True) diff --git a/src/api/views/messages.py b/src/api/views/messages.py new file mode 100644 index 0000000000000000000000000000000000000000..01a3ecf3266d9f84dc9f302736355978944ee579 --- /dev/null +++ b/src/api/views/messages.py @@ -0,0 +1,101 @@ +from rest_framework import generics, permissions + +from django.db.models import Q + +from core.models.messages import DirectMessage + +from ..serializers import ( + DirectMessageSendSerializer, + DirectMessageSerializerReceived, + DirectMessageSerializerReceivedShort, + DirectMessageSerializerSent, + DirectMessageSerializerSentShort, +) +from .mixins import ConferenceSlugMixin + + +class DirectMessagesReceived(ConferenceSlugMixin, generics.ListAPIView): + permission_classes = [permissions.IsAuthenticated] + serializer_class = DirectMessageSerializerReceivedShort + + def get_queryset(self): + return ( + DirectMessage.objects.filter(conference=self.conference, recipient=self.request.user, deleted_by_recipient=False) + .select_related('recipient', 'sender') + .order_by('-timestamp') + ) + + +class DirectMessagesSent(ConferenceSlugMixin, generics.ListAPIView): + permission_classes = [permissions.IsAuthenticated] + serializer_class = DirectMessageSerializerSentShort + + def get_queryset(self): + return ( + DirectMessage.objects.filter(conference=self.conference, sender=self.request.user, deleted_by_sender=False) + .select_related('recipient', 'sender') + .order_by('-timestamp') + ) + + +class DirectMessageReceived(ConferenceSlugMixin, generics.RetrieveUpdateAPIView): + permission_classes = [permissions.IsAuthenticated] + serializer_class = DirectMessageSerializerReceived + + def get_queryset(self): + return ( + DirectMessage.objects.filter(conference=self.conference, recipient=self.request.user, deleted_by_recipient=False) + .select_related('recipient', 'sender') + .order_by('-timestamp') + ) + + +class DirectMessageSent(ConferenceSlugMixin, generics.RetrieveAPIView): + permission_classes = [permissions.IsAuthenticated] + serializer_class = DirectMessageSerializerSent + + def get_queryset(self): + return ( + DirectMessage.objects.filter(conference=self.conference, sender=self.request.user, deleted_by_sender=False) + .select_related('recipient', 'sender') + .order_by('-timestamp') + ) + + +class DirectMessageSend(ConferenceSlugMixin, generics.CreateAPIView): + permission_classes = [permissions.IsAuthenticated] + serializer_class = DirectMessageSendSerializer + + def perform_create(self, serializer: DirectMessageSendSerializer): + serializer.save( + conference=self.conference, + sender=self.request.user, + deleted_by_recipient=self.request.user != serializer.validated_data['recipient'] and self.request.user.shadow_banned, + ) + + +class DirectMessageDelete(ConferenceSlugMixin, generics.DestroyAPIView): + permission_classes = [permissions.IsAuthenticated] + + def get_queryset(self): + return DirectMessage.objects.filter( + Q(conference=self.conference, recipient=self.request.user, deleted_by_recipient=False) + | Q(conference=self.conference, sender=self.request.user, deleted_by_sender=False) + ) + + def perform_destroy(self, instance: DirectMessage): + if instance.sender == self.request.user: + if instance.deleted_by_recipient: + instance.delete() + return + + instance.deleted_by_sender = True + instance.save(update_fields=['deleted_by_sender']) + + elif instance.recipient == self.request.user: + if instance.deleted_by_sender: + instance.delete() + return + + instance.deleted_by_recipient = True + instance.save(update_fields=['deleted_by_recipient'])