123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536 |
- from django.contrib import messages
- from django.core.urlresolvers import reverse
- from django.db.transaction import atomic
- from django.shortcuts import redirect
- from django.utils.translation import ugettext_lazy, ugettext as _, ungettext
- from misago.core.shortcuts import paginate
- from misago.forums.lists import get_forums_list, get_forum_path
- from misago.threads import moderation
- from misago.threads.models import ANNOUNCEMENT, Thread, Label
- from misago.threads.permissions import exclude_invisible_threads
- from misago.threads.views.generic.threads import (Actions, Sorting, Threads,
- ThreadsView)
- __all__ = ['ForumFiltering', 'ForumThreads', 'ForumView']
- class ForumActions(Actions):
- def get_available_actions(self, kwargs):
- self.forum = kwargs['forum']
- actions = []
- if self.forum.acl['can_change_threads_labels'] == 2:
- for label in self.forum.labels:
- actions.append({
- 'action': 'label:%s' % label.slug,
- 'icon': 'tag',
- 'name': _('Label as "%(label)s"') % {'label': label.name}
- })
- if self.forum.labels:
- actions.append({
- 'action': 'unlabel',
- 'icon': 'times-circle',
- 'name': _("Remove labels")
- })
- if self.forum.acl['can_change_threads_weight'] == 2:
- actions.append({
- 'action': 'announce',
- 'icon': 'star',
- 'name': _("Change to announcements")
- })
- if self.forum.acl['can_change_threads_weight']:
- actions.append({
- 'action': 'pin',
- 'icon': 'bookmark',
- 'name': _("Change to pinned")
- })
- actions.append({
- 'action': 'reset',
- 'icon': 'circle',
- 'name': _("Reset weight")
- })
- if self.forum.acl['can_close_threads']:
- actions.append({
- 'action': 'open',
- 'icon': 'unlock-alt',
- 'name': _("Open threads")
- })
- actions.append({
- 'action': 'close',
- 'icon': 'lock',
- 'name': _("Close threads")
- })
- if self.forum.acl['can_hide_threads']:
- actions.append({
- 'action': 'unhide',
- 'icon': 'eye',
- 'name': _("Unhide threads")
- })
- actions.append({
- 'action': 'hide',
- 'icon': 'eye-slash',
- 'name': _("Hide threads")
- })
- if self.forum.acl['can_hide_threads'] == 2:
- actions.append({
- 'action': 'delete',
- 'icon': 'times',
- 'name': _("Delete threads")
- })
- return actions
- def action_label(self, request, threads, label_slug):
- for label in self.forum.labels:
- if label.slug == label_slug:
- break
- else:
- raise ModerationError(_("Requested action is invalid."))
- changed_threads = 0
- for thread in threads:
- if moderation.label_thread(request.user, thread, label):
- changed_threads += 1
- if changed_threads:
- message = ungettext(
- '%(changed)d thread was labeled "%(label)s".',
- '%(changed)d threads were labeled "%(label)s".',
- changed_threads)
- messages.success(request, message % {
- 'changed': changed_threads,
- 'label': label.name
- })
- else:
- message = ("No threads were labeled.")
- messages.info(request, message)
- def action_unlabel(self, request, threads):
- changed_threads = 0
- for thread in threads:
- if moderation.unlabel_thread(request.user, thread):
- changed_threads += 1
- if changed_threads:
- message = ungettext(
- '%(changed)d thread label was remoded.',
- '%(changed)d threads labels were removed.',
- changed_threads)
- messages.success(request, message % {'changed': changed_threads})
- else:
- message = ("No threads were unlabeled.")
- messages.info(request, message)
- def action_announce(self, request, threads):
- changed_threads = 0
- for thread in threads:
- if moderation.announce_thread(request.user, thread):
- changed_threads += 1
- if changed_threads:
- message = ungettext(
- '%(changed)d thread was changed to announcement.',
- '%(changed)d threads were changed to announcements.',
- changed_threads)
- messages.success(request, message % {'changed': changed_threads})
- else:
- message = ("No threads were changed to announcements.")
- messages.info(request, message)
- def action_pin(self, request, threads):
- changed_threads = 0
- for thread in threads:
- if moderation.pin_thread(request.user, thread):
- changed_threads += 1
- if changed_threads:
- message = ungettext(
- '%(changed)d thread was pinned.',
- '%(changed)d threads were pinned.',
- changed_threads)
- messages.success(request, message % {'changed': changed_threads})
- else:
- message = ("No threads were pinned.")
- messages.info(request, message)
- def action_reset(self, request, threads):
- changed_threads = 0
- for thread in threads:
- if moderation.reset_thread(request.user, thread):
- changed_threads += 1
- if changed_threads:
- message = ungettext(
- '%(changed)d thread weight was reset.',
- '%(changed)d threads weight was reset.',
- changed_threads)
- messages.success(request, message % {'changed': changed_threads})
- else:
- message = ("No threads weight was reset.")
- messages.info(request, message)
- def action_close(self, request, threads):
- changed_threads = 0
- for thread in threads:
- if moderation.close_thread(request.user, thread):
- changed_threads += 1
- if changed_threads:
- message = ungettext(
- '%(changed)d thread was closed.',
- '%(changed)d threads were closed.',
- changed_threads)
- messages.success(request, message % {'changed': changed_threads})
- else:
- message = ("No threads were closed.")
- messages.info(request, message)
- def action_open(self, request, threads):
- changed_threads = 0
- for thread in threads:
- if moderation.open_thread(request.user, thread):
- changed_threads += 1
- if changed_threads:
- message = ungettext(
- '%(changed)d thread was opened.',
- '%(changed)d threads were opened.',
- changed_threads)
- messages.success(request, message % {'changed': changed_threads})
- else:
- message = ("No threads were opened.")
- messages.info(request, message)
- def action_unhide(self, request, threads):
- changed_threads = 0
- for thread in threads:
- if moderation.unhide_thread(request.user, thread):
- changed_threads += 1
- if changed_threads:
- with atomic():
- self.forum.synchronize()
- self.forum.save()
- message = ungettext(
- '%(changed)d thread was made visible.',
- '%(changed)d threads were made visible.',
- changed_threads)
- messages.success(request, message % {'changed': changed_threads})
- else:
- message = ("No threads were made visible.")
- messages.info(request, message)
- def action_hide(self, request, threads):
- changed_threads = 0
- for thread in threads:
- if moderation.hide_thread(request.user, thread):
- changed_threads += 1
- if changed_threads:
- with atomic():
- self.forum.synchronize()
- self.forum.save()
- message = ungettext(
- '%(changed)d thread was hidden.',
- '%(changed)d threads were hidden.',
- changed_threads)
- messages.success(request, message % {'changed': changed_threads})
- else:
- message = ("No threads were hidden.")
- messages.info(request, message)
- def action_delete(self, request, threads):
- changed_threads = 0
- for thread in threads:
- if moderation.delete_thread(request.user, thread):
- changed_threads += 1
- if changed_threads:
- with atomic():
- self.forum.synchronize()
- self.forum.save()
- message = ungettext(
- '%(changed)d thread was deleted.',
- '%(changed)d threads were deleted.',
- changed_threads)
- messages.success(request, message % {'changed': changed_threads})
- else:
- message = ("No threads were deleted.")
- messages.info(request, message)
- class ForumFiltering(object):
- def __init__(self, forum, link_name, link_params):
- self.forum = forum
- self.link_name = link_name
- self.link_params = link_params.copy()
- self.filters = self.get_available_filters()
- def get_available_filters(self):
- filters = []
- if self.forum.acl['can_see_all_threads']:
- filters.append({
- 'type': 'my-threads',
- 'name': _("My threads"),
- 'is_label': False,
- })
- if self.forum.acl['can_see_reports']:
- filters.append({
- 'type': 'reported',
- 'name': _("With reported posts"),
- 'is_label': False,
- })
- if self.forum.acl['can_review_moderated_content']:
- filters.extend(({
- 'type': 'moderated-threads',
- 'name': _("Moderated threads"),
- 'is_label': False,
- },
- {
- 'type': 'moderated-posts',
- 'name': _("With moderated posts"),
- 'is_label': False,
- }))
- for label in self.forum.labels:
- filters.append({
- 'type': label.slug,
- 'name': label.name,
- 'is_label': True,
- 'css_class': label.css_class,
- })
- return filters
- def clean_kwargs(self, kwargs):
- show = kwargs.get('show')
- if show:
- available_filters = [method['type'] for method in self.filters]
- if show in available_filters:
- self.show = show
- else:
- kwargs.pop('show')
- else:
- self.show = None
- return kwargs
- def filter(self, threads):
- threads.filter(self.show)
- def get_filtering_dics(self):
- try:
- return self._dicts
- except AttributeError:
- self._dicts = self.create_dicts()
- return self._dicts
- def create_dicts(self):
- dicts = []
- if self.forum.acl['can_see_all_threads']:
- default_name = _("All threads")
- else:
- default_name = _("Your threads")
- self.link_params.pop('show', None)
- dicts.append({
- 'type': None,
- 'url': reverse(self.link_name, kwargs=self.link_params),
- 'name': default_name,
- 'is_label': False,
- })
- for filtering in self.filters:
- self.link_params['show'] = filtering['type']
- filtering['url'] = reverse(self.link_name, kwargs=self.link_params)
- dicts.append(filtering)
- return dicts
- @property
- def is_active(self):
- return bool(self.show)
- @property
- def current(self):
- try:
- return self._current
- except AttributeError:
- for filtering in self.get_filtering_dics():
- if filtering['type'] == self.show:
- self._current = filtering
- return filtering
- def choices(self):
- if self.show:
- choices = []
- for filtering in self.get_filtering_dics():
- if filtering['type'] != self.show:
- choices.append(filtering)
- return choices
- else:
- return self.get_filtering_dics()[1:]
- class ForumThreads(Threads):
- def __init__(self, user, forum):
- self.user = user
- self.forum = forum
- self.filter_by = None
- self.sort_by = ('-weight', '-last_post_on')
- def filter(self, filter_by):
- self.filter_by = filter_by
- def sort(self, sort_by):
- if sort_by[0] == '-':
- weight = '-weight'
- else:
- weight = 'weight'
- self.sort_by = (weight, sort_by)
- def list(self, page=0):
- queryset = self.get_queryset()
- queryset = queryset.order_by(*self.sort_by)
- announcements_qs = queryset.filter(weight=ANNOUNCEMENT)
- threads_qs = queryset.filter(weight__lt=ANNOUNCEMENT)
- self._page = paginate(threads_qs, page, 20, 10)
- self._paginator = self._page.paginator
- threads = []
- for announcement in announcements_qs:
- threads.append(announcement)
- for thread in self._page.object_list:
- threads.append(thread)
- for thread in threads:
- thread.forum = self.forum
- self.label_threads(threads, self.forum.labels)
- self.make_threads_read_aware(threads)
- return threads
- def filter_threads(self, queryset):
- if self.filter_by == 'my-threads':
- return queryset.filter(starter_id=self.user.id)
- else:
- if self.forum.acl['can_see_own_threads']:
- if self.user.is_authenticated():
- queryset = queryset.filter(starter_id=self.user.id)
- else:
- queryset = queryset.filter(starter_id=0)
- if self.filter_by == 'reported':
- return queryset.filter(has_reported_posts=True)
- elif self.filter_by == 'moderated-threads':
- return queryset.filter(is_moderated=True)
- elif self.filter_by == 'moderated-posts':
- return queryset.filter(has_moderated_posts=True)
- else:
- for label in self.forum.labels:
- if label.slug == self.filter_by:
- return queryset.filter(label_id=label.pk)
- else:
- return queryset
- def get_queryset(self):
- queryset = exclude_invisible_threads(
- self.user, self.forum, self.forum.thread_set)
- return self.filter_threads(queryset)
- error_message = ("threads list has to be loaded via call to list() before "
- "pagination data will be available")
- @property
- def paginator(self):
- try:
- return self._paginator
- except AttributeError:
- raise AttributeError(self.error_message)
- @property
- def page(self):
- try:
- return self._page
- except AttributeError:
- raise AttributeError(self.error_message)
- class ForumView(ThreadsView):
- """
- Basic view for forum threads lists
- """
- template = 'misago/threads/forum.html'
- Threads = ForumThreads
- Sorting = Sorting
- Filtering = ForumFiltering
- Actions = ForumActions
- def dispatch(self, request, *args, **kwargs):
- forum = self.get_forum(request, **kwargs)
- forum.labels = Label.objects.get_forum_labels(forum)
- if forum.lft + 1 < forum.rght:
- forum.subforums = get_forums_list(request.user, forum)
- else:
- forum.subforums = []
- page_number = kwargs.pop('page', None)
- cleaned_kwargs = self.clean_kwargs(request, kwargs)
- sorting = self.Sorting(self.link_name, cleaned_kwargs)
- cleaned_kwargs = sorting.clean_kwargs(cleaned_kwargs)
- filtering = self.Filtering(forum, self.link_name, cleaned_kwargs)
- cleaned_kwargs = filtering.clean_kwargs(cleaned_kwargs)
- if cleaned_kwargs != kwargs:
- return redirect('misago:forum', **cleaned_kwargs)
- threads = self.Threads(request.user, forum)
- sorting.sort(threads)
- filtering.filter(threads)
- actions = self.Actions(user=request.user, forum=forum)
- if request.method == 'POST':
- # see if we can delegate anything to actions manager
- response = actions.handle_post(request, threads.get_queryset())
- if response:
- return response
- return self.render(request, {
- 'link_name': self.link_name,
- 'links_params': cleaned_kwargs,
- 'forum': forum,
- 'path': get_forum_path(forum),
- 'threads': threads.list(page_number),
- 'page': threads.page,
- 'paginator': threads.paginator,
- 'list_actions': actions.get_list(),
- 'selected_threads': actions.get_selected_ids(),
- 'sorting': sorting,
- 'filtering': filtering,
- })
|