Browse Source

Don't allow trading threads between different forums types

Ralfp 12 years ago
parent
commit
4888aca38d

+ 14 - 14
misago/apps/oldthreads/views/jumps.py

@@ -1,5 +1,5 @@
 from django.core.urlresolvers import reverse
 from django.core.urlresolvers import reverse
-from django.shortcuts import redirect
+from django.shortcuts import redirect as django_redirect
 from django.template import RequestContext
 from django.template import RequestContext
 from django.utils import timezone
 from django.utils import timezone
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
@@ -10,9 +10,9 @@ from misago.messages import Message
 from misago.models import Forum, Thread, Post, Karma, WatchedThread
 from misago.models import Forum, Thread, Post, Karma, WatchedThread
 from misago.readstrackers import ThreadsTracker
 from misago.readstrackers import ThreadsTracker
 from misago.utils.pagination import make_pagination
 from misago.utils.pagination import make_pagination
-from misago.apps.threads.views.base import BaseView
+from misago.apps.threads.views.base import ViewBase
 
 
-class JumpView(BaseView):
+class JumpViewBase(ViewBase):
     def fetch_thread(self, thread):
     def fetch_thread(self, thread):
         self.thread = Thread.objects.get(pk=thread)
         self.thread = Thread.objects.get(pk=thread)
         self.forum = self.thread.forum
         self.forum = self.thread.forum
@@ -23,11 +23,11 @@ class JumpView(BaseView):
         self.post = self.thread.post_set.get(pk=post)
         self.post = self.thread.post_set.get(pk=post)
         self.request.acl.threads.allow_post_view(self.request.user, self.thread, self.post)
         self.request.acl.threads.allow_post_view(self.request.user, self.thread, self.post)
 
 
-    def redirect(self, post):
+    def redirect_to_post(self, post):
         pagination = make_pagination(0, self.request.acl.threads.filter_posts(self.request, self.thread, self.thread.post_set.filter(date__lt=post.date)).count() + 1, self.request.settings.posts_per_page)
         pagination = make_pagination(0, self.request.acl.threads.filter_posts(self.request, self.thread, self.thread.post_set.filter(date__lt=post.date)).count() + 1, self.request.settings.posts_per_page)
         if pagination['total'] > 1:
         if pagination['total'] > 1:
-            return redirect(reverse('thread', kwargs={'thread': self.thread.pk, 'slug': self.thread.slug, 'page': pagination['total']}) + ('#post-%s' % post.pk))
-        return redirect(reverse('thread', kwargs={'thread': self.thread.pk, 'slug': self.thread.slug}) + ('#post-%s' % post.pk))
+            return django_redirect(reverse('thread', kwargs={'thread': self.thread.pk, 'slug': self.thread.slug, 'page': pagination['total']}) + ('#post-%s' % post.pk))
+        return django_redirect(reverse('thread', kwargs={'thread': self.thread.pk, 'slug': self.thread.slug}) + ('#post-%s' % post.pk))
 
 
     def make_jump(self):
     def make_jump(self):
         raise NotImplementedError('JumpView cannot be called directly.')
         raise NotImplementedError('JumpView cannot be called directly.')
@@ -49,24 +49,24 @@ class JumpView(BaseView):
 
 
 class LastReplyView(JumpView):
 class LastReplyView(JumpView):
     def make_jump(self):
     def make_jump(self):
-        return self.redirect(self.thread.post_set.order_by('-id')[:1][0])
+        return self.redirect_to_post(self.thread.post_set.order_by('-id')[:1][0])
 
 
 
 
 class FindReplyView(JumpView):
 class FindReplyView(JumpView):
     def make_jump(self):
     def make_jump(self):
-        return self.redirect(self.post)
+        return self.redirect_to_post(self.post)
 
 
 
 
 class NewReplyView(JumpView):
 class NewReplyView(JumpView):
     def make_jump(self):
     def make_jump(self):
         if not self.request.user.is_authenticated():
         if not self.request.user.is_authenticated():
