from collections import OrderedDict from django.contrib.auth import get_user_model from django.db import transaction from django.db.models.signals import pre_delete from django.dispatch import Signal, receiver from django.utils.translation import ugettext as _ from misago.categories.models import Category from misago.categories.signals import delete_category_content, move_category_content from misago.core.pgutils import chunk_queryset from misago.users.signals import ( anonymize_user_data, archive_user_data, delete_user_content, username_changed) from .anonymize import ANONYMIZABLE_EVENTS, anonymize_event, anonymize_post_last_likes from .models import Attachment, Poll, PollVote, Post, PostEdit, PostLike, Thread delete_post = Signal() delete_thread = Signal() merge_post = Signal(providing_args=["other_post"]) merge_thread = Signal(providing_args=["other_thread"]) move_post = Signal() move_thread = Signal() @receiver(merge_thread) def merge_threads(sender, **kwargs): other_thread = kwargs['other_thread'] other_thread.post_set.update( category=sender.category, thread=sender, ) other_thread.postedit_set.update( category=sender.category, thread=sender, ) other_thread.postlike_set.update( category=sender.category, thread=sender, ) other_thread.subscription_set.exclude( user__in=sender.subscription_set.values('user'), ).update( category=sender.category, thread=sender, ) @receiver(merge_post) def merge_posts(sender, **kwargs): other_post = kwargs['other_post'] for user in sender.mentions.iterator(): other_post.mentions.add(user) @receiver(move_thread) def move_thread_content(sender, **kwargs): sender.post_set.update(category=sender.category) sender.postedit_set.update(category=sender.category) sender.postlike_set.update(category=sender.category) sender.pollvote_set.update(category=sender.category) sender.subscription_set.update(category=sender.category) Poll.objects.filter(thread=sender).update(category=sender.category) @receiver(delete_category_content) def delete_category_threads(sender, **kwargs): sender.subscription_set.all().delete() sender.pollvote_set.all().delete() sender.poll_set.all().delete() sender.postlike_set.all().delete() sender.thread_set.all().delete() sender.postedit_set.all().delete() sender.post_set.all().delete() @receiver(move_category_content) def move_category_threads(sender, **kwargs): new_category = kwargs['new_category'] sender.thread_set.update(category=new_category) sender.post_set.filter(category=sender).update(category=new_category) sender.postedit_set.filter(category=sender).update(category=new_category) sender.postlike_set.filter(category=sender).update(category=new_category) sender.poll_set.filter(category=sender).update(category=new_category) sender.pollvote_set.update(category=new_category) sender.subscription_set.update(category=new_category) @receiver(delete_user_content) def delete_user_threads(sender, **kwargs): recount_categories = set() recount_threads = set() for post in chunk_queryset(sender.liked_post_set): cleaned_likes = list(filter(lambda i: i['id'] != sender.id, post.last_likes)) if cleaned_likes != post.last_likes: post.last_likes = cleaned_likes post.save(update_fields=['last_likes']) for thread in chunk_queryset(sender.thread_set): recount_categories.add(thread.category_id) with transaction.atomic(): thread.delete() for post in chunk_queryset(sender.post_set): recount_categories.add(post.category_id) recount_threads.add(post.thread_id) with transaction.atomic(): post.delete() if recount_threads: changed_threads_qs = Thread.objects.filter(id__in=recount_threads) for thread in chunk_queryset(changed_threads_qs): thread.synchronize() thread.save() if recount_categories: for category in Category.objects.filter(id__in=recount_categories): category.synchronize() category.save() @receiver(archive_user_data) def archive_user_attachments(sender, archive=None, **kwargs): queryset = sender.attachment_set.order_by('id') for attachment in chunk_queryset(queryset): archive.add_model_file( attachment.file, prefix=attachment.uploaded_on.strftime('%H%M%S-file'), date=attachment.uploaded_on, ) archive.add_model_file( attachment.image, prefix=attachment.uploaded_on.strftime('%H%M%S-image'), date=attachment.uploaded_on, ) archive.add_model_file( attachment.thumbnail, prefix=attachment.uploaded_on.strftime('%H%M%S-thumbnail'), date=attachment.uploaded_on, ) @receiver(archive_user_data) def archive_user_posts(sender, archive=None, **kwargs): queryset = sender.post_set.order_by('id') for post in chunk_queryset(queryset): item_name = post.posted_on.strftime('%H%M%S-post') archive.add_text(item_name, post.parsed, date=post.posted_on) @receiver(archive_user_data) def archive_user_posts_edits(sender, archive=None, **kwargs): queryset = PostEdit.objects.filter(post__poster=sender).order_by('id') for post_edit in chunk_queryset(queryset): item_name = post_edit.edited_on.strftime('%H%M%S-post-edit') archive.add_text(item_name, post_edit.edited_from, date=post_edit.edited_on) queryset = sender.postedit_set.exclude(id__in=queryset.values('id')).order_by('id') for post_edit in chunk_queryset(queryset): item_name = post_edit.edited_on.strftime('%H%M%S-post-edit') archive.add_text(item_name, post_edit.edited_from, date=post_edit.edited_on) @receiver(archive_user_data) def archive_user_polls(sender, archive=None, **kwargs): queryset = sender.poll_set.order_by('id') for poll in chunk_queryset(queryset): item_name = poll.posted_on.strftime('%H%M%S-poll') archive.add_dict( item_name, OrderedDict([ (_("Question"), poll.question), (_("Choices"), ', '.join([c['label'] for c in poll.choices])), ]), date=poll.posted_on, ) @receiver(anonymize_user_data) def anonymize_user_in_events(sender, **kwargs): queryset = Post.objects.filter( is_event=True, event_type__in=ANONYMIZABLE_EVENTS, event_context__user__id=sender.id, ) for event in chunk_queryset(queryset): anonymize_event(sender, event) @receiver([anonymize_user_data]) def anonymize_user_in_likes(sender, **kwargs): for post in chunk_queryset(sender.liked_post_set): anonymize_post_last_likes(sender, post) @receiver([anonymize_user_data, username_changed]) def update_usernames(sender, **kwargs): Thread.objects.filter(starter=sender).update( starter_name=sender.username, starter_slug=sender.slug, ) Thread.objects.filter(last_poster=sender).update( last_poster_name=sender.username, last_poster_slug=sender.slug, ) Thread.objects.filter(best_answer_marked_by=sender).update( best_answer_marked_by_name=sender.username, best_answer_marked_by_slug=sender.slug, ) Post.objects.filter(poster=sender).update( poster_name=sender.username, ) Post.objects.filter(last_editor=sender).update( last_editor_name=sender.username, last_editor_slug=sender.slug, ) PostEdit.objects.filter(editor=sender).update( editor_name=sender.username, editor_slug=sender.slug, ) PostLike.objects.filter(liker=sender).update( liker_name=sender.username, liker_slug=sender.slug, ) Attachment.objects.filter(uploader=sender).update( uploader_name=sender.username, uploader_slug=sender.slug, ) Poll.objects.filter(poster=sender).update( poster_name=sender.username, poster_slug=sender.slug, ) PollVote.objects.filter(voter=sender).update( voter_name=sender.username, voter_slug=sender.slug, ) @receiver(pre_delete, sender=get_user_model()) def remove_unparticipated_private_threads(sender, **kwargs): threads_qs = kwargs['instance'].privatethread_set.all() for thread in chunk_queryset(threads_qs): if thread.participants.count() == 1: with transaction.atomic(): thread.delete()