attachments.py 3.5 KB

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