123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391 |
- from django.contrib import messages
- from django.db.transaction import atomic
- from django.http import Http404
- from django.shortcuts import redirect, render
- from django.utils import timezone
- from django.utils.translation import ungettext, ugettext_lazy, ugettext as _
- from misago.forums.lists import get_forum_path
- from misago.threads import moderation
- from misago.threads.forms.moderation import MovePostsForm, SplitThreadForm
- from misago.threads.models import Thread
- from misago.threads.paginator import Paginator
- from misago.threads.views.generic.actions import ActionsBase, ReloadAfterDelete
- __all__ = ['PostsActions']
- def thread_aware_posts(f):
- def decorator(self, request, posts):
- for post in posts:
- post.thread = self.thread
- return f(self, request, posts)
- return decorator
- def changes_thread_state(f):
- @thread_aware_posts
- def decorator(self, request, posts):
- with atomic():
- self.thread.lock()
- response = f(self, request, posts)
- self.thread.synchronize()
- self.thread.save()
- self.forum.lock()
- self.forum.synchronize()
- self.forum.save()
- return response
- return decorator
- class PostsActions(ActionsBase):
- select_items_message = ugettext_lazy(
- "You have to select at least one post.")
- is_mass_action = True
- def redirect_after_deletion(self, request, queryset):
- paginator = Paginator(queryset, 10, 3)
- current_page = int(request.resolver_match.kwargs.get('page', 0))
- if paginator.num_pages < current_page:
- namespace = request.resolver_match.namespace
- url_name = request.resolver_match.url_name
- kwars = request.resolver_match.kwargs
- kwars['page'] = paginator.num_pages
- if kwars['page'] == 1:
- del kwars['page']
- return redirect('%s:%s' % (namespace, url_name), **kwars)
- else:
- return redirect(request.path)
- def get_available_actions(self, kwargs):
- self.thread = kwargs['thread']
- self.forum = self.thread.forum
- actions = []
- if self.thread.acl['can_review']:
- if self.thread.has_moderated_posts:
- actions.append({
- 'action': 'approve',
- 'icon': 'check',
- 'name': _("Approve posts")
- })
- if self.forum.acl['can_merge_posts']:
- actions.append({
- 'action': 'merge',
- 'icon': 'compress',
- 'name': _("Merge posts into one")
- })
- if self.forum.acl['can_move_posts']:
- actions.append({
- 'action': 'move',
- 'icon': 'arrow-right',
- 'name': _("Move posts to other thread")
- })
- if self.forum.acl['can_split_threads']:
- actions.append({
- 'action': 'split',
- 'icon': 'code-fork',
- 'name': _("Split posts to new thread")
- })
- if self.forum.acl['can_protect_posts']:
- actions.append({
- 'action': 'unprotect',
- 'icon': 'unlock-alt',
- 'name': _("Release posts")
- })
- actions.append({
- 'action': 'protect',
- 'icon': 'lock',
- 'name': _("Protect posts")
- })
- if self.forum.acl['can_hide_posts']:
- actions.append({
- 'action': 'unhide',
- 'icon': 'eye',
- 'name': _("Reveal posts")
- })
- actions.append({
- 'action': 'hide',
- 'icon': 'eye-slash',
- 'name': _("Hide posts")
- })
- if self.forum.acl['can_hide_posts'] == 2:
- actions.append({
- 'action': 'delete',
- 'icon': 'times',
- 'name': _("Delete posts"),
- 'confirmation': _("Are you sure you want to delete selected "
- "posts? This action can't be undone.")
- })
- return actions
- @changes_thread_state
- def action_approve(self, request, posts):
- changed_posts = 0
- for post in posts:
- if moderation.approve_post(request.user, post):
- changed_posts += 1
- if changed_posts:
- message = ungettext(
- '%(changed)d post was approved.',
- '%(changed)d posts were approved.',
- changed_posts)
- messages.success(request, message % {'changed': changed_posts})
- else:
- message = _("No posts were approved.")
- messages.info(request, message)
- @changes_thread_state
- def action_merge(self, request, posts):
- first_post = posts[0]
- changed_posts = len(posts)
- if changed_posts < 2:
- message = _("You have to select at least two posts to merge.")
- raise moderation.ModerationError(message)
- for post in posts:
- if not post.poster_id or first_post.poster_id != post.poster_id:
- message = _("You can't merge posts made by different authors.")
- raise moderation.ModerationError(message)
- for post in posts[1:]:
- post.merge(first_post)
- post.delete()
- first_post.save()
- message = ungettext(
- '%(changed)d post was merged.',
- '%(changed)d posts were merged.',
- changed_posts)
- messages.success(request, message % {'changed': changed_posts})
- move_posts_full_template = 'misago/thread/move_posts/full.html'
- move_posts_modal_template = 'misago/thread/move_posts/modal.html'
- @changes_thread_state
- def action_move(self, request, posts):
- if posts[0].id == self.thread.first_post_id:
- message = _("You can't move thread's first post.")
- raise moderation.ModerationError(message)
- form = MovePostsForm(user=request.user, thread=self.thread)
- if 'submit' in request.POST or 'follow' in request.POST:
- form = MovePostsForm(request.POST,
- user=request.user,
- thread=self.thread)
- if form.is_valid():
- for post in posts:
- post.move(form.new_thread)
- post.save()
- form.new_thread.lock()
- form.new_thread.synchronize()
- form.new_thread.save()
- if form.new_thread.forum != self.forum:
- form.new_thread.forum.lock()
- form.new_thread.forum.synchronize()
- form.new_thread.forum.save()
- changed_posts = len(posts)
- message = ungettext(
- '%(changed)d post was moved to "%(thread)s".',
- '%(changed)d posts were moved to "%(thread)s".',
- changed_posts)
- messages.success(request, message % {
- 'changed': changed_posts,
- 'thread': form.new_thread.title
- })
- if 'follow' in request.POST:
- return redirect(form.new_thread.get_absolute_url())
- else:
- return None # trigger thread refresh
- if request.is_ajax():
- template = self.move_posts_modal_template
- else:
- template = self.move_posts_full_template
- return render(request, template, {
- 'form': form,
- 'forum': self.forum,
- 'thread': self.thread,
- 'path': get_forum_path(self.forum),
- 'posts': posts
- })
- split_thread_full_template = 'misago/thread/split/full.html'
- split_thread_modal_template = 'misago/thread/split/modal.html'
- @changes_thread_state
- def action_split(self, request, posts):
- if posts[0].id == self.thread.first_post_id:
- message = _("You can't split thread's first post.")
- raise moderation.ModerationError(message)
- form = SplitThreadForm(acl=request.user.acl)
- if 'submit' in request.POST or 'follow' in request.POST:
- form = SplitThreadForm(request.POST, acl=request.user.acl)
- if form.is_valid():
- split_thread = Thread()
- split_thread.forum = form.cleaned_data['forum']
- split_thread.set_title(
- form.cleaned_data['thread_title'])
- split_thread.starter_name = "-"
- split_thread.starter_slug = "-"
- split_thread.last_poster_name = "-"
- split_thread.last_poster_slug = "-"
- split_thread.started_on = timezone.now()
- split_thread.last_post_on = timezone.now()
- split_thread.save()
- for post in posts:
- post.move(split_thread)
- post.save()
- split_thread.synchronize()
- split_thread.save()
- if split_thread.forum != self.forum:
- split_thread.forum.lock()
- split_thread.forum.synchronize()
- split_thread.forum.save()
- changed_posts = len(posts)
- message = ungettext(
- '%(changed)d post was split to "%(thread)s".',
- '%(changed)d posts were split to "%(thread)s".',
- changed_posts)
- messages.success(request, message % {
- 'changed': changed_posts,
- 'thread': split_thread.title
- })
- if 'follow' in request.POST:
- return redirect(split_thread.get_absolute_url())
- else:
- return None # trigger thread refresh
- if request.is_ajax():
- template = self.split_thread_modal_template
- else:
- template = self.split_thread_full_template
- return render(request, template, {
- 'form': form,
- 'forum': self.forum,
- 'thread': self.thread,
- 'path': get_forum_path(self.forum),
- 'posts': posts
- })
- def action_unprotect(self, request, posts):
- changed_posts = 0
- for post in posts:
- if moderation.unprotect_post(request.user, post):
- changed_posts += 1
- if changed_posts:
- message = ungettext(
- '%(changed)d post was released from protection.',
- '%(changed)d posts were released from protection.',
- changed_posts)
- messages.success(request, message % {'changed': changed_posts})
- else:
- message = _("No posts were released from protection.")
- messages.info(request, message)
- def action_protect(self, request, posts):
- changed_posts = 0
- for post in posts:
- if moderation.protect_post(request.user, post):
- changed_posts += 1
- if changed_posts:
- message = ungettext(
- '%(changed)d post was made protected.',
- '%(changed)d posts were made protected.',
- changed_posts)
- messages.success(request, message % {'changed': changed_posts})
- else:
- message = _("No posts were made protected.")
- messages.info(request, message)
- @changes_thread_state
- def action_unhide(self, request, posts):
- changed_posts = 0
- for post in posts:
- if moderation.unhide_post(request.user, post):
- changed_posts += 1
- if changed_posts:
- message = ungettext(
- '%(changed)d post was made visible.',
- '%(changed)d posts were made visible.',
- changed_posts)
- messages.success(request, message % {'changed': changed_posts})
- else:
- message = _("No posts were made visible.")
- messages.info(request, message)
- @changes_thread_state
- def action_hide(self, request, posts):
- changed_posts = 0
- for post in posts:
- if moderation.hide_post(request.user, post):
- changed_posts += 1
- if changed_posts:
- message = ungettext(
- '%(changed)d post was hidden.',
- '%(changed)d posts were hidden.',
- changed_posts)
- messages.success(request, message % {'changed': changed_posts})
- else:
- message = _("No posts were hidden.")
- messages.info(request, message)
- @changes_thread_state
- def action_delete(self, request, posts):
- changed_posts = 0
- first_deleted = None
- for post in posts:
- if moderation.delete_post(request.user, post):
- changed_posts += 1
- if not first_deleted:
- first_deleted = post
- if changed_posts:
- message = ungettext(
- '%(changed)d post was deleted.',
- '%(changed)d posts were deleted.',
- changed_posts)
- messages.success(request, message % {'changed': changed_posts})
- return ReloadAfterDelete()
- else:
- message = _("No posts were deleted.")
- messages.info(request, message)
|