Browse Source

Threads multi-moderation uses floppyforms. #114

Rafał Pitoń 12 years ago
parent
commit
8cdfeb081b

+ 54 - 66
misago/apps/threadtype/list/forms.py

@@ -1,66 +1,54 @@
-from django import forms
-from django.utils.translation import ugettext_lazy as _
-from misago.conf import settings
-from misago.forms import Form, ForumChoiceField
-from misago.models import Forum
-from misago.validators import validate_sluggable
-from misago.apps.threadtype.mixins import ValidateThreadNameMixin
-
-class MoveThreadsForm(Form):
-    error_source = 'new_forum'
-
-    def __init__(self, data=None, request=None, forum=None, *args, **kwargs):
-        self.forum = forum
-        super(MoveThreadsForm, self).__init__(data, request=request, *args, **kwargs)
-
-    def finalize_form(self):
-        self.fields['new_forum'] = ForumChoiceField(queryset=Forum.objects.get(special='root').get_descendants().filter(pk__in=self.request.acl.forums.acl['can_browse']))
-        self.layout = [
-                       [
-                        None,
-                        [
-                         ('new_forum', {'label': _("Move Threads to"), 'help_text': _("Select forum you want to move threads to.")}),
-                         ],
-                        ],
-                       ]
-
-    def clean_new_forum(self):
-        new_forum = self.cleaned_data['new_forum']
-        # Assert its forum and its not current forum
-        if new_forum.type != 'forum':
-            raise forms.ValidationError(_("This is not forum."))
-        if new_forum.pk == self.forum.pk:
-            raise forms.ValidationError(_("New forum is same as current one."))
-        return new_forum
-
-
-class MergeThreadsForm(Form, ValidateThreadNameMixin):
-    def __init__(self, data=None, request=None, threads=[], *args, **kwargs):
-        self.threads = threads
-        super(MergeThreadsForm, self).__init__(data, request=request, *args, **kwargs)
-
-    def finalize_form(self):
-        self.fields['new_forum'] = ForumChoiceField(queryset=Forum.objects.get(special='root').get_descendants().filter(pk__in=self.request.acl.forums.acl['can_browse']), initial=self.threads[0].forum)
-        self.fields['thread_name'] = forms.CharField(
-                                                     max_length=settings.thread_name_max,
-                                                     initial=self.threads[-1].name,
-                                                     validators=[validate_sluggable(
-                                                                                    _("Thread name must contain at least one alpha-numeric character."),
-                                                                                    _("Thread name is too long. Try shorter name.")
-                                                                                    )])
-
-        self.layout = [
-                       [
-                        None,
-                        [
-                         ('thread_name', {'label': _("Thread Name"), 'help_text': _("Name of new thread that will be created as result of merge.")}),
-                         ('new_forum', {'label': _("Thread Forum"), 'help_text': _("Select forum you want to put new thread in.")}),
-                         ],
-                        ],
-                       ]
-
-    def clean_new_forum(self):
-        new_forum = self.cleaned_data['new_forum']
-        if new_forum.type != 'forum':
-            raise forms.ValidationError(_("This is not forum."))
-        return new_forum
+import floppyforms as forms
+from django.utils.translation import ugettext_lazy as _
+from misago.conf import settings
+from misago.forms import Form, ForumChoiceField
+from misago.models import Forum
+from misago.validators import validate_sluggable
+from misago.apps.threadtype.mixins import ValidateThreadNameMixin
+
+class MoveThreadsForm(Form):
+    error_source = 'new_forum'
+
+    def __init__(self, data=None, request=None, forum=None, *args, **kwargs):
+        self.forum = forum
+        super(MoveThreadsForm, self).__init__(data, request=request, *args, **kwargs)
+
+    def finalize_form(self):
+        self.fields['new_forum'] = ForumChoiceField(label=_("Move Threads to"),
+                                                    help_text=_("Select forum you want to move threads to."),
+                                                    queryset=Forum.objects.get(special='root').get_descendants().filter(pk__in=self.request.acl.forums.acl['can_browse']))
+
+    def clean_new_forum(self):
+        new_forum = self.cleaned_data['new_forum']
+        # Assert its forum and its not current forum
+        if new_forum.type != 'forum':
+            raise forms.ValidationError(_("This is not forum."))
+        if new_forum.pk == self.forum.pk:
+            raise forms.ValidationError(_("New forum is same as current one."))
+        return new_forum
+
+
+class MergeThreadsForm(Form, ValidateThreadNameMixin):
+    def __init__(self, data=None, request=None, threads=[], *args, **kwargs):
+        self.threads = threads
+        super(MergeThreadsForm, self).__init__(data, request=request, *args, **kwargs)
+
+    def finalize_form(self):
+        self.fields['new_forum'] = ForumChoiceField(label=_("Thread Forum"),
+                                                    help_text=_("Select forum you want to put new thread in."),
+                                                    queryset=Forum.objects.get(special='root').get_descendants().filter(pk__in=self.request.acl.forums.acl['can_browse']),
+                                                    initial=self.threads[0].forum)
+        self.fields['thread_name'] = forms.CharField(label=_("Thread Name"),
+                                                     help_text=_("Name of new thread that will be created as result of merge."),
+                                                     max_length=settings.thread_name_max,
+                                                     initial=self.threads[-1].name,
+                                                     validators=[validate_sluggable(
+                                                                                    _("Thread name must contain at least one alpha-numeric character."),
+                                                                                    _("Thread name is too long. Try shorter name.")
+                                                                                    )])
+
+    def clean_new_forum(self):
+        new_forum = self.cleaned_data['new_forum']
+        if new_forum.type != 'forum':
+            raise forms.ValidationError(_("This is not forum."))
+        return new_forum

