Browse Source

Maintain relations between forum models #26

Ralfp 12 years ago
parent
commit
e4090400d7

+ 56 - 1
misago/threads/models.py

@@ -1,7 +1,9 @@
 from django.db import models
+from django.db.models import F
 from django.utils import timezone
 from django.utils.translation import ugettext_lazy as _
 from misago.forums.signals import move_forum_content
+from misago.threads.signals import move_thread, merge_thread, move_post, merge_post
 from misago.users.signals import delete_user_content, rename_user
 from misago.utils import slugify
 
@@ -47,6 +49,13 @@ class Thread(models.Model):
     def get_date(self):
         return self.start
 
+    def move_to(self, move_to):
+        move_thread.send(sender=self, move_to=move_to)
+        self.forum = move_to
+
+    def merge_with(self, thread, merge):
+        merge_thread.send(sender=self, new_thread=thread, merge=merge)
+
     def sync(self):
         # Counters
         self.replies = self.post_set.filter(moderated=False).filter(deleted=False).count() - 1
@@ -119,6 +128,15 @@ class Post(models.Model):
     def get_date(self):
         return self.date
 
+    def move_to(self, thread):
+        move_post.send(sender=self, move_to=thread)
+        self.thread = thread
+        self.forum = thread.forum
+        
+    def merge_with(self, post):
+        post.post = '%s\n- - -\n%s' % (post.post, self.post)
+        merge_post.send(sender=self, new_post=post)
+
     def set_checkpoint(self, request, action):
         if request.user.is_authenticated():
             self.checkpoints = True
@@ -204,7 +222,7 @@ def delete_user_content_handler(sender, **kwargs):
     prev_posts = []
     for post in sender.post_set.filter(checkpoints=True):
         threads.append(post.thread_id)
-        prev_post = Post.objects.filter(thread=post.thread_id).exclude(user=sender).order_by('-id')[:1][0]
+        prev_post = Post.objects.filter(thread=post.thread_id).exclude(merge__gt=post.merge).exclude(user=sender).order_by('merge', '-id')[:1][0]
         post.checkpoint_set.update(post=prev_post)
         if not prev_post.pk in prev_posts:
             prev_posts.append(prev_post.pk)
@@ -228,3 +246,40 @@ def move_forum_content_handler(sender, **kwargs):
     Checkpoint.objects.filter(forum=sender).update(forum=kwargs['move_to'])
 
 move_forum_content.connect(move_forum_content_handler, dispatch_uid="move_forum_threads_posts")
+
+
+def move_thread_handler(sender, **kwargs):
+    Post.objects.filter(forum=sender.forum_pk).update(forum=kwargs['move_to'])
+    Change.objects.filter(forum=sender.forum_pk).update(forum=kwargs['move_to'])
+    Checkpoint.objects.filter(forum=sender.forum_pk).update(forum=kwargs['move_to'])
+
+move_thread.connect(move_thread_handler, dispatch_uid="move_thread")
+
+
+def merge_thread_handler(sender, **kwargs):
+    Post.objects.filter(thread=sender).update(thread=kwargs['new_thread'], merge=F('merge') + kwargs['merge'])
+    Change.objects.filter(thread=sender).update(thread=kwargs['new_thread'])
+    Checkpoint.objects.filter(thread=sender).delete()
+
+merge_thread.connect(merge_thread_handler, dispatch_uid="merge_threads")
+
+
+def move_posts_handler(sender, **kwargs):
+    Change.objects.filter(post=sender).update(forum=kwargs['move_to'].forum, thread=kwargs['move_to'])
+    if sender.checkpoints:
+        prev_post = Post.objects.filter(thread=sender.thread_id).filter(merge__lte=sender.merge).exclude(id=sender.pk).order_by('merge', '-id')[:1][0]
+        Checkpoint.objects.filter(post=sender).update(post=prev_post)
+        prev_post.checkpoints = True
+        prev_post.save(force_update=True)
+    sender.checkpoints = False
+
+move_post.connect(move_posts_handler, dispatch_uid="move_posts")
+
+
+def merge_posts_handler(sender, **kwargs):
+    Change.objects.filter(post=sender).update(post=kwargs['new_post'])
+    Checkpoint.objects.filter(post=sender).update(post=kwargs['new_post'])
+    if sender.checkpoints:
+        kwargs['new_post'].checkpoints = True
+
+merge_post.connect(merge_posts_handler, dispatch_uid="merge_posts")

+ 6 - 0
misago/threads/signals.py

