signals.py 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. from collections import OrderedDict
  2. from django.contrib.auth import get_user_model
  3. from django.db import transaction
  4. from django.db.models.signals import pre_delete
  5. from django.dispatch import Signal, receiver
  6. from django.utils.translation import ugettext as _
  7. from misago.categories.models import Category
  8. from misago.categories.signals import delete_category_content, move_category_content
  9. from misago.core.pgutils import chunk_queryset
  10. from misago.users.signals import (
  11. anonymize_user_data, archive_user_data, delete_user_content, username_changed)
  12. from .anonymize import ANONYMIZABLE_EVENTS, anonymize_event, anonymize_post_last_likes
  13. from .models import Attachment, Poll, PollVote, Post, PostEdit, PostLike, Thread
  14. delete_post = Signal()
  15. delete_thread = Signal()
  16. merge_post = Signal(providing_args=["other_post"])
  17. merge_thread = Signal(providing_args=["other_thread"])
  18. move_post = Signal()
  19. move_thread = Signal()
  20. @receiver(merge_thread)
  21. def merge_threads(sender, **kwargs):
  22. other_thread = kwargs['other_thread']
  23. other_thread.post_set.update(
  24. category=sender.category,
  25. thread=sender,
  26. )
  27. other_thread.postedit_set.update(
  28. category=sender.category,
  29. thread=sender,
  30. )
  31. other_thread.postlike_set.update(
  32. category=sender.category,
  33. thread=sender,
  34. )
  35. other_thread.subscription_set.exclude(
  36. user__in=sender.subscription_set.values('user'),
  37. ).update(
  38. category=sender.category,
  39. thread=sender,
  40. )
  41. @receiver(merge_post)
  42. def merge_posts(sender, **kwargs):
  43. other_post = kwargs['other_post']
  44. for user in sender.mentions.iterator():
  45. other_post.mentions.add(user)
  46. @receiver(move_thread)
  47. def move_thread_content(sender, **kwargs):
  48. sender.post_set.update(category=sender.category)
  49. sender.postedit_set.update(category=sender.category)
  50. sender.postlike_set.update(category=sender.category)
  51. sender.pollvote_set.update(category=sender.category)
  52. sender.subscription_set.update(category=sender.category)
  53. Poll.objects.filter(thread=sender).update(category=sender.category)
  54. @receiver(delete_category_content)
  55. def delete_category_threads(sender, **kwargs):
  56. sender.subscription_set.all().delete()
  57. sender.pollvote_set.all().delete()
  58. sender.poll_set.all().delete()
  59. sender.postlike_set.all().delete()
  60. sender.thread_set.all().delete()
  61. sender.postedit_set.all().delete()
  62. sender.post_set.all().delete()
  63. @receiver(move_category_content)
  64. def move_category_threads(sender, **kwargs):
  65. new_category = kwargs['new_category']
  66. sender.thread_set.update(category=new_category)
  67. sender.post_set.filter(category=sender).update(category=new_category)
  68. sender.postedit_set.filter(category=sender).update(category=new_category)
  69. sender.postlike_set.filter(category=sender).update(category=new_category)
  70. sender.poll_set.filter(category=sender).update(category=new_category)
  71. sender.pollvote_set.update(category=new_category)
  72. sender.subscription_set.update(category=new_category)
  73. @receiver(delete_user_content)
  74. def delete_user_threads(sender, **kwargs):
  75. recount_categories = set()
  76. recount_threads = set()
  77. for post in chunk_queryset(sender.liked_post_set):
  78. cleaned_likes = list(filter(lambda i: i['id'] != sender.id, post.last_likes))
  79. if cleaned_likes != post.last_likes:
  80. post.last_likes = cleaned_likes
  81. post.save(update_fields=['last_likes'])
  82. for thread in chunk_queryset(sender.thread_set):
  83. recount_categories.add(thread.category_id)
  84. with transaction.atomic():
  85. thread.delete()
  86. for post in chunk_queryset(sender.post_set):
  87. recount_categories.add(post.category_id)
  88. recount_threads.add(post.thread_id)
  89. with transaction.atomic():
  90. post.delete()
  91. if recount_threads:
  92. changed_threads_qs = Thread.objects.filter(id__in=recount_threads)
  93. for thread in chunk_queryset(changed_threads_qs):
  94. thread.synchronize()
  95. thread.save()
  96. if recount_categories:
  97. for category in Category.objects.filter(id__in=recount_categories):
  98. category.synchronize()
  99. category.save()
  100. @receiver(archive_user_data)
  101. def archive_user_attachments(sender, archive=None, **kwargs):
  102. queryset = sender.attachment_set.order_by('id')
  103. for attachment in chunk_queryset(queryset):
  104. archive.add_model_file(
  105. attachment.file,
  106. prefix=attachment.uploaded_on.strftime('%H%M%S-file'),
  107. date=attachment.uploaded_on,
  108. )
  109. archive.add_model_file(
  110. attachment.image,
  111. prefix=attachment.uploaded_on.strftime('%H%M%S-image'),
  112. date=attachment.uploaded_on,
  113. )
  114. archive.add_model_file(
  115. attachment.thumbnail,
  116. prefix=attachment.uploaded_on.strftime('%H%M%S-thumbnail'),
  117. date=attachment.uploaded_on,
  118. )
  119. @receiver(archive_user_data)
  120. def archive_user_posts(sender, archive=None, **kwargs):
  121. queryset = sender.post_set.order_by('id')
  122. for post in chunk_queryset(queryset):
  123. item_name = post.posted_on.strftime('%H%M%S-post')
  124. archive.add_text(item_name, post.parsed, date=post.posted_on)
  125. @receiver(archive_user_data)
  126. def archive_user_posts_edits(sender, archive=None, **kwargs):
  127. queryset = PostEdit.objects.filter(post__poster=sender).order_by('id')
  128. for post_edit in chunk_queryset(queryset):
  129. item_name = post_edit.edited_on.strftime('%H%M%S-post-edit')
  130. archive.add_text(item_name, post_edit.edited_from, date=post_edit.edited_on)
  131. queryset = sender.postedit_set.exclude(id__in=queryset.values('id')).order_by('id')
  132. for post_edit in chunk_queryset(queryset):
  133. item_name = post_edit.edited_on.strftime('%H%M%S-post-edit')
  134. archive.add_text(item_name, post_edit.edited_from, date=post_edit.edited_on)
  135. @receiver(archive_user_data)
  136. def archive_user_polls(sender, archive=None, **kwargs):
  137. queryset = sender.poll_set.order_by('id')
  138. for poll in chunk_queryset(queryset):
  139. item_name = poll.posted_on.strftime('%H%M%S-poll')
  140. archive.add_dict(
  141. item_name,
  142. OrderedDict([
  143. (_("Question"), poll.question),
  144. (_("Choices"), ', '.join([c['label'] for c in poll.choices])),
  145. ]),
  146. date=poll.posted_on,
  147. )
  148. @receiver(anonymize_user_data)
  149. def anonymize_user_in_events(sender, **kwargs):
  150. queryset = Post.objects.filter(
  151. is_event=True,
  152. event_type__in=ANONYMIZABLE_EVENTS,
  153. event_context__user__id=sender.id,
  154. )
  155. for event in chunk_queryset(queryset):
  156. anonymize_event(sender, event)
  157. @receiver([anonymize_user_data])
  158. def anonymize_user_in_likes(sender, **kwargs):
  159. for post in chunk_queryset(sender.liked_post_set):
  160. anonymize_post_last_likes(sender, post)
  161. @receiver([anonymize_user_data, username_changed])
  162. def update_usernames(sender, **kwargs):
  163. Thread.objects.filter(starter=sender).update(
  164. starter_name=sender.username,
  165. starter_slug=sender.slug,
  166. )
  167. Thread.objects.filter(last_poster=sender).update(
  168. last_poster_name=sender.username,
  169. last_poster_slug=sender.slug,
  170. )
  171. Thread.objects.filter(best_answer_marked_by=sender).update(
  172. best_answer_marked_by_name=sender.username,
  173. best_answer_marked_by_slug=sender.slug,
  174. )
  175. Post.objects.filter(poster=sender).update(
  176. poster_name=sender.username,
  177. )
  178. Post.objects.filter(last_editor=sender).update(
  179. last_editor_name=sender.username,
  180. last_editor_slug=sender.slug,
  181. )
  182. PostEdit.objects.filter(editor=sender).update(
  183. editor_name=sender.username,
  184. editor_slug=sender.slug,
  185. )
  186. PostLike.objects.filter(liker=sender).update(
  187. liker_name=sender.username,
  188. liker_slug=sender.slug,
  189. )
  190. Attachment.objects.filter(uploader=sender).update(
  191. uploader_name=sender.username,
  192. uploader_slug=sender.slug,
  193. )
  194. Poll.objects.filter(poster=sender).update(
  195. poster_name=sender.username,
  196. poster_slug=sender.slug,
  197. )
  198. PollVote.objects.filter(voter=sender).update(
  199. voter_name=sender.username,
  200. voter_slug=sender.slug,
  201. )
  202. @receiver(pre_delete, sender=get_user_model())
  203. def remove_unparticipated_private_threads(sender, **kwargs):
  204. threads_qs = kwargs['instance'].privatethread_set.all()
  205. for thread in chunk_queryset(threads_qs):
  206. if thread.participants.count() == 1:
  207. with transaction.atomic():
  208. thread.delete()