diff --git a/apimail/api/serializers/composed_message.py b/apimail/api/serializers/composed_message.py index 705b4621932473ee0692668e81962fe7517ecf47..827ae4f05040d35e3e38afc7876e36c3017bd497 100644 --- a/apimail/api/serializers/composed_message.py +++ b/apimail/api/serializers/composed_message.py @@ -9,10 +9,7 @@ from ...models import ( ComposedMessage, ComposedMessageAPIResponse, AttachmentFile, ) -from ..serializers import ( - ComposedMessageAPIResponseSerializer, - AttachmentFileSerializer -) +from ..serializers import AttachmentFileSerializer class ComposedMessageAPIResponseSerializer(serializers.ModelSerializer): diff --git a/apimail/api/views/__init__.py b/apimail/api/views/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e14b3a275032bf0d06b744ae05edd80b9504a6ba --- /dev/null +++ b/apimail/api/views/__init__.py @@ -0,0 +1,21 @@ +__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)" +__license__ = "AGPL v3" + + +from .account import EmailAccountListAPIView, UserEmailAccountAccessListAPIView + +from .attachment import AttachmentFileCreateAPIView + +from .event import EventListAPIView, EventRetrieveAPIView + +from .tag import UserTagCreateAPIView, UserTagDestroyAPIView, UserTagListAPIView + +from .composed_message import ( + ComposedMessageCreateAPIView, ComposedMessageUpdateAPIView, + ComposedMessageDestroyAPIView, ComposedMessageListAPIView +) + +from .stored_message import ( + StoredMessageListAPIView, StoredMessageRetrieveAPIView, + StoredMessageUpdateReadAPIView, StoredMessageUpdateTagAPIView +) diff --git a/apimail/api/views/account.py b/apimail/api/views/account.py new file mode 100644 index 0000000000000000000000000000000000000000..1c91dbbf85be065e27279487f0bd9a925e31faba --- /dev/null +++ b/apimail/api/views/account.py @@ -0,0 +1,32 @@ +__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)" +__license__ = "AGPL v3" + +from rest_framework.generics import ListAPIView +from rest_framework.permissions import IsAdminUser, IsAuthenticated + +from ...models import EmailAccount + +from ..serializers import EmailAccountSerializer, EmailAccountAccessSerializer + + +class EmailAccountListAPIView(ListAPIView): + permission_classes = (IsAdminUser,) + queryset = EmailAccount.objects.all() + serializer_class = EmailAccountSerializer + + +class UserEmailAccountAccessListAPIView(ListAPIView): + """ + ListAPIView returning request.user's email account accesses. + """ + + permission_classes = (IsAuthenticated,) + serializer_class = EmailAccountAccessSerializer + + def get_queryset(self): + queryset = self.request.user.email_account_accesses.all() + if self.request.query_params.get('current', None) == 'true': + queryset = queryset.current() + if self.request.query_params.get('cansend', None) == 'true': + queryset = queryset.can_send() + return queryset diff --git a/apimail/api/views/attachment.py b/apimail/api/views/attachment.py new file mode 100644 index 0000000000000000000000000000000000000000..95b70db840baa39974c31736a4f4e5c5eb8dbbb5 --- /dev/null +++ b/apimail/api/views/attachment.py @@ -0,0 +1,17 @@ +__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)" +__license__ = "AGPL v3" + + +from rest_framework.generics import CreateAPIView +from rest_framework.parsers import FormParser, MultiPartParser +from rest_framework.permissions import IsAuthenticated + +from ...models import AttachmentFile +from ..serializers import AttachmentFileSerializer + + +class AttachmentFileCreateAPIView(CreateAPIView): + permission_classes = (IsAuthenticated,) + queryset = AttachmentFile.objects.all() + serializer_class = AttachmentFileSerializer + parser_classes = [FormParser, MultiPartParser,] diff --git a/apimail/api/views/composed_message.py b/apimail/api/views/composed_message.py new file mode 100644 index 0000000000000000000000000000000000000000..3a4584c6fcf5bd290656bd9e93da950029a59ef0 --- /dev/null +++ b/apimail/api/views/composed_message.py @@ -0,0 +1,78 @@ +__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)" +__license__ = "AGPL v3" + + +from rest_framework.generics import ( + CreateAPIView, ListAPIView, RetrieveAPIView, UpdateAPIView, DestroyAPIView +) + +from rest_framework.permissions import IsAdminUser, IsAuthenticated +from rest_framework.response import Response +from rest_framework import status + +from ...models import ComposedMessage +from ..serializers import ComposedMessageSerializer +from ...permissions import CanHandleComposedMessage + + +class ComposedMessageCreateAPIView(CreateAPIView): + permission_classes = (IsAuthenticated,) + queryset = ComposedMessage.objects.all() + serializer_class = ComposedMessageSerializer + lookup_field = 'uuid' + + def create(self, request, *args, **kwargs): + # Override rest_framework.mixins.CreateModelMixin.create in order + # to include request.user in data, and attachment_uuids for serializer.create + data = request.data + data['author'] = request.user.id + serializer = self.get_serializer(data=data) + serializer.is_valid(raise_exception=True) + # Attachment uuids passed as extra args to be popped in serializer.create + serializer.save(attachment_uuids=request.data['attachment_uuids']) + headers = self.get_success_headers(serializer.data) + return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) + + +class ComposedMessageUpdateAPIView(UpdateAPIView): + permission_classes = (IsAuthenticated, CanHandleComposedMessage,) + queryset = ComposedMessage.objects.all() + serializer_class = ComposedMessageSerializer + lookup_field = 'uuid' + + def update(self, request, *args, **kwargs): + # Override rest_framework.mixins.CreateModelMixin.update in order + # to include request.user in data, and attachment_uuids for serializer.update + partial = kwargs.pop('partial', False) + instance = self.get_object() + serializer = self.get_serializer(instance, data=request.data, partial=partial) + serializer.is_valid(raise_exception=True) + serializer.save(attachment_uuids=request.data['attachment_uuids']) + + if getattr(instance, '_prefetched_objects_cache', None): + # If 'prefetch_related' has been applied to a queryset, we need to + # forcibly invalidate the prefetch cache on the instance. + instance._prefetched_objects_cache = {} + + return Response(serializer.data) + + +class ComposedMessageDestroyAPIView(DestroyAPIView): + permission_classes = (IsAuthenticated, CanHandleComposedMessage,) + serializer_class = ComposedMessageSerializer + lookup_field = 'uuid' + + def get_queryset(self): + return ComposedMessage.objects.filter_for_user(self.request.user) + + +class ComposedMessageListAPIView(ListAPIView): + permission_classes = (IsAuthenticated,) + serializer_class = ComposedMessageSerializer + lookup_field = 'uuid' + + def get_queryset(self): + queryset = ComposedMessage.objects.filter_for_user(self.request.user) + if self.request.query_params.get('status', None) == 'draft': + queryset = queryset.filter(status=ComposedMessage.STATUS_DRAFT) + return queryset diff --git a/apimail/api/views/event.py b/apimail/api/views/event.py new file mode 100644 index 0000000000000000000000000000000000000000..cd62ead413ad7068beab749a068d29eb21e578bc --- /dev/null +++ b/apimail/api/views/event.py @@ -0,0 +1,23 @@ +__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)" +__license__ = "AGPL v3" + + +from rest_framework.generics import ListAPIView, RetrieveAPIView +from rest_framework.permissions import IsAdminUser + +from ...models import Event +from ..serializers import EventSerializer + + +class EventListAPIView(ListAPIView): + permission_classes = (IsAdminUser,) + queryset = Event.objects.all() + serializer_class = EventSerializer + lookup_field = 'uuid' + + +class EventRetrieveAPIView(RetrieveAPIView): + permission_classes = (IsAdminUser,) + queryset = Event.objects.all() + serializer_class = EventSerializer + lookup_field = 'uuid' diff --git a/apimail/api/views.py b/apimail/api/views/stored_message.py similarity index 53% rename from apimail/api/views.py rename to apimail/api/views/stored_message.py index 05fc3b93a4de59f7a2f8fc2f088a8caff9c68a78..ffb01b78364857f8cea8723a4ec59aa3a00b21c5 100644 --- a/apimail/api/views.py +++ b/apimail/api/views/stored_message.py @@ -8,139 +8,16 @@ from django.db.models import Q from django.shortcuts import get_object_or_404 from django.utils import timezone -from rest_framework.generics import ( - CreateAPIView, DestroyAPIView, ListAPIView, - RetrieveAPIView, UpdateAPIView) -from rest_framework.parsers import FormParser, MultiPartParser -from rest_framework.permissions import IsAdminUser, IsAuthenticated +from rest_framework.generics import ListAPIView, RetrieveAPIView, UpdateAPIView +from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response -from rest_framework import filters, status +from rest_framework import filters -from ..models import ( - EmailAccount, - AttachmentFile, - ComposedMessage, - Event, - StoredMessage, UserTag) -from ..permissions import ( - CanHandleComposedMessage, - CanHandleStoredMessage, CanReadStoredMessage) +from ...models import StoredMessage, UserTag +from ..serializers import StoredMessageSerializer -from .serializers import ( - EmailAccountSerializer, EmailAccountAccessSerializer, - AttachmentFileSerializer, - ComposedMessageSerializer, - EventSerializer, - StoredMessageSerializer, - UserTagSerializer) - - -class EmailAccountListAPIView(ListAPIView): - permission_classes = (IsAdminUser,) - queryset = EmailAccount.objects.all() - serializer_class = EmailAccountSerializer - - -class UserEmailAccountAccessListAPIView(ListAPIView): - """ - ListAPIView returning request.user's email account accesses. - """ - - permission_classes = (IsAuthenticated,) - serializer_class = EmailAccountAccessSerializer - - def get_queryset(self): - queryset = self.request.user.email_account_accesses.all() - if self.request.query_params.get('current', None) == 'true': - queryset = queryset.current() - if self.request.query_params.get('cansend', None) == 'true': - queryset = queryset.can_send() - return queryset - - -class AttachmentFileCreateAPIView(CreateAPIView): - permission_classes = (IsAuthenticated,) - queryset = AttachmentFile.objects.all() - serializer_class = AttachmentFileSerializer - parser_classes = [FormParser, MultiPartParser,] - - -class ComposedMessageCreateAPIView(CreateAPIView): - permission_classes = (IsAuthenticated,) - queryset = ComposedMessage.objects.all() - serializer_class = ComposedMessageSerializer - lookup_field = 'uuid' - - def create(self, request, *args, **kwargs): - # Override rest_framework.mixins.CreateModelMixin.create in order - # to include request.user in data, and attachment_uuids for serializer.create - data = request.data - data['author'] = request.user.id - serializer = self.get_serializer(data=data) - serializer.is_valid(raise_exception=True) - # Attachment uuids passed as extra args to be popped in serializer.create - serializer.save(attachment_uuids=request.data['attachment_uuids']) - headers = self.get_success_headers(serializer.data) - return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) - - -class ComposedMessageUpdateAPIView(UpdateAPIView): - permission_classes = (IsAuthenticated, CanHandleComposedMessage,) - queryset = ComposedMessage.objects.all() - serializer_class = ComposedMessageSerializer - lookup_field = 'uuid' - - def update(self, request, *args, **kwargs): - # Override rest_framework.mixins.CreateModelMixin.update in order - # to include request.user in data, and attachment_uuids for serializer.update - partial = kwargs.pop('partial', False) - instance = self.get_object() - serializer = self.get_serializer(instance, data=request.data, partial=partial) - serializer.is_valid(raise_exception=True) - serializer.save(attachment_uuids=request.data['attachment_uuids']) - - if getattr(instance, '_prefetched_objects_cache', None): - # If 'prefetch_related' has been applied to a queryset, we need to - # forcibly invalidate the prefetch cache on the instance. - instance._prefetched_objects_cache = {} - - return Response(serializer.data) - - -class ComposedMessageDestroyAPIView(DestroyAPIView): - permission_classes = (IsAuthenticated, CanHandleComposedMessage,) - serializer_class = ComposedMessageSerializer - lookup_field = 'uuid' - - def get_queryset(self): - return ComposedMessage.objects.filter_for_user(self.request.user) - - -class ComposedMessageListAPIView(ListAPIView): - permission_classes = (IsAuthenticated,) - serializer_class = ComposedMessageSerializer - lookup_field = 'uuid' - - def get_queryset(self): - queryset = ComposedMessage.objects.filter_for_user(self.request.user) - if self.request.query_params.get('status', None) == 'draft': - queryset = queryset.filter(status=ComposedMessage.STATUS_DRAFT) - return queryset - - -class EventListAPIView(ListAPIView): - permission_classes = (IsAdminUser,) - queryset = Event.objects.all() - serializer_class = EventSerializer - lookup_field = 'uuid' - - -class EventRetrieveAPIView(RetrieveAPIView): - permission_classes = (IsAdminUser,) - queryset = Event.objects.all() - serializer_class = EventSerializer - lookup_field = 'uuid' +from ...permissions import CanReadStoredMessage class StoredMessageFilterBackend(filters.BaseFilterBackend): @@ -275,37 +152,6 @@ class StoredMessageUpdateReadAPIView(UpdateAPIView): return Response() -class UserTagCreateAPIView(CreateAPIView): - permission_classes = (IsAuthenticated,) - queryset = UserTag.objects.all() - serializer_class = UserTagSerializer - - def create(self, request, *args, **kwargs): - data = request.data - data['user'] = request.user.id - serializer = self.get_serializer(data=data) - serializer.is_valid(raise_exception=True) - self.perform_create(serializer) - headers = self.get_success_headers(serializer.data) - return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) - - -class UserTagDestroyAPIView(DestroyAPIView): - permission_classes = (IsAuthenticated,) - serializer_class = UserTagSerializer - - def get_queryset(self): - return UserTag.objects.filter(user=self.request.user) - - -class UserTagListAPIView(ListAPIView): - permission_classes = (IsAuthenticated,) - serializer_class = UserTagSerializer - - def get_queryset(self): - return self.request.user.email_tags.all() - - class StoredMessageUpdateTagAPIView(UpdateAPIView): """ Adds or removes a user tag on a StoredMessage. diff --git a/apimail/api/views/tag.py b/apimail/api/views/tag.py new file mode 100644 index 0000000000000000000000000000000000000000..772ab43955454282e77da4c4a90029ed9325fe78 --- /dev/null +++ b/apimail/api/views/tag.py @@ -0,0 +1,44 @@ +__copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)" +__license__ = "AGPL v3" + + +from rest_framework.generics import ( + CreateAPIView, DestroyAPIView, ListAPIView, +) +from rest_framework.permissions import IsAuthenticated +from rest_framework.response import Response +from rest_framework import status + +from ...models import UserTag +from ..serializers import UserTagSerializer + + +class UserTagCreateAPIView(CreateAPIView): + permission_classes = (IsAuthenticated,) + queryset = UserTag.objects.all() + serializer_class = UserTagSerializer + + def create(self, request, *args, **kwargs): + data = request.data + data['user'] = request.user.id + serializer = self.get_serializer(data=data) + serializer.is_valid(raise_exception=True) + self.perform_create(serializer) + headers = self.get_success_headers(serializer.data) + return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) + + +class UserTagDestroyAPIView(DestroyAPIView): + permission_classes = (IsAuthenticated,) + serializer_class = UserTagSerializer + + def get_queryset(self): + return UserTag.objects.filter(user=self.request.user) + + +class UserTagListAPIView(ListAPIView): + permission_classes = (IsAuthenticated,) + serializer_class = UserTagSerializer + + def get_queryset(self): + return self.request.user.email_tags.all() diff --git a/apimail/models/account.py b/apimail/models/account.py index fb59da3d1db1ef86471d81717800aad1dcf081f6..cb2fb9e6617e49d0e97e49d11cc1db82754d1176 100644 --- a/apimail/models/account.py +++ b/apimail/models/account.py @@ -2,6 +2,8 @@ _copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)" __license__ = "AGPL v3" +import pytz + from django.conf import settings from django.core.exceptions import ValidationError from django.db import models diff --git a/apimail/models/composed_message.py b/apimail/models/composed_message.py index 654f911d2f7ea6285c438650a5fe88fc37885bd9..68b12370212384a1892b6acf1007dcf2643ba363 100644 --- a/apimail/models/composed_message.py +++ b/apimail/models/composed_message.py @@ -2,6 +2,7 @@ __copyright__ = "Copyright © Stichting SciPost (SciPost Foundation)" __license__ = "AGPL v3" +import pytz import uuid as uuid_lib from django.conf import settings