+ 277 - 278
misago/apps/threadtype/list/moderation.py

@@ -1,278 +1,277 @@
-from django.forms import ValidationError
-from django.template import RequestContext
-from django.utils import timezone
-from django.utils.translation import ugettext as _
-from misago.forms import FormLayout
-from misago.messages import Message
-from misago.models import Forum, Thread, Post
-from misago.monitor import monitor, UpdatingMonitor
-from misago.shortcuts import render_to_response
-from misago.apps.threadtype.list.forms import MoveThreadsForm, MergeThreadsForm
-from misago.utils.strings import slugify
-
-class ThreadsListModeration(object):
-    def action_accept(self, ids):
-        if self._action_accept(ids):
-            self.request.messages.set_flash(Message(_('Selected threads have been marked as reviewed and made visible to other members.')), 'success', 'threads')
-        else:
-            self.request.messages.set_flash(Message(_('No threads were marked as reviewed.')), 'info', 'threads')
-
-    def _action_accept(self, ids):
-        accepted = 0
-        users = []
-        for thread in self.threads:
-            if thread.pk in ids and thread.moderated:
-                accepted += 1
-                # Sync thread and post
-                thread.moderated = False
-                thread.replies_moderated -= 1
-                thread.save(force_update=True)
-                thread.start_post.moderated = False
-                thread.start_post.save(force_update=True)
-                thread.set_checkpoint(self.request, 'accepted')
-                thread.update_current_dates()
-                # Sync user
-                if thread.last_post.user:
-                    thread.start_post.user.threads += 1
-                    thread.start_post.user.posts += 1
-                    users.append(thread.start_post.user)
-        if accepted:
-            with UpdatingMonitor() as cm:
-                monitor.increase('threads', accepted)
-                monitor.increase('posts', accepted)
-            self.forum.sync()
-            self.forum.save(force_update=True)
-            for user in users:
-                user.save(force_update=True)
-        return accepted
-
-    def action_annouce(self, ids):
-        if self._action_annouce(ids):
-            self.request.messages.set_flash(Message(_('Selected threads have been turned into announcements.')), 'success', 'threads')
-        else:
-            self.request.messages.set_flash(Message(_('No threads were turned into announcements.')), 'info', 'threads')
-
-    def _action_annouce(self, ids):
-        acl = self.request.acl.threads.get_role(self.forum)
-        annouced = []
-        for thread in self.threads:
-            if thread.pk in ids and thread.weight < 2:
-                annouced.append(thread.pk)
-        if annouced:
-            Thread.objects.filter(id__in=annouced).update(weight=2)
-        return annouced
-
-    def action_sticky(self, ids):
-        if self._action_sticky(ids):
-            self.request.messages.set_flash(Message(_('Selected threads have been sticked to the top of list.')), 'success', 'threads')
-        else:
-            self.request.messages.set_flash(Message(_('No threads were turned into stickies.')), 'info', 'threads')
-
-    def _action_sticky(self, ids):
-        acl = self.request.acl.threads.get_role(self.forum)
-        sticky = []
-        for thread in self.threads:
-            if thread.pk in ids and thread.weight != 1 and (acl['can_pin_threads'] == 2 or thread.weight < 2):
-                sticky.append(thread.pk)
-        if sticky:
-            Thread.objects.filter(id__in=sticky).update(weight=1)
-        return sticky
-
-    def action_normal(self, ids):
-        if self._action_normal(ids):
-            self.request.messages.set_flash(Message(_('Selected threads weight has been removed.')), 'success', 'threads')
-        else:
-            self.request.messages.set_flash(Message(_('No threads have had their weight removed.')), 'info', 'threads')
-
-    def _action_normal(self, ids):
-        normalised = []
-        for thread in self.threads:
-            if thread.pk in ids and thread.weight > 0:
-                normalised.append(thread.pk)
-        if normalised:
-            Thread.objects.filter(id__in=normalised).update(weight=0)
-        return normalised
-
-    def action_move(self, ids):
-        threads = []
-        for thread in self.threads:
-            if thread.pk in ids:
-                threads.append(thread)
-        if self.request.POST.get('origin') == 'move_form':
-            form = MoveThreadsForm(self.request.POST, request=self.request, forum=self.forum)
-            if form.is_valid():
-                new_forum = form.cleaned_data['new_forum']
-                for thread in threads:
-                    thread.move_to(new_forum)
-                    thread.save(force_update=True)
-                    thread.set_checkpoint(self.request, 'moved', forum=self.forum)
-                    thread.update_current_dates()
-                new_forum.sync()
-                new_forum.save(force_update=True)
-                self.forum.sync()
-                self.forum.save(force_update=True)
-                self.request.messages.set_flash(Message(_('Selected threads have been moved to "%(forum)s".') % {'forum': new_forum.name}), 'success', 'threads')
-                return None
-            self.message = Message(form.non_field_errors()[0], 'error')
-        else:
-            form = MoveThreadsForm(request=self.request, forum=self.forum)
-        return render_to_response('%ss/move_threads.html' % self.type_prefix,
-                                  {
-                                  'type_prefix': self.type_prefix,
-                                  'message': self.message,
-                                  'forum': self.forum,
-                                  'parents': self.parents,
-                                  'threads': threads,
-                                  'form': FormLayout(form),
-                                  },
-                                  context_instance=RequestContext(self.request));
-
-    def action_merge(self, ids):
-        if len(ids) < 2:
-            raise ValidationError(_("You have to pick two or more threads to merge."))
-        threads = []
-        for thread in self.threads:
-            if thread.pk in ids:
-                threads.append(thread)
-        if self.request.POST.get('origin') == 'merge_form':
-            form = MergeThreadsForm(self.request.POST, request=self.request, threads=threads)
-            if form.is_valid():
-                new_thread = Thread.objects.create(
-                                                   forum=form.cleaned_data['new_forum'],
-                                                   name=form.cleaned_data['thread_name'],
-                                                   slug=slugify(form.cleaned_data['thread_name']),
-                                                   start=timezone.now(),
-                                                   last=timezone.now()
-                                                   )
-                merged = []
-                for thread in reversed(threads):
-                    merged.append(thread.pk)
-                    thread.merge_with(new_thread)
-                Thread.objects.filter(id__in=merged).delete()
-                new_thread.sync()
-                new_thread.save(force_update=True)
-                new_thread.update_current_dates()
-                self.forum.sync()
-                self.forum.save(force_update=True)
-                if form.cleaned_data['new_forum'].pk != self.forum.pk:
-                    form.cleaned_data['new_forum'].sync()
-                    form.cleaned_data['new_forum'].save(force_update=True)
-                self.request.messages.set_flash(Message(_('Selected threads have been merged into new one.')), 'success', 'threads')
-                return None
-            self.message = Message(form.non_field_errors()[0], 'error')
-        else:
-            form = MergeThreadsForm(request=self.request, threads=threads)
-
-        warning = None
-        lookback = threads[0].last_post_id
-        for thread in threads[1:]:
-            if thread.start_post_id < lookback:
-                warning = Message(_("Warning: Posting times in one or more of threads that you are going to merge are overlapping. This may result in disturbed flow of merged thread."), 'warning')
-                break
-            else:
-                lookback = thread.last_post_id
-
-        return render_to_response('%ss/merge.html' % self.type_prefix,
-                                  {
-                                  'type_prefix': self.type_prefix,
-                                  'message': self.message,
-                                  'warning': warning,
-                                  'forum': self.forum,
-                                  'parents': self.parents,
-                                  'threads': threads,
-                                  'form': FormLayout(form),
-                                  },
-                                  context_instance=RequestContext(self.request));
-
-    def action_open(self, ids):
-        if self._action_open(ids):
-            self.request.messages.set_flash(Message(_('Selected threads have been opened.')), 'success', 'threads')
-        else:
-            self.request.messages.set_flash(Message(_('No threads were opened.')), 'info', 'threads')
-
-    def _action_open(self, ids):        
-        opened = []
-        for thread in self.threads:
-            if thread.pk in ids and thread.closed:
-                opened.append(thread.pk)
-                thread.set_checkpoint(self.request, 'opened')
-        if opened:
-            Thread.objects.filter(id__in=opened).update(closed=False)
-        return opened
-
-    def action_close(self, ids):
-        if self._action_close(ids):
-            self.request.messages.set_flash(Message(_('Selected threads have been closed.')), 'success', 'threads')
-        else:
-            self.request.messages.set_flash(Message(_('No threads were closed.')), 'info', 'threads')
-
-    def _action_close(self, ids):
-        closed = []
-        for thread in self.threads:
-            if thread.pk in ids and not thread.closed:
-                closed.append(thread.pk)
-                thread.set_checkpoint(self.request, 'closed')
-        if closed:
-            Thread.objects.filter(id__in=closed).update(closed=True)
-        return closed
-
-    def action_undelete(self, ids):
-        if self._action_undelete(ids):
-            self.request.messages.set_flash(Message(_('Selected threads have been restored.')), 'success', 'threads')
-        else:
-            self.request.messages.set_flash(Message(_('No threads were restored.')), 'info', 'threads')
-
-    def _action_undelete(self, ids):
-        undeleted = []
-        for thread in self.threads:
-            if thread.pk in ids and thread.deleted:
-                undeleted.append(thread.pk)
-                thread.start_post.deleted = False
-                thread.start_post.save(force_update=True)
-                thread.sync()
-                thread.save(force_update=True)
-                thread.set_checkpoint(self.request, 'undeleted')
-                thread.update_current_dates()
-        if undeleted:
-            self.forum.sync()
-            self.forum.save(force_update=True)
-        return undeleted
-
-    def action_soft(self, ids):
-        if self._action_soft(ids):
-            self.request.messages.set_flash(Message(_('Selected threads have been hidden.')), 'success', 'threads')
-        else:
-            self.request.messages.set_flash(Message(_('No threads were hidden.')), 'info', 'threads')
-
-    def _action_soft(self, ids):
-        deleted = []
-        for thread in self.threads:
-            if thread.pk in ids and not thread.deleted:
-                deleted.append(thread.pk)
-                thread.start_post.deleted = True
-                thread.start_post.save(force_update=True)
-                thread.sync()
-                thread.save(force_update=True)
-                thread.set_checkpoint(self.request, 'deleted')
-                thread.update_current_dates()
-        if deleted:
-            self.forum.sync()
-            self.forum.save(force_update=True)
-        return deleted
-
-    def action_hard(self, ids):
-        if self._action_hard(ids):
-            self.request.messages.set_flash(Message(_('Selected threads have been deleted.')), 'success', 'threads')
-        else:
-            self.request.messages.set_flash(Message(_('No threads were deleted.')), 'info', 'threads')
-    
-    def _action_hard(self, ids):        
-        deleted = []
-        for thread in self.threads:
-            if thread.pk in ids:
-                deleted.append(thread.pk)
-                thread.delete()
-        if deleted:
-            self.forum.sync()
-            self.forum.save(force_update=True)
-        return deleted
+from django.forms import ValidationError
+from django.template import RequestContext
+from django.utils import timezone
+from django.utils.translation import ugettext as _
+from misago.messages import Message
+from misago.models import Forum, Thread, Post
+from misago.monitor import monitor, UpdatingMonitor
+from misago.shortcuts import render_to_response
+from misago.apps.threadtype.list.forms import MoveThreadsForm, MergeThreadsForm
+from misago.utils.strings import slugify
+
+class ThreadsListModeration(object):
+    def action_accept(self, ids):
+        if self._action_accept(ids):
+            self.request.messages.set_flash(Message(_('Selected threads have been marked as reviewed and made visible to other members.')), 'success', 'threads')
+        else:
+            self.request.messages.set_flash(Message(_('No threads were marked as reviewed.')), 'info', 'threads')
+
+    def _action_accept(self, ids):
+        accepted = 0
+        users = []
+        for thread in self.threads:
+            if thread.pk in ids and thread.moderated:
+                accepted += 1
+                # Sync thread and post
+                thread.moderated = False
+                thread.replies_moderated -= 1
+                thread.save(force_update=True)
+                thread.start_post.moderated = False
+                thread.start_post.save(force_update=True)
+                thread.set_checkpoint(self.request, 'accepted')
+                thread.update_current_dates()
+                # Sync user
+                if thread.last_post.user:
+                    thread.start_post.user.threads += 1
+                    thread.start_post.user.posts += 1
+                    users.append(thread.start_post.user)
+        if accepted:
+            with UpdatingMonitor() as cm:
+                monitor.increase('threads', accepted)
+                monitor.increase('posts', accepted)
+            self.forum.sync()
+            self.forum.save(force_update=True)
+            for user in users:
+                user.save(force_update=True)
+        return accepted
+
+    def action_annouce(self, ids):
+        if self._action_annouce(ids):
+            self.request.messages.set_flash(Message(_('Selected threads have been turned into announcements.')), 'success', 'threads')
+        else:
+            self.request.messages.set_flash(Message(_('No threads were turned into announcements.')), 'info', 'threads')
+
+    def _action_annouce(self, ids):
+        acl = self.request.acl.threads.get_role(self.forum)
+        annouced = []
+        for thread in self.threads:
+            if thread.pk in ids and thread.weight < 2:
+                annouced.append(thread.pk)
+        if annouced:
+            Thread.objects.filter(id__in=annouced).update(weight=2)
+        return annouced
+
+    def action_sticky(self, ids):
+        if self._action_sticky(ids):
+            self.request.messages.set_flash(Message(_('Selected threads have been sticked to the top of list.')), 'success', 'threads')
+        else:
+            self.request.messages.set_flash(Message(_('No threads were turned into stickies.')), 'info', 'threads')
+
+    def _action_sticky(self, ids):
+        acl = self.request.acl.threads.get_role(self.forum)
+        sticky = []
+        for thread in self.threads:
+            if thread.pk in ids and thread.weight != 1 and (acl['can_pin_threads'] == 2 or thread.weight < 2):
+                sticky.append(thread.pk)
+        if sticky:
+            Thread.objects.filter(id__in=sticky).update(weight=1)
+        return sticky
+
+    def action_normal(self, ids):
+        if self._action_normal(ids):
+            self.request.messages.set_flash(Message(_('Selected threads weight has been removed.')), 'success', 'threads')
+        else:
+            self.request.messages.set_flash(Message(_('No threads have had their weight removed.')), 'info', 'threads')
+
+    def _action_normal(self, ids):
+        normalised = []
+        for thread in self.threads:
+            if thread.pk in ids and thread.weight > 0:
+                normalised.append(thread.pk)
+        if normalised:
+            Thread.objects.filter(id__in=normalised).update(weight=0)
+        return normalised
+
+    def action_move(self, ids):
+        threads = []
+        for thread in self.threads:
+            if thread.pk in ids:
+                threads.append(thread)
+        if self.request.POST.get('origin') == 'move_form':
+            form = MoveThreadsForm(self.request.POST, request=self.request, forum=self.forum)
+            if form.is_valid():
+                new_forum = form.cleaned_data['new_forum']
+                for thread in threads:
+                    thread.move_to(new_forum)
+                    thread.save(force_update=True)
+                    thread.set_checkpoint(self.request, 'moved', forum=self.forum)
+                    thread.update_current_dates()
+                new_forum.sync()
+                new_forum.save(force_update=True)
+                self.forum.sync()
+                self.forum.save(force_update=True)
+                self.request.messages.set_flash(Message(_('Selected threads have been moved to "%(forum)s".') % {'forum': new_forum.name}), 'success', 'threads')
+                return None
+            self.message = Message(form.non_field_errors()[0], 'error')
+        else:
+            form = MoveThreadsForm(request=self.request, forum=self.forum)
+        return render_to_response('%ss/move_threads.html' % self.type_prefix,
+                                  {
+                                  'type_prefix': self.type_prefix,
+                                  'message': self.message,
+                                  'forum': self.forum,
+                                  'parents': self.parents,
+                                  'threads': threads,
+                                  'form': form,
+                                  },
+                                  context_instance=RequestContext(self.request));
+
+    def action_merge(self, ids):
+        if len(ids) < 2:
+            raise ValidationError(_("You have to pick two or more threads to merge."))
+        threads = []
+        for thread in self.threads:
+            if thread.pk in ids:
+                threads.append(thread)
+        if self.request.POST.get('origin') == 'merge_form':
+            form = MergeThreadsForm(self.request.POST, request=self.request, threads=threads)
+            if form.is_valid():
+                new_thread = Thread.objects.create(
+                                                   forum=form.cleaned_data['new_forum'],
+                                                   name=form.cleaned_data['thread_name'],
+                                                   slug=slugify(form.cleaned_data['thread_name']),
+                                                   start=timezone.now(),
+                                                   last=timezone.now()
+                                                   )
+                merged = []
+                for thread in reversed(threads):
+                    merged.append(thread.pk)
+                    thread.merge_with(new_thread)
+                Thread.objects.filter(id__in=merged).delete()
+                new_thread.sync()
+                new_thread.save(force_update=True)
+                new_thread.update_current_dates()
+                self.forum.sync()
+                self.forum.save(force_update=True)
+                if form.cleaned_data['new_forum'].pk != self.forum.pk:
+                    form.cleaned_data['new_forum'].sync()
+                    form.cleaned_data['new_forum'].save(force_update=True)
+                self.request.messages.set_flash(Message(_('Selected threads have been merged into new one.')), 'success', 'threads')
+                return None
+            self.message = Message(form.non_field_errors()[0], 'error')
+        else:
+            form = MergeThreadsForm(request=self.request, threads=threads)
+
+        warning = None
+        lookback = threads[0].last_post_id
+        for thread in threads[1:]:
+            if thread.start_post_id < lookback:
+                warning = Message(_("Warning: Posting times in one or more of threads that you are going to merge are overlapping. This may result in disturbed flow of merged thread."), 'warning')
+                break
+            else:
+                lookback = thread.last_post_id
+
+        return render_to_response('%ss/merge.html' % self.type_prefix,
+                                  {
+                                  'type_prefix': self.type_prefix,
+                                  'message': self.message,
+                                  'warning': warning,
+                                  'forum': self.forum,
+                                  'parents': self.parents,
+                                  'threads': threads,
+                                  'form': form,
+                                  },
+                                  context_instance=RequestContext(self.request));
+
+    def action_open(self, ids):
+        if self._action_open(ids):
+            self.request.messages.set_flash(Message(_('Selected threads have been opened.')), 'success', 'threads')
+        else:
+            self.request.messages.set_flash(Message(_('No threads were opened.')), 'info', 'threads')
+
+    def _action_open(self, ids):        
+        opened = []
+        for thread in self.threads:
+            if thread.pk in ids and thread.closed:
+                opened.append(thread.pk)
+                thread.set_checkpoint(self.request, 'opened')
+        if opened:
+            Thread.objects.filter(id__in=opened).update(closed=False)
+        return opened
+
+    def action_close(self, ids):
+        if self._action_close(ids):
+            self.request.messages.set_flash(Message(_('Selected threads have been closed.')), 'success', 'threads')
+        else:
+            self.request.messages.set_flash(Message(_('No threads were closed.')), 'info', 'threads')
+
+    def _action_close(self, ids):
+        closed = []
+        for thread in self.threads:
+            if thread.pk in ids and not thread.closed:
+                closed.append(thread.pk)
+                thread.set_checkpoint(self.request, 'closed')
+        if closed:
+            Thread.objects.filter(id__in=closed).update(closed=True)
+        return closed
+
+    def action_undelete(self, ids):
+        if self._action_undelete(ids):
+            self.request.messages.set_flash(Message(_('Selected threads have been restored.')), 'success', 'threads')
+        else:
+            self.request.messages.set_flash(Message(_('No threads were restored.')), 'info', 'threads')
+
+    def _action_undelete(self, ids):
+        undeleted = []
+        for thread in self.threads:
+            if thread.pk in ids and thread.deleted:
+                undeleted.append(thread.pk)
+                thread.start_post.deleted = False
+                thread.start_post.save(force_update=True)
+                thread.sync()
+                thread.save(force_update=True)
+                thread.set_checkpoint(self.request, 'undeleted')
+                thread.update_current_dates()
+        if undeleted:
+            self.forum.sync()
+            self.forum.save(force_update=True)
+        return undeleted
+
+    def action_soft(self, ids):
+        if self._action_soft(ids):
+            self.request.messages.set_flash(Message(_('Selected threads have been hidden.')), 'success', 'threads')
+        else:
+            self.request.messages.set_flash(Message(_('No threads were hidden.')), 'info', 'threads')
+
+    def _action_soft(self, ids):
+        deleted = []
+        for thread in self.threads:
+            if thread.pk in ids and not thread.deleted:
+                deleted.append(thread.pk)
+                thread.start_post.deleted = True
+                thread.start_post.save(force_update=True)
+                thread.sync()
+                thread.save(force_update=True)
+                thread.set_checkpoint(self.request, 'deleted')
+                thread.update_current_dates()
+        if deleted:
+            self.forum.sync()
+            self.forum.save(force_update=True)
+        return deleted
+
+    def action_hard(self, ids):
+        if self._action_hard(ids):
+            self.request.messages.set_flash(Message(_('Selected threads have been deleted.')), 'success', 'threads')
+        else:
+            self.request.messages.set_flash(Message(_('No threads were deleted.')), 'info', 'threads')
+    
+    def _action_hard(self, ids):        
+        deleted = []
+        for thread in self.threads:
+            if thread.pk in ids:
+                deleted.append(thread.pk)
+                thread.delete()
+        if deleted:
+            self.forum.sync()
+            self.forum.save(force_update=True)
+        return deleted

