attachments.py 4.9 KB

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