Browse Source

Moved new attachment creation to serializer

Rafał Pitoń 7 years ago
parent
commit
ec62b5bf54

+ 4 - 97
misago/threads/api/attachments.py

@@ -2,115 +2,22 @@ from rest_framework import viewsets
 from rest_framework.response import Response
 
 from django.core.exceptions import PermissionDenied
-from django.template.defaultfilters import filesizeformat
-from django.utils import six
 from django.utils.translation import ugettext as _
 
 from misago.acl import add_acl
-from misago.threads.models import Attachment, AttachmentType
-from misago.threads.serializers import AttachmentSerializer
+from misago.threads.serializers import AttachmentSerializer, NewAttachmentSerializer
 
 
 IMAGE_EXTENSIONS = ('jpg', 'jpeg', 'png', 'gif')
 
 
-class UploadError(Exception):
-    pass
-
-
 class AttachmentViewSet(viewsets.ViewSet):
     def create(self, request):
         if not request.user.acl_cache['max_attachment_size']:
             raise PermissionDenied(_("You don't have permission to upload new files."))
-        try:
-            return self.create_attachment(request)
-        except UploadError as e:
-            return Response({'upload': e.args}, status=400)
-
-    def create_attachment(self, request):
-        upload = request.FILES.get('upload')
-        if not upload:
-            raise UploadError(_("No file has been uploaded."))
-
-        user_roles = set(r.pk for r in request.user.get_roles())
-        filetype = validate_filetype(upload, user_roles)
-        validate_filesize(upload, filetype, request.user.acl_cache['max_attachment_size'])
-
-        attachment = Attachment(
-            secret=Attachment.generate_new_secret(),
-            filetype=filetype,
-            size=upload.size,
-            uploader=request.user,
-            uploader_name=request.user.username,
-            uploader_slug=request.user.slug,
-            uploader_ip=request.user_ip,
-            filename=upload.name,
-        )
 
-        if is_upload_image(upload):
-            try:
-                attachment.set_image(upload)
-            except IOError:
-                raise UploadError(_("Uploaded image was corrupted or invalid."))
-        else:
-            attachment.set_file(upload)
-
-        attachment.save()
+        serializer = NewAttachmentSerializer(data=request.data, context={'request': request})
+        serializer.is_valid(raise_exception=True)
+        attachment = serializer.save()
         add_acl(request.user, attachment)
-
         return Response(AttachmentSerializer(attachment, context={'user': request.user}).data)
-
-
-def validate_filetype(upload, user_roles):
-    filename = upload.name.strip().lower()
-
-    queryset = AttachmentType.objects.filter(status=AttachmentType.ENABLED)
-    for filetype in queryset.prefetch_related('limit_uploads_to'):
-        for extension in filetype.extensions_list:
-            if filename.endswith('.%s' % extension):
-                break
-        else:
-            continue
-
-        if filetype.mimetypes_list and upload.content_type not in filetype.mimetypes_list:
-            continue
-
-        if filetype.limit_uploads_to.exists():
-            allowed_roles = set(r.pk for r in filetype.limit_uploads_to.all())
-            if not user_roles & allowed_roles:
-                continue
-
-        return filetype
-
-    raise UploadError(_("You can't upload files of this type."))
-
-
-def validate_filesize(upload, filetype, hard_limit):
-    if upload.size > hard_limit * 1024:
-        message = _("You can't upload files larger than %(limit)s (your file has %(upload)s).")
-        raise UploadError(
-            message % {
-                'upload': filesizeformat(upload.size).rstrip('.0'),
-                'limit': filesizeformat(hard_limit * 1024).rstrip('.0'),
-            }
-        )
-
-    if filetype.size_limit and upload.size > filetype.size_limit * 1024:
-        message = _(
-            "You can't upload files of this type larger than %(limit)s (your file has %(upload)s)."
-        )
-        raise UploadError(
-            message % {
-                'upload': filesizeformat(upload.size).rstrip('.0'),
-                'limit': filesizeformat(filetype.size_limit * 1024).rstrip('.0'),
-            }
-        )
-
-
-def is_upload_image(upload):
-    filename = upload.name.strip().lower()
-
-    for extension in IMAGE_EXTENSIONS:
-        if filename.endswith('.%s' % extension):
-            return True
-    return False

+ 1 - 1
misago/threads/serializers/__init__.py

@@ -8,6 +8,6 @@ from .post import PostSerializer
 from .feed import FeedSerializer
 from .postedit import PostEditSerializer
 from .postlike import PostLikeSerializer
-from .attachment import AttachmentSerializer
+from .attachment import AttachmentSerializer, NewAttachmentSerializer
 from .poll import EditPollSerializer, NewPollSerializer, PollChoiceSerializer, PollSerializer
 from .pollvote import NewVoteSerializer, PollVoteSerializer