+ 35 - 32
misago/forms/fields.py

@@ -1,32 +1,35 @@
-from mptt.forms import TreeNodeChoiceField
-from recaptcha.client.captcha import API_SSL_SERVER, API_SERVER, VERIFY_SERVER
-from floppyforms import fields
-from django.utils.html import conditional_escape, mark_safe
-from django.utils.translation import ugettext_lazy as _
-from misago.forms.widgets import ReCaptchaWidget
-
-class ForumChoiceField(TreeNodeChoiceField):
-    """
-    Custom forum choice field
-    """
-    def __init__(self, *args, **kwargs):
-        kwargs['level_indicator'] = u'- - '
-        super(ForumChoiceField, self).__init__(*args, **kwargs)
-
-    def _get_level_indicator(self, obj):
-        level = getattr(obj, obj._mptt_meta.level_attr)
-        return mark_safe(conditional_escape(self.level_indicator) * (level - 1))
-
-
-class ReCaptchaField(fields.CharField):
-    widget = ReCaptchaWidget
-    api_error = None
-    def __init__(self, *args, **kwargs):
-        kwargs['label'] = _("Verification Code")
-        kwargs['help_text'] = _("Enter the code from image into the text field.")
-        kwargs['required'] = False
-        super(ReCaptchaField, self).__init__(*args, **kwargs)
-
-
-class QACaptchaField(fields.CharField):
-    pass
+from mptt.forms import TreeNodeChoiceField
+from recaptcha.client.captcha import API_SSL_SERVER, API_SERVER, VERIFY_SERVER
+from floppyforms import fields, widgets
+from django.utils.html import conditional_escape, mark_safe
+from django.utils.translation import ugettext_lazy as _
+from misago.forms.widgets import ReCaptchaWidget
+
+class ForumChoiceField(TreeNodeChoiceField):
+    """
+    Custom forum choice field
+    """
+    widget = widgets.Select
+
+    def __init__(self, *args, **kwargs):
+        kwargs['level_indicator'] = u'- - '
+        super(ForumChoiceField, self).__init__(*args, **kwargs)
+
+    def _get_level_indicator(self, obj):
+        level = getattr(obj, obj._mptt_meta.level_attr)
+        return mark_safe(conditional_escape(self.level_indicator) * (level - 1))
+
+
+class ReCaptchaField(fields.CharField):
+    widget = ReCaptchaWidget
+    api_error = None
+
+    def __init__(self, *args, **kwargs):
+        kwargs['label'] = _("Verification Code")
+        kwargs['help_text'] = _("Enter the code from image into the text field.")
+        kwargs['required'] = False
+        super(ReCaptchaField, self).__init__(*args, **kwargs)
+
+
+class QACaptchaField(fields.CharField):
+    pass