@@ -0,0 +1,6 @@
+import django.dispatch
+
+move_thread = django.dispatch.Signal(providing_args=["move_to"])
+move_post = django.dispatch.Signal(providing_args=["move_to"])
+merge_thread = django.dispatch.Signal(providing_args=["new_thread", "merge"])
+merge_post = django.dispatch.Signal(providing_args=["new_post"])

+ 75 - 5
misago/threads/tests.py

@@ -22,6 +22,13 @@ class DeleteThreadTestCase(unittest.TestCase):
         
         self.thread = create_thread(self.forum)
         self.post = create_post(self.thread, self.user)
+     
+    def make_request(self, user=None):
+        request = self.factory.get('/customer/details')
+        request.session = SessionMock()
+        request.user = user
+        request.META['HTTP_USER_AGENT'] = 'TestAgent'
+        return request
         
     def test_deletion_owned(self):
         """Check if user content delete results in correct deletion of thread"""
@@ -65,10 +72,7 @@ class DeleteThreadTestCase(unittest.TestCase):
     def test_deletion_checkpoints(self):
         """Check if user content delete results in correct update of thread checkpoints"""
         # Create an instance of a GET request.
-        request = self.factory.get('/customer/details')
-        request.session = SessionMock()
-        request.user = self.user_alt
-        request.META['HTTP_USER_AGENT'] = 'TestAgent'
+        request = self.make_request(self.user_alt)
         
         # Create second and third post
         self.post = create_post(self.thread, self.user)
@@ -88,4 +92,70 @@ class DeleteThreadTestCase(unittest.TestCase):
         self.assertEqual(Post.objects.filter(checkpoints=True).count(), 1)
         self.assertEqual(Post.objects.get(id=self.post.pk).checkpoints, True)
         self.assertEqual(Post.objects.get(id=self.post.pk).checkpoint_set.count(), 1)
-        
+    
+    def test_threads_merge(self):
+        """Check if threads are correctly merged"""
+        # Create second thread
+        self.thread_b = create_thread(self.forum)
+        self.post_b = create_post(self.thread_b, self.user)
+        
+        # Merge threads
+        self.thread_b.merge_with(self.thread, 1)
+        self.thread_b.delete()
+        self.thread.merges += 1
+        self.thread.save(force_update=True)
+        
+        # See if merger was correct
+        self.assertEqual(Thread.objects.count(), 1)
+        self.assertEqual(Post.objects.count(), 2)
+        last_post = Post.objects.order_by('-id')[:1][0]
+        self.assertEqual(last_post.thread_id, self.thread.pk)
+        self.assertEqual(last_post.merge, 1)
+                
+        # Create third thread
+        self.thread_c = create_thread(self.forum)
+        self.post_c = create_post(self.thread_c, self.user)
+              
+        # Merge first thread into third one
+        self.thread.merge_with(self.thread_c, 1)
+        self.thread.delete()
+
+        # See if merger was correct
+        self.assertEqual(Thread.objects.count(), 1)
+        self.assertEqual(Post.objects.count(), 3)
+        last_post = Post.objects.get(id=last_post.pk)
+        self.assertEqual(last_post.thread_id, self.thread_c.pk)
+        self.assertEqual(last_post.merge, 2)
+        
+    def test_threads_move_checkpoints(self):
+        """Check if post_move correctly handles checkpoints"""
+        # Create thread with two posts
+        self.thread_b = create_thread(self.forum)
+        self.post_b = create_post(self.thread_b, self.user)
+        self.post_c = create_post(self.thread_b, self.user)
+        
+        # Create an instance of a GET request.
+        request = self.make_request(self.user)
+        
+        # Add checkpoint to post c
+        self.post_c.set_checkpoint(request, 'locked')
+        self.post_c.save(force_update=True)
+        
+        # Move post and sync threads
+        self.post_c.move_to(self.thread)
+        self.post_c.save(force_update=True)
+        self.thread.sync()
+        self.thread.save(force_update=True)
+        self.thread_b.sync()
+        self.thread_b.save(force_update=True)
+        
+        # See threads and post counters
+        self.assertEqual(Thread.objects.count(), 2)
+        self.assertEqual(Post.objects.count(), 3)
+        
+        # Refresh post b
+        self.post_b = Post.objects.get(id=self.post_b.pk)
+        
+        # Check if post b has post's c checkpoints
+        self.assertEqual(self.post_b.checkpoints, True)
+        self.assertEqual(self.post_b.checkpoint_set.count(), 1)

+ 3 - 8
misago/threads/views/list.py

@@ -1,5 +1,5 @@
 from django.core.urlresolvers import reverse
-from django.db.models import Q, F
+from django.db.models import Q
 from django import forms
 from django.forms import ValidationError
 from django.shortcuts import redirect