-            return self.redirect(self.thread.post_set.order_by('-id')[:1][0])
+            return self.redirect_to_post(self.thread.post_set.order_by('-id')[:1][0])
         tracker = ThreadsTracker(self.request, self.forum)
         tracker = ThreadsTracker(self.request, self.forum)
         read_date = tracker.read_date(self.thread)
         read_date = tracker.read_date(self.thread)
         post = self.thread.post_set.filter(date__gt=read_date).order_by('id')[:1]
         post = self.thread.post_set.filter(date__gt=read_date).order_by('id')[:1]
         if not post:
         if not post:
-            return self.redirect(self.thread.post_set.order_by('-id')[:1][0])
-        return self.redirect(post[0])
+            return self.redirect_to_post(self.thread.post_set.order_by('-id')[:1][0])
+        return self.redirect_to_post(post[0])
 
 
 
 
 class FirstModeratedView(JumpView):
 class FirstModeratedView(JumpView):
@@ -74,7 +74,7 @@ class FirstModeratedView(JumpView):
         if not self.request.acl.threads.can_approve(self.forum):
         if not self.request.acl.threads.can_approve(self.forum):
             raise ACLError404()
             raise ACLError404()
         try:
         try:
-            return self.redirect(
+            return self.redirect_to_post(
                 self.thread.post_set.get(moderated=True))
                 self.thread.post_set.get(moderated=True))
         except Post.DoesNotExist:
         except Post.DoesNotExist:
             return error404(self.request)
             return error404(self.request)
@@ -85,7 +85,7 @@ class FirstReportedView(JumpView):
         if not self.request.acl.threads.can_mod_posts(self.forum):
         if not self.request.acl.threads.can_mod_posts(self.forum):
             raise ACLError404()
             raise ACLError404()
         try:
         try:
-            return self.redirect(
+            return self.redirect_to_post(
                 self.thread.post_set.get(reported=True))
                 self.thread.post_set.get(reported=True))
         except Post.DoesNotExist:
         except Post.DoesNotExist:
             return error404(self.request)
             return error404(self.request)
@@ -221,7 +221,7 @@ class UpvotePostView(JumpView):
             request.user.save(force_update=True)
             request.user.save(force_update=True)
             if self.post.user_id:
             if self.post.user_id:
                 self.post.user.save(force_update=True)
                 self.post.user.save(force_update=True)
-            return self.redirect(self.post)
+            return self.redirect_to_post(self.post)
         return view(self.request)
         return view(self.request)
     
     
     def check_acl(self, request):
     def check_acl(self, request):

+ 1 - 2
misago/apps/threads/mixins.py

@@ -2,8 +2,7 @@ from django.core.urlresolvers import reverse
 from django.shortcuts import redirect
 from django.shortcuts import redirect
 
 
 class TypeMixin(object):
 class TypeMixin(object):
-    templates_prefix = 'threads'
-    thread_url = 'thread'
+    type_prefix = 'thread'
 
 
     def threads_list_redirect(self):
     def threads_list_redirect(self):
         return redirect(reverse('forum', kwargs={'forum': self.forum.pk, 'slug': self.forum.slug}))
         return redirect(reverse('forum', kwargs={'forum': self.forum.pk, 'slug': self.forum.slug}))

+ 16 - 2
misago/apps/threadtype/base.py

@@ -1,4 +1,5 @@
 from django.core.urlresolvers import reverse
 from django.core.urlresolvers import reverse
+from django.http import Http404
 from django.shortcuts import redirect
 from django.shortcuts import redirect
 from misago.models import Forum, Thread, Post
 from misago.models import Forum, Thread, Post
 from misago.utils.pagination import make_pagination
 from misago.utils.pagination import make_pagination
@@ -21,8 +22,21 @@ class ViewBase(object):
     def set_post_contex(self):
     def set_post_contex(self):
         pass
         pass
 
 