+ 4 - 2
templates/cranefly/threads/merge.html

@@ -1,5 +1,5 @@
 {% extends "cranefly/layout.html" %}
-{% import "_forms.html" as form_theme with context %}
+{% import "forms.html" as form_theme with context %}
 {% import "cranefly/macros.html" as macros with context %}
 
 {% block title %}{{ macros.page_title(title=_("Merge Threads"),parent=forum.name) }}{% endblock %}
@@ -42,13 +42,15 @@
         {% endif %}
 
         <form action="{{ url('forum', forum=forum.pk, slug=forum.slug) }}" method="post">
+          {{ form_theme.hidden_fields(form) }}
           <input type="hidden" name="origin" value="merge_form">
           <input type="hidden" name="list_action" value="merge">
           {% for thread in threads -%}
           <input type="hidden" name="list_items" value="{{ thread.pk }}">
           {% endfor %}
           <div class="form-fields">
-            {{ form_theme.form_widget(form, width=6) }}
+            {{ form_theme.row(form.thread_name, attrs={'class': 'span6'}) }}
+            {{ form_theme.row(form.new_forum, attrs={'class': 'span6'}) }}
           </div>
           <div class="form-actions">
             <button type="submit" class="btn btn-primary">{% trans %}Merge Threads{% endtrans %}</button>

