123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263 |
- from datetime import timedelta
- from django.conf import settings
- from django.core.exceptions import PermissionDenied
- from django.db.models import F, Q
- from django.http import Http404
- from django.shortcuts import render
- from django.views.generic import View
- from django.utils import timezone
- from django.utils.translation import ugettext as _, ugettext_lazy
- from misago.categories.models import CATEGORIES_TREE_ID, Category
- from misago.categories.permissions import (
- allow_see_category, allow_browse_category)
- from misago.core.shortcuts import (
- get_object_or_404, paginate, pagination_dict, validate_slug)
- from misago.readtracker import threadstracker
- from misago.threads.models import Thread
- from misago.threads.permissions import exclude_invisible_threads
- from misago.threads.utils import add_categories_to_threads
- LISTS_NAMES = {
- 'my': ugettext_lazy("Your threads"),
- 'new': ugettext_lazy("New threads"),
- 'unread': ugettext_lazy("Unread threads"),
- 'subscribed': ugettext_lazy("Subscribed threads"),
- }
- def filter_threads_queryset(user, categories, list_type, queryset):
- if list_type == 'my':
- return queryset.filter(starter=user)
- elif list_type == 'subscribed':
- return queryset # TODO: filter(id__in=[subscribed threads])
- else:
- # grab cutoffs for categories
- cutoff_date = timezone.now() - timedelta(
- days=settings.MISAGO_FRESH_CONTENT_PERIOD
- )
- if cutoff_date < user.reads_cutoff:
- cutoff_date = user.reads_cutoff
- categories_dict = {}
- for record in user.categoryread_set.filter(category__in=categories):
- if record.last_read_on > cutoff_date:
- categories_dict[record.category_id] = record.last_read_on
- if list_type == 'new':
- # new threads have no entry in reads table
- # AND were started after cutoff date
- read_threads = user.threadread_set.filter(
- category__in=categories
- ).values('thread_id')
- if categories_dict:
- condition = Q(
- started_on__lte=cutoff_date,
- id__in=read_threads,
- )
- for category_id, category_cutoff in categories_dict.items():
- condition = condition | Q(
- category_id=category_id,
- started_on__lte=category_cutoff,
- )
- return queryset.exclude(condition)
- else:
- return queryset.exclude(
- started_on__lte=cutoff_date,
- id__in=read_threads,
- )
- elif list_type == 'unread':
- # unread threads were read in past but have new posts
- # after cutoff date
- read_threads = user.threadread_set.filter(
- category__in=categories,
- thread__last_post_on__gt=cutoff_date,
- last_read_on__lt=F('thread__last_post_on')
- ).values('thread_id')
- queryset = queryset.filter(id__in=read_threads)
- # unread threads have last reply after read/cutoff date
- if categories_dict:
- conditions = None
- for category_id, category_cutoff in categories_dict.items():
- condition = Q(
- category_id=category_id,
- last_post_on__lte=category_cutoff,
- )
- if conditions:
- conditions = conditions | condition
- else:
- conditions = condition
- return queryset.exclude(conditions)
- else:
- return queryset
- def get_threads_queryset(user, categories, list_type):
- queryset = Thread.objects
- queryset = exclude_invisible_threads(user, categories, queryset)
- if list_type == 'all':
- return queryset
- else:
- return filter_threads_queryset(user, categories, list_type, queryset)
- class BaseList(View):
- template_name = 'misago/threadslist/threads.html'
- preloaded_data_prefix = ''
- def allow_see_list(self, request, category, list_type):
- if request.user.is_anonymous():
- if list_type == 'my':
- raise PermissionDenied( _("You have to sign in to see list of "
- "threads that you have started."))
- if list_type == 'new':
- raise PermissionDenied(_("You have to sign in to see list of "
- "threads you haven't read."))
- if list_type == 'unread':
- raise PermissionDenied(_("You have to sign in to see list of "
- "threads with new replies."))
- if list_type == 'subscribed':
- raise PermissionDenied(_("You have to sign in to see list of "
- "threads you are subscribing."))
- def get_subcategories(self, request, category):
- if category.is_leaf_node():
- return []
- visible_categories = request.user.acl['visible_categories']
- queryset = category.get_descendants().filter(id__in=visible_categories)
- return list(queryset.order_by('lft'))
- def get_extra_context(self, request, category, subcategories, list_type):
- return {}
- def get(self, request, **kwargs):
- try:
- page = int(request.GET.get('page', 0))
- if page == 1:
- page = None
- except ValueError:
- raise Http404()
- list_type = kwargs['list_type']
- category = self.get_category(request, **kwargs)
- self.allow_see_list(request, category, list_type)
- subcategories = self.get_subcategories(request, category)
- categories = [category] + subcategories
- queryset = self.get_queryset(
- request, categories, list_type).order_by('-last_post_on')
- page = paginate(queryset, page, 24, 6)
- paginator = pagination_dict(page)
- if list_type in ('new', 'unread'):
- """we already know all threads on list are unread"""
- threadstracker.make_unread(page.object_list)
- else:
- threadstracker.make_threads_read_aware(
- request.user, page.object_list)
- add_categories_to_threads(categories, page.object_list)
- visible_subcategories = []
- for thread in page.object_list:
- if (thread.top_category and
- thread.top_category not in visible_subcategories):
- visible_subcategories.append(thread.top_category.pk)
- category.subcategories = []
- for subcategory in subcategories:
- if subcategory.pk in visible_subcategories:
- category.subcategories.append(subcategory)
- extra_context = self.get_extra_context(
- request, category, subcategories, list_type)
- show_toolbar = False
- if paginator['count']:
- if category.subcategories:
- show_toolbar = True
- if request.user.is_authenticated():
- show_toolbar = True
- return render(request, self.template_name, dict(
- category=category,
- show_toolbar=show_toolbar,
- list_type=list_type,
- list_name=LISTS_NAMES.get(list_type),
- threads=page.object_list,
- paginator=paginator,
- count=paginator['count'],
- **extra_context
- ))
- class ThreadsList(BaseList):
- template_name = 'misago/threadslist/threads.html'
- def get_category(self, request, **kwargs):
- return Category.objects.root_category()
- def get_queryset(self, request, categories, list_type):
- # [:1] cos we are cutting off root caregory on forum threads list
- # as it includes nedless extra condition to DB filter
- return get_threads_queryset(request.user, categories[1:], list_type)
- def get_extra_context(self, request, category, subcategories, list_type):
- return {
- 'is_index': not settings.MISAGO_CATEGORIES_ON_INDEX
- }
- class CategoryThreadsList(ThreadsList):
- template_name = 'misago/threadslist/category.html'
- preloaded_data_prefix = 'CATEGORY_'
- def get_category(self, request, **kwargs):
- category = get_object_or_404(Category.objects.select_related('parent'),
- tree_id=CATEGORIES_TREE_ID,
- id=kwargs['category_id'],
- )
- allow_see_category(request.user, category)
- allow_browse_category(request.user, category)
- validate_slug(category, kwargs['category_slug'])
- return category
- def get_queryset(self, request, categories, list_type):
- return get_threads_queryset(request.user, categories, list_type)
- class PrivateThreadsList(ThreadsList):
- template_name = 'misago/threadslist/private_threads.html'
- preloaded_data_prefix = 'PRIVATE_'
- def get_category(self, request, **kwargs):
- return Category.objects.private_threads()
- def get_subcategories(self, request, category):
- return []
|