diff --git a/src/api/serializers.py b/src/api/serializers.py index e0341bd899ad99a43ac6e8fe8549e497d41c0611..2ac02e23e81d31d04572c295787c421cc36da605 100644 --- a/src/api/serializers.py +++ b/src/api/serializers.py @@ -78,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) @@ -307,6 +313,14 @@ class PlatformUserVisibleSerializer(serializers.ModelSerializer): 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 @@ -345,3 +359,13 @@ class DirectMessageSerializerReceived(HubModelSerializer): 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/messages.py b/src/api/tests/messages.py index b80b0e9f64a3275ee6a71471de3f4f2ed875460e..77b9649dce27d2a15509435bb31c341290696783 100644 --- a/src/api/tests/messages.py +++ b/src/api/tests/messages.py @@ -1,6 +1,7 @@ import uuid from datetime import datetime +from freezegun import freeze_time from pytz import UTC from django.test import Client, TestCase, override_settings @@ -259,3 +260,61 @@ class MessagesTestCase(TestCase): 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) diff --git a/src/api/urls.py b/src/api/urls.py index cc96f91ff392a4374f4442305095f678406579ec..b92ea88dca3a9781b96bf6d0306ee9fa74f345ef 100644 --- a/src/api/urls.py +++ b/src/api/urls.py @@ -19,6 +19,7 @@ urlpatterns = [ 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/friends', users.friends, name='friends'), path('me/timeline', users.UserTimelineList.as_view(), name='timeline-list'), # conference-specific views diff --git a/src/api/views/messages.py b/src/api/views/messages.py index 3cf37d2bc6bd03435610d793da8b062542817b0f..1e9adb8176771bc3d75ee2fb19fb7a0949e25a40 100644 --- a/src/api/views/messages.py +++ b/src/api/views/messages.py @@ -1,8 +1,9 @@ +from django.conf import settings from rest_framework import generics, permissions from core.models.messages import DirectMessage -from ..serializers import DirectMessageSerializerReceived, DirectMessageSerializerReceivedShort, DirectMessageSerializerSent, DirectMessageSerializerSentShort +from ..serializers import DirectMessageSerializerReceived, DirectMessageSerializerReceivedShort, DirectMessageSerializerSent, DirectMessageSerializerSentShort, DirectMessageSendSerializer from .mixins import ConferenceSlugMixin @@ -52,3 +53,15 @@ class DirectMessageSent(ConferenceSlugMixin, generics.RetrieveAPIView): .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, + )