attachments.py 3.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. from django.contrib import messages
  2. from django.db import transaction
  3. from django.utils.translation import ugettext_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(AttachmentAdmin, self).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 = (('-id', _("From newest")), ('id', _("From oldest")), ('filename', _("A to z")),
  18. ('-filename', _("Z to a")), ('size', _("Smallest files")),
  19. ('-size', _("Largest files")), )
  20. selection_label = _('With attachments: 0')
  21. empty_selection_label = _('Select attachments')
  22. mass_actions = [
  23. {
  24. 'action': 'delete',
  25. 'name': _("Delete attachments"),
  26. 'icon': 'fa fa-times-circle',
  27. 'confirmation': _("Are you sure you want to delete selected attachments?"),
  28. 'is_atomic': False,
  29. },
  30. ]
  31. def get_search_form(self, request):
  32. return SearchAttachmentsForm
  33. def action_delete(self, request, attachments):
  34. deleted_attachments = []
  35. desynced_posts = []
  36. for attachment in attachments:
  37. if attachment.post:
  38. deleted_attachments.append(attachment.pk)
  39. desynced_posts.append(attachment.post_id)
  40. if desynced_posts:
  41. with transaction.atomic():
  42. for post in Post.objects.select_for_update().filter(id__in=desynced_posts):
  43. self.delete_from_cache(post, deleted_attachments)
  44. for attachment in attachments:
  45. attachment.delete()
  46. message = _("Selected attachments have been deleted.")
  47. messages.success(request, message)
  48. def delete_from_cache(self, post, attachments):
  49. if not post.attachments_cache:
  50. return # admin action may be taken due to desynced state
  51. clean_cache = []
  52. for a in post.attachments_cache:
  53. if a['id'] not in attachments:
  54. clean_cache.append(a)
  55. post.attachments_cache = clean_cache or None
  56. post.save(update_fields=['attachments_cache'])
  57. class DeleteAttachment(AttachmentAdmin, generic.ButtonView):
  58. def button_action(self, request, target):
  59. if target.post:
  60. self.delete_from_cache(target)
  61. target.delete()
  62. message = _('Attachment "%(filename)s" has been deleted.')
  63. messages.success(request, message % {'filename': target.filename})
  64. def delete_from_cache(self, attachment):
  65. if not attachment.post.attachments_cache:
  66. return # admin action may be taken due to desynced state
  67. clean_cache = []
  68. for a in attachment.post.attachments_cache:
  69. if a['id'] != attachment.id:
  70. clean_cache.append(a)
  71. attachment.post.attachments_cache = clean_cache or None
  72. attachment.post.save(update_fields=['attachments_cache'])