attachments.py 3.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. from django.contrib import messages
  2. from django.db import transaction
  3. from django.utils.translation import gettext_lazy as _
  4. from misago.admin.views import generic
  5. from misago.threads.forms import SearchAttachmentsForm
  6. from misago.threads.models import Attachment, Post
  7. class AttachmentAdmin(generic.AdminBaseMixin):
  8. root_link = 'misago:admin:system:attachments:index'
  9. model = Attachment
  10. templates_dir = 'misago/admin/attachments'
  11. message_404 = _("Requested attachment could not be found.")
  12. def get_queryset(self):
  13. qs = super().get_queryset()
  14. return qs.select_related('filetype', 'uploader', 'post', 'post__thread', 'post__category')
  15. class AttachmentsList(AttachmentAdmin, generic.ListView):
  16. items_per_page = 20
  17. ordering = [
  18. ('-id', _("From newest")),
  19. ('id', _("From oldest")),
  20. ('filename', _("A to z")),
  21. ('-filename', _("Z to a")),
  22. ('size', _("Smallest files")),
  23. ('-size', _("Largest files")),
  24. ]
  25. selection_label = _('With attachments: 0')
  26. empty_selection_label = _('Select attachments')
  27. mass_actions = [
  28. {
  29. 'action': 'delete',
  30. 'name': _("Delete attachments"),
  31. 'icon': 'fa fa-times-circle',
  32. 'confirmation': _("Are you sure you want to delete selected attachments?"),
  33. 'is_atomic': False,
  34. },
  35. ]
  36. def get_search_form(self, request):
  37. return SearchAttachmentsForm
  38. def action_delete(self, request, attachments):
  39. deleted_attachments = []
  40. desynced_posts = []
  41. for attachment in attachments:
  42. if attachment.post:
  43. deleted_attachments.append(attachment.pk)
  44. desynced_posts.append(attachment.post_id)
  45. if desynced_posts:
  46. with transaction.atomic():
  47. for post in Post.objects.filter(id__in=desynced_posts):
  48. self.delete_from_cache(post, deleted_attachments)
  49. for attachment in attachments:
  50. attachment.delete()
  51. message = _("Selected attachments have been deleted.")
  52. messages.success(request, message)
  53. def delete_from_cache(self, post, attachments):
  54. if not post.attachments_cache:
  55. return # admin action may be taken due to desynced state
  56. clean_cache = []
  57. for a in post.attachments_cache:
  58. if a['id'] not in attachments:
  59. clean_cache.append(a)
  60. post.attachments_cache = clean_cache or None
  61. post.save(update_fields=['attachments_cache'])
  62. class DeleteAttachment(AttachmentAdmin, generic.ButtonView):
  63. def button_action(self, request, target):
  64. if target.post:
  65. self.delete_from_cache(target)
  66. target.delete()
  67. message = _('Attachment "%(filename)s" has been deleted.')
  68. messages.success(request, message % {'filename': target.filename})
  69. def delete_from_cache(self, attachment):
  70. if not attachment.post.attachments_cache:
  71. return # admin action may be taken due to desynced state
  72. clean_cache = []
  73. for a in attachment.post.attachments_cache:
  74. if a['id'] != attachment.id:
  75. clean_cache.append(a)
  76. attachment.post.attachments_cache = clean_cache or None
  77. attachment.post.save(update_fields=['attachments_cache'])