+    def check_forum_type(self):
+        type_prefix = self.type_prefix
+        if type_prefix == 'thread':
+            type_prefix = 'root'
+        else:
+            type_prefix = '%ss' % type_prefix
+        try:
+            if self.parents[0].parent_id != Forum.objects.special_pk(type_prefix):
+                raise Http404()
+        except (AttributeError, IndexError):
+            if self.forum.special != type_prefix:
+                raise Http404()
+
     def redirect_to_post(self, post):
     def redirect_to_post(self, post):
         pagination = make_pagination(0, self.request.acl.threads.filter_posts(self.request, self.thread, self.thread.post_set).filter(id__lte=post.pk).count(), self.request.settings.posts_per_page)
         pagination = make_pagination(0, self.request.acl.threads.filter_posts(self.request, self.thread, self.thread.post_set).filter(id__lte=post.pk).count(), self.request.settings.posts_per_page)
         if pagination['total'] > 1:
         if pagination['total'] > 1:
-            return redirect(reverse(self.thread_url, kwargs={'thread': self.thread.pk, 'slug': self.thread.slug, 'page': pagination['total']}) + ('#post-%s' % post.pk))
-        return redirect(reverse(self.thread_url, kwargs={'thread': self.thread.pk, 'slug': self.thread.slug}) + ('#post-%s' % post.pk))
+            return redirect(reverse(self.type_prefix, kwargs={'thread': self.thread.pk, 'slug': self.thread.slug, 'page': pagination['total']}) + ('#post-%s' % post.pk))
+        return redirect(reverse(self.type_prefix, kwargs={'thread': self.thread.pk, 'slug': self.thread.slug}) + ('#post-%s' % post.pk))

+ 233 - 0
misago/apps/threadtype/jumps.py

