attachment.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. from rest_framework import serializers
  2. from django.template.defaultfilters import filesizeformat
  3. from django.urls import reverse
  4. from django.utils.translation import ugettext as _
  5. from misago.threads.models import Attachment, AttachmentType
  6. IMAGE_EXTENSIONS = ('jpg', 'jpeg', 'png', 'gif')
  7. class AttachmentSerializer(serializers.ModelSerializer):
  8. post = serializers.PrimaryKeyRelatedField(read_only=True)
  9. acl = serializers.SerializerMethodField()
  10. is_image = serializers.SerializerMethodField()
  11. filetype = serializers.SerializerMethodField()
  12. uploader_ip = serializers.SerializerMethodField()
  13. url = serializers.SerializerMethodField()
  14. class Meta:
  15. model = Attachment
  16. fields = [
  17. 'id',
  18. 'filetype',
  19. 'post',
  20. 'uploaded_on',
  21. 'uploader_name',
  22. 'uploader_ip',
  23. 'filename',
  24. 'size',
  25. 'acl',
  26. 'is_image',
  27. 'url',
  28. ]
  29. def get_acl(self, obj):
  30. try:
  31. return obj.acl
  32. except AttributeError:
  33. return None
  34. def get_is_image(self, obj):
  35. return obj.is_image
  36. def get_filetype(self, obj):
  37. return obj.filetype.name
  38. def get_uploader_ip(self, obj):
  39. user = self.context.get('user')
  40. if user and user.acl_cache['can_see_users_ips']:
  41. return obj.uploader_ip
  42. else:
  43. return None
  44. def get_url(self, obj):
  45. return {
  46. 'index': obj.get_absolute_url(),
  47. 'thumb': obj.get_thumbnail_url(),
  48. 'uploader': self.get_uploader_url(obj),
  49. }
  50. def get_uploader_url(self, obj):
  51. if obj.uploader_id:
  52. return reverse(
  53. 'misago:user', kwargs={
  54. 'slug': obj.uploader_slug,
  55. 'pk': obj.uploader_id,
  56. }
  57. )
  58. else:
  59. return None
  60. class NewAttachmentSerializer(serializers.Serializer):
  61. upload = serializers.FileField()
  62. def validate_upload(self, upload):
  63. user = self.context['request'].user
  64. user_roles = set(r.pk for r in user.get_roles())
  65. max_attachment_size = user.acl_cache['max_attachment_size']
  66. self.filetype = self.validate_filetype(upload, user_roles)
  67. self.validate_filesize(upload, self.filetype, max_attachment_size)
  68. return upload
  69. def validate_filetype(self, upload, user_roles):
  70. filename = upload.name.strip().lower()
  71. queryset = AttachmentType.objects.filter(status=AttachmentType.ENABLED)
  72. for filetype in queryset.prefetch_related('limit_uploads_to'):
  73. for extension in filetype.extensions_list:
  74. if filename.endswith('.%s' % extension):
  75. break
  76. else:
  77. continue
  78. if filetype.mimetypes_list and upload.content_type not in filetype.mimetypes_list:
  79. continue
  80. if filetype.limit_uploads_to.exists():
  81. allowed_roles = set(r.pk for r in filetype.limit_uploads_to.all())
  82. if not user_roles & allowed_roles:
  83. continue
  84. return filetype
  85. raise serializers.ValidationError(_("You can't upload files of this type."))
  86. def validate_filesize(self, upload, filetype, hard_limit):
  87. if upload.size > hard_limit * 1024:
  88. message = _("You can't upload files larger than %(limit)s (your file has %(upload)s).")
  89. raise serializers.ValidationError(
  90. message % {
  91. 'upload': filesizeformat(upload.size).rstrip('.0'),
  92. 'limit': filesizeformat(hard_limit * 1024).rstrip('.0'),
  93. }
  94. )
  95. if filetype.size_limit and upload.size > filetype.size_limit * 1024:
  96. message = _(
  97. "You can't upload files of this type larger than %(limit)s (your file has %(upload)s)."
  98. )
  99. raise serializers.ValidationError(
  100. message % {
  101. 'upload': filesizeformat(upload.size).rstrip('.0'),
  102. 'limit': filesizeformat(filetype.size_limit * 1024).rstrip('.0'),
  103. }
  104. )
  105. def is_upload_image(self, upload):
  106. filename = upload.name.strip().lower()
  107. for extension in IMAGE_EXTENSIONS:
  108. if filename.endswith('.%s' % extension):
  109. return True
  110. return False
  111. def save(self):
  112. request = self.context['request']
  113. upload = self.validated_data['upload']
  114. self.instance = Attachment(
  115. secret=Attachment.generate_new_secret(),
  116. filetype=self.filetype,
  117. size=upload.size,
  118. uploader=request.user,
  119. uploader_name=request.user.username,
  120. uploader_slug=request.user.slug,
  121. uploader_ip=request.user_ip,
  122. filename=upload.name,
  123. )
  124. if self.is_upload_image(upload):
  125. try:
  126. self.instance.set_image(upload)
  127. except IOError:
  128. raise serializers.ValidationError({
  129. 'upload': [_("Uploaded image was corrupted or invalid.")],
  130. })
  131. else:
  132. self.instance.set_file(upload)
  133. self.instance.save()
  134. return self.instance