+ 102 - 2
misago/threads/serializers/attachment.py

@@ -1,8 +1,13 @@
 from rest_framework import serializers
 
+from django.template.defaultfilters import filesizeformat
 from django.urls import reverse
+from django.utils.translation import ugettext as _
 
-from misago.threads.models import Attachment
+from misago.threads.models import Attachment, AttachmentType
+
+
+IMAGE_EXTENSIONS = ('jpg', 'jpeg', 'png', 'gif')
 
 
 class AttachmentSerializer(serializers.ModelSerializer):
@@ -44,7 +49,8 @@ class AttachmentSerializer(serializers.ModelSerializer):
         return obj.filetype.name
 
     def get_uploader_ip(self, obj):
-        if 'user' in self.context and self.context['user'].acl_cache['can_see_users_ips']:
+        user = self.context.get('user')
+        if user and user.acl_cache['can_see_users_ips']:
             return obj.uploader_ip
         else:
             return None
@@ -66,3 +72,97 @@ class AttachmentSerializer(serializers.ModelSerializer):
             )
         else:
             return None
+
+
+class NewAttachmentSerializer(serializers.Serializer):
+    upload = serializers.FileField()
+
+    def validate_upload(self, upload):
+        user = self.context['request'].user
+        user_roles = set(r.pk for r in user.get_roles())
+        max_attachment_size = user.acl_cache['max_attachment_size']
+        
+        self.filetype = self.validate_filetype(upload, user_roles)
+        self.validate_filesize(upload, self.filetype, max_attachment_size)
+
+        return upload
+
+    def validate_filetype(self, upload, user_roles):
+        filename = upload.name.strip().lower()
+
+        queryset = AttachmentType.objects.filter(status=AttachmentType.ENABLED)
+        for filetype in queryset.prefetch_related('limit_uploads_to'):
+            for extension in filetype.extensions_list:
+                if filename.endswith('.%s' % extension):
+                    break
+            else:
+                continue
+
+            if filetype.mimetypes_list and upload.content_type not in filetype.mimetypes_list:
+                continue
+
+            if filetype.limit_uploads_to.exists():
+                allowed_roles = set(r.pk for r in filetype.limit_uploads_to.all())
+                if not user_roles & allowed_roles:
+                    continue
+
+            return filetype
+
+        raise serializers.ValidationError(_("You can't upload files of this type."))
+
+    def validate_filesize(self, upload, filetype, hard_limit):
+        if upload.size > hard_limit * 1024:
+            message = _("You can't upload files larger than %(limit)s (your file has %(upload)s).")
+            raise serializers.ValidationError(
+                message % {
+                    'upload': filesizeformat(upload.size).rstrip('.0'),
+                    'limit': filesizeformat(hard_limit * 1024).rstrip('.0'),
+                }
+            )
+
+        if filetype.size_limit and upload.size > filetype.size_limit * 1024:
+            message = _(
+                "You can't upload files of this type larger than %(limit)s (your file has %(upload)s)."
+            )
+            raise serializers.ValidationError(
+                message % {
+                    'upload': filesizeformat(upload.size).rstrip('.0'),
+                    'limit': filesizeformat(filetype.size_limit * 1024).rstrip('.0'),
+                }
+            )
+
+    def is_upload_image(self, upload):
+        filename = upload.name.strip().lower()
+
+        for extension in IMAGE_EXTENSIONS:
+            if filename.endswith('.%s' % extension):
+                return True
+        return False
+
+    def save(self):
+        request = self.context['request']
+        upload = self.validated_data['upload']
+
+        self.instance = Attachment(
+            secret=Attachment.generate_new_secret(),
+            filetype=self.filetype,
+            size=upload.size,
+            uploader=request.user,
+            uploader_name=request.user.username,
+            uploader_slug=request.user.slug,
+            uploader_ip=request.user_ip,
+            filename=upload.name,
+        )
+
+        if self.is_upload_image(upload):
+            try:
+                self.instance.set_image(upload)
+            except IOError:
+                raise serializers.ValidationError({
+                    'upload': [_("Uploaded image was corrupted or invalid.")],
+                })
+        else:
+            self.instance.set_file(upload)
+
+        self.instance.save()
+        return self.instance

+ 1 - 1
misago/threads/tests/test_attachments_api.py

@@ -56,7 +56,7 @@ class AttachmentsApiTestCase(AuthenticatedUserTestCase):
         response = self.client.post(self.api_link)
         self.assertEqual(response.status_code, 400)
         self.assertEqual(response.json(), {
-            'upload': ["No file has been uploaded."],
+            'upload': ["No file was submitted."],
         })
 
     def test_invalid_extension(self):