@@ -0,0 +1,233 @@
+from django.core.urlresolvers import reverse
+from django.shortcuts import redirect
+from django.utils import timezone
+from django.utils.translation import ugettext as _
+from misago.acl.exceptions import ACLError403, ACLError404
+from misago.apps.errors import error403, error404
+from misago.decorators import block_guest, check_csrf
+from misago.messages import Message
+from misago.models import Forum, Thread, Post, Karma, WatchedThread
+from misago.readstrackers import ThreadsTracker
+from misago.utils.pagination import make_pagination
+from misago.apps.threadtype.base import BaseView
+
+class JumpView(BaseView):
+    def fetch_thread(self, thread):
+        self.thread = Thread.objects.get(pk=thread)
+        self.forum = self.thread.forum
+        self.request.acl.forums.allow_forum_view(self.forum)
+        self.request.acl.threads.allow_thread_view(self.request.user, self.thread)
+
+    def fetch_post(self, post):
+        self.post = self.thread.post_set.get(pk=post)
+        self.request.acl.threads.allow_post_view(self.request.user, self.thread, self.post)
+
+    def make_jump(self):
+        raise NotImplementedError('JumpView cannot be called directly.')
+
+    def __call__(self, request, slug=None, thread=None, post=None):
+        self.request = request
+        try:
+            self.fetch_thread(thread)
+            self.check_forum_type()
+            if post:
+                self.fetch_post(post)
+            return self.make_jump()
+        except (Thread.DoesNotExist, Post.DoesNotExist):
+            return error404(self.request)
+        except ACLError403 as e:
+            return error403(request, e.message)
+        except ACLError404 as e:
+            return error404(request, e.message)
+
+
+class LastReplyBaseView(JumpView):
+    def make_jump(self):
+        return self.redirect_to_post(self.thread.post_set.order_by('-id')[:1][0])
+
+
+class FindReplyBaseView(JumpView):
+    def make_jump(self):
+        return self.redirect_to_post(self.post)
+
+
+class NewReplyBaseView(JumpView):
+    def make_jump(self):
+        if not self.request.user.is_authenticated():
+            return self.redirect_to_post(self.thread.post_set.order_by('-id')[:1][0])
+        tracker = ThreadsTracker(self.request, self.forum)
+        read_date = tracker.read_date(self.thread)
+        post = self.thread.post_set.filter(date__gt=read_date).order_by('id')[:1]
+        if not post:
+            return self.redirect_to_post(self.thread.post_set.order_by('-id')[:1][0])
+        return self.redirect_to_post(post[0])
+
+
+class FirstModeratedBaseView(JumpView):
+    def make_jump(self):
+        if not self.request.acl.threads.can_approve(self.forum):
+            raise ACLError404()
+        try:
+            return self.redirect_to_post(
+                self.thread.post_set.get(moderated=True))
+        except Post.DoesNotExist:
+            return error404(self.request)
+
+
+class FirstReportedBaseView(JumpView):
+    def make_jump(self):
+        if not self.request.acl.threads.can_mod_posts(self.forum):
+            raise ACLError404()
+        try:
+            return self.redirect_to_post(
+                self.thread.post_set.get(reported=True))
+        except Post.DoesNotExist:
+            return error404(self.request)
+
+
+class ShowHiddenRepliesBaseView(JumpView):
+    def make_jump(self):
+        @block_guest
+        @check_csrf
+        def view(request):
+            ignored_exclusions = request.session.get('unignore_threads', [])
+            ignored_exclusions.append(self.thread.pk)
+            request.session['unignore_threads'] = ignored_exclusions
+            request.messages.set_flash(Message(_('Replies made to this thread by members on your ignore list have been revealed.')), 'success', 'threads')
+            return redirect(reverse(self.type_prefix, kwargs={'thread': self.thread.pk, 'slug': self.thread.slug}))
+        return view(self.request)
+
+
+class WatchThreadBaseView(JumpView):
+    def get_retreat(self):
+        return redirect(self.request.POST.get('retreat', reverse('thread', kwargs={'thread': self.thread.pk, 'slug': self.thread.slug})))
+
+    def update_watcher(self, request, watcher):
+        request.messages.set_flash(Message(_('This thread has been added to your watched threads list.')), 'success', 'threads')
+
+    def make_jump(self):
+        @block_guest
+        @check_csrf
+        def view(request):
+            try:
+                watcher = WatchedThread.objects.get(user=request.user, thread=self.thread)
+            except WatchedThread.DoesNotExist:
+                watcher = WatchedThread()
+                watcher.user = request.user
+                watcher.forum = self.forum
+                watcher.thread = self.thread
+                watcher.last_read = timezone.now()
+            self.update_watcher(request, watcher)
+            if watcher.pk:
+                watcher.save(force_update=True)
+            else:
+                watcher.save(force_insert=True)
+            return self.get_retreat()
+        return view(self.request)
+
+
+class WatchEmailThreadBaseView(WatchThreadView):
+    def update_watcher(self, request, watcher):
+        watcher.email = True
+        if watcher.pk:
+            request.messages.set_flash(Message(_('You will now receive e-mail with notification when somebody replies to this thread.')), 'success', 'threads')
+        else:
+            request.messages.set_flash(Message(_('This thread has been added to your watched threads list. You will also receive e-mail with notification when somebody replies to it.')), 'success', 'threads')
+
+
+class UnwatchThreadBaseView(WatchThreadView):
+    def update_watcher(self, request, watcher):
+        watcher.deleted = True
+        watcher.delete()
+        if watcher.email:
+            request.messages.set_flash(Message(_('This thread has been removed from your watched threads list. You will no longer receive e-mails with notifications when somebody replies to it.')), 'success', 'threads')
+        else:
+            request.messages.set_flash(Message(_('This thread has been removed from your watched threads list.')), 'success', 'threads')
+
+
+class UnwatchEmailThreadBaseView(WatchThreadView):
+    def update_watcher(self, request, watcher):
+        watcher.email = False
+        request.messages.set_flash(Message(_('You will no longer receive e-mails with notifications when somebody replies to this thread.')), 'success', 'threads')
+
+
+class UpvotePostBaseView(JumpView):        
+    def make_jump(self):
+        @block_guest
+        @check_csrf
+        def view(request):
+            if self.post.user_id == request.user.id:
+                return error404(request)
+            self.check_acl(request)
+            try:
+                vote = Karma.objects.get(user=request.user, post=self.post)
+                if self.thread.start_post_id == self.post.pk:
+                    if vote.score > 0:
+                        self.thread.upvotes -= 1
+                    else:
+                        self.thread.downvotes -= 1
+                if vote.score > 0:
+                    self.post.upvotes -= 1
+                    request.user.karma_given_p -= 1
+                    if self.post.user_id:
+                        self.post.user.karma_p -= 1
+                else:
+                    self.post.downvotes -= 1
+                    request.user.karma_given_n -= 1
+                    if self.post.user_id:
+                        self.post.user.karma_n -= 1
+            except Karma.DoesNotExist:
+                vote = Karma()
+            vote.forum = self.forum
+            vote.thread = self.thread
+            vote.post = self.post
+            vote.user = request.user
+            vote.user_name = request.user.username
+            vote.user_slug = request.user.username_slug
+            vote.date = timezone.now()
+            vote.ip = request.session.get_ip(request)
+            vote.agent = request.META.get('HTTP_USER_AGENT')
+            self.make_vote(request, vote)
+            request.messages.set_flash(Message(_('Your vote has been saved.')), 'success', 'threads_%s' % self.post.pk)
+            if vote.pk:
+                vote.save(force_update=True)
+            else:
+                vote.save(force_insert=True)
+            if self.thread.start_post_id == self.post.pk:
+                if vote.score > 0:
+                    self.thread.upvotes += 1
+                else:
+                    self.thread.downvotes += 1
+                self.thread.save(force_update=True)
+            if vote.score > 0:
+                self.post.upvotes += 1
+                request.user.karma_given_p += 1
+                if self.post.user_id:
+                    self.post.user.karma_p += 1
+                    self.post.user.score += request.settings['score_reward_karma_positive']
+            else:
+                self.post.downvotes += 1
+                request.user.karma_given_n += 1
+                if self.post.user_id:
+                    self.post.user.karma_n += 1
+                    self.post.user.score -= request.settings['score_reward_karma_negative']
+            self.post.save(force_update=True)
+            request.user.save(force_update=True)
+            if self.post.user_id:
+                self.post.user.save(force_update=True)
+            return self.redirect_to_post(self.post)
+        return view(self.request)
+    
+    def check_acl(self, request):
+        request.acl.threads.allow_post_upvote(self.forum)
+    
+    def make_vote(self, request, vote):
+        vote.score = 1
+
+
+class DownvotePostBaseView(UpvotePostView):
+    def check_acl(self, request):
+        request.acl.threads.allow_post_downvote(self.forum)
+    
+    def make_vote(self, request, vote):
+        vote.score = -1