@@ -200,10 +200,7 @@ class ThreadsView(BaseView):
             if form.is_valid():
                 new_forum = form.cleaned_data['new_forum']
                 for thread in threads:
-                    thread.forum = new_forum
-                    thread.post_set.update(forum=new_forum)
-                    thread.change_set.update(forum=new_forum)
-                    thread.checkpoint_set.update(forum=new_forum)
+                    thread.move_to(new_forum)
                     thread.save(force_update=True)
                 new_forum.sync()
                 new_forum.save(force_update=True)
@@ -249,9 +246,7 @@ class ThreadsView(BaseView):
                     merged.append(thread.pk)
                     if last_thread and last_thread.last > thread.start:
                         last_merge += thread.merges + 1
-                    thread.post_set.update(thread=new_thread, merge=F('merge') + last_merge)
-                    thread.change_set.update(thread=new_thread)
-                    thread.checkpoint_set.update(thread=new_thread)
+                    thread.merge_with(new_thread, last_merge=last_merge)
                     last_thread = thread
                 Thread.objects.filter(id__in=merged).delete()
                 new_thread.sync()

+ 24 - 13
misago/threads/views/thread.py

@@ -139,9 +139,7 @@ class ThreadView(BaseView):
             raise forms.ValidationError(_("You have to select two or more posts you want to merge."))
         new_post = posts[0]
         for post in posts[1:]:
-            new_post.post = '%s\n- - -\n%s' % (new_post.post, post.post)
-            post.change_set.update(post=new_post)
-            post.checkpoint_set.update(post=new_post)
+            post.merge_with(new_post)
             post.delete()
         new_post.post_preparsed = post_markdown(self.request, new_post.post)
         new_post.save(force_update=True)
@@ -170,9 +168,16 @@ class ThreadView(BaseView):
                 new_thread.last_poster_name = 'n'
                 new_thread.last_poster_slug = 'n'
                 new_thread.save(force_insert=True)
-                self.thread.post_set.filter(id__in=ids).update(thread=new_thread, forum=new_thread.forum)
-                Change.objects.filter(post__in=ids).update(thread=new_thread, forum=new_thread.forum)
-                Checkpoint.objects.filter(post__in=ids).update(thread=new_thread, forum=new_thread.forum)
+                prev_merge = -1
+                merge = -1
+                for post in self.posts:
+                    if post.pk in ids:
+                        if prev_merge != post.merge:
+                            prev_merge = post.merge
+                            merge += 1
+                        post.merge = merge
+                        post.move_to(new_thread)
+                        post.save(force_update=True)
                 new_thread.sync()
                 new_thread.save(force_update=True)
                 self.thread.sync()
@@ -207,9 +212,16 @@ class ThreadView(BaseView):
             form = MovePostsForm(self.request.POST, request=self.request, thread=self.thread)
             if form.is_valid():
                 thread = form.cleaned_data['thread_url']
-                self.thread.post_set.filter(id__in=ids).update(thread=thread, forum=thread.forum, merge=F('merge') + thread.merges + 1)
-                Change.objects.filter(post__in=ids).update(thread=thread, forum=thread.forum)
-                Checkpoint.objects.filter(post__in=ids).update(thread=thread, forum=thread.forum)
+                prev_merge = -1
+                merge = -1
+                for post in self.posts:
+                    if post.pk in ids:
+                        if prev_merge != post.merge:
+                            prev_merge = post.merge
+                            merge += 1
+                        post.merge = merge + thread.merges
+                        post.move_to(thread)
+                        post.save(force_update=True)
                 if self.thread.post_set.count() == 0:
                     self.thread.delete()
                 else:
@@ -409,13 +421,12 @@ class ThreadView(BaseView):
             form = MoveThreadsForm(self.request.POST, request=self.request, forum=self.forum)
             if form.is_valid():
                 new_forum = form.cleaned_data['new_forum']
-                self.thread.forum = new_forum
-                self.thread.post_set.update(forum=new_forum)
-                self.thread.change_set.update(forum=new_forum)
-                self.thread.checkpoint_set.update(forum=new_forum)
+                self.thread.move_to(new_forum)
                 self.thread.save(force_update=True)
                 self.forum.sync()
                 self.forum.save(force_update=True)
+                new_forum.sync()
+                new_forum.save(force_update=True)
                 self.request.messages.set_flash(Message(_('Thread has been moved to "%(forum)s".') % {'forum': new_forum.name}), 'success', 'threads')
                 return None
             message = Message(form.non_field_errors()[0], 'error')