attachments.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. from rest_framework import serializers
  2. from django.utils.translation import gettext as _
  3. from django.utils.translation import ngettext
  4. from misago.acl import add_acl
  5. from misago.conf import settings
  6. from misago.threads.serializers import AttachmentSerializer
  7. from . import PostingEndpoint, PostingMiddleware
  8. class AttachmentsMiddleware(PostingMiddleware):
  9. def use_this_middleware(self):
  10. return bool(self.user_acl['max_attachment_size'])
  11. def get_serializer(self):
  12. return AttachmentsSerializer(
  13. data=self.request.data,
  14. context={
  15. 'mode': self.mode,
  16. 'user': self.user,
  17. 'user_acl': self.user_acl,
  18. 'post': self.post,
  19. }
  20. )
  21. def save(self, serializer):
  22. serializer.save()
  23. class AttachmentsSerializer(serializers.Serializer):
  24. attachments = serializers.ListField(child=serializers.IntegerField(), required=False)
  25. def validate_attachments(self, ids):
  26. self.update_attachments = False
  27. self.removed_attachments = []
  28. self.final_attachments = []
  29. ids = list(set(ids))
  30. validate_attachments_count(ids)
  31. attachments = self.get_initial_attachments(
  32. self.context['mode'], self.context['user_acl'], self.context['post']
  33. )
  34. new_attachments = self.get_new_attachments(self.context['user'], ids)
  35. if not attachments and not new_attachments:
  36. return [] # no attachments
  37. # clean existing attachments
  38. for attachment in attachments:
  39. if attachment.pk in ids:
  40. self.final_attachments.append(attachment)
  41. else:
  42. if attachment.acl['can_delete']:
  43. self.update_attachments = True
  44. self.removed_attachments.append(attachment)
  45. else:
  46. message = _(
  47. "You don't have permission to remove \"%(attachment)s\" attachment."
  48. )
  49. raise serializers.ValidationError(
  50. message % {'attachment': attachment.filename}
  51. )
  52. if new_attachments:
  53. self.update_attachments = True
  54. self.final_attachments += new_attachments
  55. self.final_attachments.sort(key=lambda a: a.pk, reverse=True)
  56. def get_initial_attachments(self, mode, user_acl, post):
  57. attachments = []
  58. if mode == PostingEndpoint.EDIT:
  59. queryset = post.attachment_set.select_related('filetype')
  60. attachments = list(queryset)
  61. add_acl(user_acl, attachments)
  62. return attachments
  63. def get_new_attachments(self, user, ids):
  64. if not ids:
  65. return []
  66. queryset = user.attachment_set.select_related('filetype').filter(
  67. post__isnull=True,
  68. id__in=ids,
  69. )
  70. return list(queryset)
  71. def save(self):
  72. if not self.update_attachments:
  73. return
  74. if self.removed_attachments:
  75. for attachment in self.removed_attachments:
  76. attachment.delete_files()
  77. self.context['post'].attachment_set.filter(
  78. id__in=[a.id for a in self.removed_attachments]
  79. ).delete()
  80. if self.final_attachments:
  81. # sort final attachments by id, descending
  82. self.final_attachments.sort(key=lambda a: a.pk, reverse=True)
  83. self.context['user'].attachment_set.filter(
  84. id__in=[a.id for a in self.final_attachments]
  85. ).update(post=self.context['post'])
  86. self.sync_attachments_cache(self.context['post'], self.final_attachments)
  87. def sync_attachments_cache(self, post, attachments):
  88. if attachments:
  89. post.attachments_cache = AttachmentSerializer(attachments, many=True).data
  90. for attachment in post.attachments_cache:
  91. del attachment['acl']
  92. del attachment['post']
  93. else:
  94. post.attachments_cache = None
  95. post.update_fields.append('attachments_cache')
  96. def validate_attachments_count(data):
  97. total_attachments = len(data)
  98. if total_attachments > settings.MISAGO_POST_ATTACHMENTS_LIMIT:
  99. message = ngettext(
  100. "You can't attach more than %(limit_value)s file to single post (added %(show_value)s).",
  101. "You can't attach more than %(limit_value)s flies to single post (added %(show_value)s).",
  102. settings.MISAGO_POST_ATTACHMENTS_LIMIT,
  103. )
  104. raise serializers.ValidationError(
  105. message % {
  106. 'limit_value': settings.MISAGO_POST_ATTACHMENTS_LIMIT,
  107. 'show_value': total_attachments,
  108. }
  109. )