attachments.py 4.7 KB

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