attachments.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116
  1. from rest_framework import viewsets
  2. from rest_framework.response import Response
  3. from django.core.exceptions import PermissionDenied
  4. from django.template.defaultfilters import filesizeformat
  5. from django.utils import six
  6. from django.utils.translation import ugettext as _
  7. from misago.acl import add_acl
  8. from misago.threads.models import Attachment, AttachmentType
  9. from misago.threads.serializers import AttachmentSerializer
  10. IMAGE_EXTENSIONS = ('jpg', 'jpeg', 'png', 'gif')
  11. class UploadError(Exception):
  12. pass
  13. class AttachmentViewSet(viewsets.ViewSet):
  14. def create(self, request):
  15. if not request.user.acl_cache['max_attachment_size']:
  16. raise PermissionDenied(_("You don't have permission to upload new files."))
  17. try:
  18. return self.create_attachment(request)
  19. except UploadError as e:
  20. return Response({'upload': e.args}, status=400)
  21. def create_attachment(self, request):
  22. upload = request.FILES.get('upload')
  23. if not upload:
  24. raise UploadError(_("No file has been uploaded."))
  25. user_roles = set(r.pk for r in request.user.get_roles())
  26. filetype = validate_filetype(upload, user_roles)
  27. validate_filesize(upload, filetype, request.user.acl_cache['max_attachment_size'])
  28. attachment = Attachment(
  29. secret=Attachment.generate_new_secret(),
  30. filetype=filetype,
  31. size=upload.size,
  32. uploader=request.user,
  33. uploader_name=request.user.username,
  34. uploader_slug=request.user.slug,
  35. uploader_ip=request.user_ip,
  36. filename=upload.name,
  37. )
  38. if is_upload_image(upload):
  39. try:
  40. attachment.set_image(upload)
  41. except IOError:
  42. raise UploadError(_("Uploaded image was corrupted or invalid."))
  43. else:
  44. attachment.set_file(upload)
  45. attachment.save()
  46. add_acl(request.user, attachment)
  47. return Response(AttachmentSerializer(attachment, context={'user': request.user}).data)
  48. def validate_filetype(upload, user_roles):
  49. filename = upload.name.strip().lower()
  50. queryset = AttachmentType.objects.filter(status=AttachmentType.ENABLED)
  51. for filetype in queryset.prefetch_related('limit_uploads_to'):
  52. for extension in filetype.extensions_list:
  53. if filename.endswith('.%s' % extension):
  54. break
  55. else:
  56. continue
  57. if filetype.mimetypes_list and upload.content_type not in filetype.mimetypes_list:
  58. continue
  59. if filetype.limit_uploads_to.exists():
  60. allowed_roles = set(r.pk for r in filetype.limit_uploads_to.all())
  61. if not user_roles & allowed_roles:
  62. continue
  63. return filetype
  64. raise UploadError(_("You can't upload files of this type."))
  65. def validate_filesize(upload, filetype, hard_limit):
  66. if upload.size > hard_limit * 1024:
  67. message = _("You can't upload files larger than %(limit)s (your file has %(upload)s).")
  68. raise UploadError(
  69. message % {
  70. 'upload': filesizeformat(upload.size).rstrip('.0'),
  71. 'limit': filesizeformat(hard_limit * 1024).rstrip('.0'),
  72. }
  73. )
  74. if filetype.size_limit and upload.size > filetype.size_limit * 1024:
  75. message = _(
  76. "You can't upload files of this type larger than %(limit)s (your file has %(upload)s)."
  77. )
  78. raise UploadError(
  79. message % {
  80. 'upload': filesizeformat(upload.size).rstrip('.0'),
  81. 'limit': filesizeformat(filetype.size_limit * 1024).rstrip('.0'),
  82. }
  83. )
  84. def is_upload_image(upload):
  85. filename = upload.name.strip().lower()
  86. for extension in IMAGE_EXTENSIONS:
  87. if filename.endswith('.%s' % extension):
  88. return True
  89. return False