+ 4 - 2
templates/cranefly/threads/move_thread.html

@@ -1,8 +1,8 @@
 {% extends "cranefly/layout.html" %}
-{% import "_forms.html" as form_theme with context %}
+{% import "forms.html" as form_theme with context %}
 {% import "cranefly/macros.html" as macros with context %}
 
-{% block title %}{{ macros.page_title(title=_("Move Threads"),parent=forum.name) }}{% endblock %}
+{% block title %}{{ macros.page_title(title=_("Move Thread"),parent=forum.name) }}{% endblock %}
 
 {% block breadcrumb %}{{ super() }} <span class="divider"><i class="icon-chevron-right"></i></span></li>
 {{ macros.parents_list(parents) }}
@@ -37,6 +37,7 @@
         {% endif %}
 
         <form action="{{ url('thread', thread=thread.pk, slug=thread.slug) }}" method="post">
+          {{ form_theme.hidden_fields(form) }}
           <input type="hidden" name="origin" value="thread_form">
           <input type="hidden" name="thread_action" value="move">
           <input type="hidden" name="do" value="move">
@@ -44,6 +45,7 @@
           <input type="hidden" name="list_items" value="{{ thread.pk }}">
           {% endfor %}
           <div class="form-fields">
+            {{ form_theme.row(form.username, attrs={'class': 'span6'}) }}
             {% do form.fieldsets[0]['fields'][0].update({'label': _("Move Thread To"), 'help_text': _("Select forum you want to move this thread to.")}) %}
             {{ form_theme.form_widget(form, width=6) }}
           </div>

+ 3 - 2
templates/cranefly/threads/move_threads.html

@@ -1,5 +1,5 @@
 {% extends "cranefly/layout.html" %}
-{% import "_forms.html" as form_theme with context %}
+{% import "forms.html" as form_theme with context %}
 {% import "cranefly/macros.html" as macros with context %}
 
 {% block title %}{{ macros.page_title(title=_("Move Threads"),parent=forum.name) }}{% endblock %}
@@ -37,13 +37,14 @@
         {% endif %}
 
         <form action="{{ url('forum', forum=forum.pk, slug=forum.slug) }}" method="post">
+          {{ form_theme.hidden_fields(form) }}
           <input type="hidden" name="origin" value="move_form">
           <input type="hidden" name="list_action" value="move">
           {% for thread in threads -%}
           <input type="hidden" name="list_items" value="{{ thread.pk }}">
           {% endfor %}
           <div class="form-fields">
-            {{ form_theme.form_widget(form, width=6) }}
+            {{ form_theme.row(form.new_forum, attrs={'class': 'span6'}) }}
           </div>
           <div class="form-actions">
             <button type="submit" class="btn btn-primary">{% trans %}Move Threads{% endtrans %}</button>