+ 2 - 2
misago/apps/threadtype/list/moderation.py

@@ -87,7 +87,7 @@ class ThreadsListModeration(object):
             self.message = Message(form.non_field_errors()[0], 'error')
             self.message = Message(form.non_field_errors()[0], 'error')
         else:
         else:
             form = MoveThreadsForm(request=self.request, forum=self.forum)
             form = MoveThreadsForm(request=self.request, forum=self.forum)
-        return self.request.theme.render_to_response(('%s/move_threads.html' % self.templates_prefix),
+        return self.request.theme.render_to_response(('%ss/move_threads.html' % self.type_prefix),
                                                      {
                                                      {
                                                       'message': self.message,
                                                       'message': self.message,
                                                       'forum': self.forum,
                                                       'forum': self.forum,
@@ -137,7 +137,7 @@ class ThreadsListModeration(object):
             self.message = Message(form.non_field_errors()[0], 'error')
             self.message = Message(form.non_field_errors()[0], 'error')
         else:
         else:
             form = MergeThreadsForm(request=self.request, threads=threads)
             form = MergeThreadsForm(request=self.request, threads=threads)
-        return self.request.theme.render_to_response(('%s/merge.html' % self.templates_prefix),
+        return self.request.theme.render_to_response(('%ss/merge.html' % self.type_prefix),
                                                      {
                                                      {
                                                       'message': self.message,
                                                       'message': self.message,
                                                       'forum': self.forum,
                                                       'forum': self.forum,

+ 1 - 1
misago/apps/threadtype/list/views.py

@@ -109,7 +109,7 @@ class ThreadsListBaseView(ViewBase):
         # Merge proxy into forum
         # Merge proxy into forum
         self.forum.closed = self.proxy.closed
         self.forum.closed = self.proxy.closed
 
 
-        return request.theme.render_to_response(('%s/list.html' % self.templates_prefix),
+        return request.theme.render_to_response(('%ss/list.html' % self.type_prefix),
                                                 {
                                                 {
                                                  'message': self.message,
                                                  'message': self.message,
                                                  'forum': self.forum,
                                                  'forum': self.forum,

+ 2 - 1
misago/apps/threadtype/posting/base.py

@@ -77,6 +77,7 @@ class PostingBaseView(ViewBase):
 
 
         try:
         try:
             self._set_context()
             self._set_context()
+            self.check_forum_type()
             if request.method == 'POST':
             if request.method == 'POST':
                 # Create correct form instance
                 # Create correct form instance
                 if self.allow_quick_reply and 'quick_reply' in request.POST:
                 if self.allow_quick_reply and 'quick_reply' in request.POST:
@@ -112,7 +113,7 @@ class PostingBaseView(ViewBase):
         except ACLError404 as e:
         except ACLError404 as e:
             return error404(request, unicode(e))
             return error404(request, unicode(e))
 
 
-        return request.theme.render_to_response(('%s/posting.html' % self.templates_prefix),
+        return request.theme.render_to_response(('%ss/posting.html' % self.type_prefix),
                                                 {
                                                 {
                                                  'action': self.action,
                                                  'action': self.action,
                                                  'message': self.message,
                                                  'message': self.message,

+ 10 - 4
misago/apps/threadtype/posting/forms.py

@@ -24,8 +24,8 @@ class PostingForm(Form, ValidatePostLengthMixin):
                        ]
                        ]
 
 
         # Can we change threads states?
         # Can we change threads states?
-        if (self.request.acl.threads.can_pin_threads(self.forum) >= self.thread.weight and
-                self.request.acl.threads.can_pin_threads(self.forum)):
+        if (self.request.acl.threads.can_pin_threads(self.forum) and
+            (not self.thread or self.request.acl.threads.can_pin_threads(self.forum) >= self.thread.weight)):
             thread_weight = []
             thread_weight = []
             if self.request.acl.threads.can_pin_threads(self.forum) == 2:
             if self.request.acl.threads.can_pin_threads(self.forum) == 2:
                 thread_weight.append((2, _("Announcement")))
                 thread_weight.append((2, _("Announcement")))
@@ -33,11 +33,15 @@ class PostingForm(Form, ValidatePostLengthMixin):
             thread_weight.append((0, _("Standard")))
             thread_weight.append((0, _("Standard")))
             if thread_weight:
             if thread_weight:
                 self.layout[0][1].append(('thread_weight', {'label': _("Thread Importance")}))
                 self.layout[0][1].append(('thread_weight', {'label': _("Thread Importance")}))
+                try:
+                    current_weight = self.thread.weight
+                except AttributeError:
+                    current_weight = 0
                 self.fields['thread_weight'] = forms.TypedChoiceField(widget=forms.RadioSelect,
                 self.fields['thread_weight'] = forms.TypedChoiceField(widget=forms.RadioSelect,
                                                                       choices=thread_weight,
                                                                       choices=thread_weight,
                                                                       required=False,
                                                                       required=False,
                                                                       coerce=int,
                                                                       coerce=int,
-                                                                      initial=(self.thread.weight if self.thread else 0))
+                                                                      initial=current_weight)
 
 
         # Can we lock threads?
         # Can we lock threads?
         if self.request.acl.threads.can_close(self.forum):
         if self.request.acl.threads.can_close(self.forum):
@@ -50,8 +54,10 @@ class PostingForm(Form, ValidatePostLengthMixin):
     def clean_thread_weight(self):
     def clean_thread_weight(self):
         data = self.cleaned_data['thread_weight']
         data = self.cleaned_data['thread_weight']
         if not data:
         if not data:
-            if self.thread:
+            try:
                 return self.thread.weight
                 return self.thread.weight
+            except AttributeError:
+                pass
             return 0
             return 0
         return data
         return data
 
 

+ 2 - 2
misago/apps/threadtype/thread/moderation/posts.py

@@ -85,7 +85,7 @@ class PostsModeration(object):
                     new_thread.forum.sync()
                     new_thread.forum.sync()
                     new_thread.forum.save(force_update=True)
                     new_thread.forum.save(force_update=True)
                 self.request.messages.set_flash(Message(_("Selected posts have been split to new thread.")), 'success', 'threads')
                 self.request.messages.set_flash(Message(_("Selected posts have been split to new thread.")), 'success', 'threads')
-                return redirect(reverse(self.thread_url, kwargs={'thread': new_thread.pk, 'slug': new_thread.slug}))
+                return redirect(reverse(self.type_prefix, kwargs={'thread': new_thread.pk, 'slug': new_thread.slug}))
             message = Message(form.non_field_errors()[0], 'error')
             message = Message(form.non_field_errors()[0], 'error')
         else:
         else:
             form = SplitThreadForm(request=self.request, initial={
             form = SplitThreadForm(request=self.request, initial={
@@ -132,7 +132,7 @@ class PostsModeration(object):
                     self.forum.sync()
                     self.forum.sync()
                     self.forum.save(force_update=True)
                     self.forum.save(force_update=True)
                 self.request.messages.set_flash(Message(_("Selected posts have been moved to new thread.")), 'success', 'threads')
                 self.request.messages.set_flash(Message(_("Selected posts have been moved to new thread.")), 'success', 'threads')
-                return redirect(reverse(self.thread_url, kwargs={'thread': thread.pk, 'slug': thread.slug}))
+                return redirect(reverse(self.type_prefix, kwargs={'thread': thread.pk, 'slug': thread.slug}))
             message = Message(form.non_field_errors()[0], 'error')
             message = Message(form.non_field_errors()[0], 'error')
         else:
         else:
             form = MovePostsForm(request=self.request)
             form = MovePostsForm(request=self.request)

+ 2 - 1
misago/apps/threadtype/thread/views.py

@@ -163,6 +163,7 @@ class ThreadBaseView(ViewBase):
         self.message = request.messages.get_message('threads')
         self.message = request.messages.get_message('threads')
         try:
         try:
             self.fetch_thread()
             self.fetch_thread()
+            self.check_forum_type()
             self.fetch_posts()
             self.fetch_posts()
             self.make_thread_form()
             self.make_thread_form()
             if self.thread_form:
             if self.thread_form:
@@ -184,7 +185,7 @@ class ThreadBaseView(ViewBase):
         # Merge proxy into forum
         # Merge proxy into forum
         self.forum.closed = self.proxy.closed
         self.forum.closed = self.proxy.closed
 
 
-        return request.theme.render_to_response(('%s/thread.html' % self.templates_prefix),
+        return request.theme.render_to_response(('%ss/thread.html' % self.type_prefix),
                                                 {
                                                 {
                                                  'message': self.message,
                                                  'message': self.message,
                                                  'forum': self.forum,
                                                  'forum': self.forum,