threadslists.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. from datetime import timedelta
  2. from django.conf import settings
  3. from django.core.exceptions import PermissionDenied
  4. from django.db.models import F, Q
  5. from django.utils import timezone
  6. from django.utils.translation import ugettext as _
  7. from misago.categories.models import CATEGORIES_TREE_ID, Category
  8. from misago.categories.permissions import (
  9. allow_see_category, allow_browse_category)
  10. from misago.core.shortcuts import get_object_or_404, validate_slug
  11. from misago.readtracker import threadstracker
  12. from misago.threads.models import Thread
  13. from misago.threads.permissions import exclude_invisible_threads
  14. def filter_threads_queryset(user, categories, list_type, queryset):
  15. if list_type == 'my':
  16. return queryset.filter(starter=user)
  17. elif list_type == 'subscribed':
  18. subscribed_threads = user.subscription_set.values('thread_id')
  19. return queryset.filter(id__in=subscribed_threads)
  20. elif list_type == 'unapproved':
  21. return queryset.filter(has_unapproved_posts=True)
  22. else:
  23. # grab cutoffs for categories
  24. cutoff_date = timezone.now() - timedelta(
  25. days=settings.MISAGO_FRESH_CONTENT_PERIOD
  26. )
  27. if cutoff_date < user.joined_on:
  28. cutoff_date = user.joined_on
  29. categories_dict = {}
  30. for record in user.categoryread_set.filter(category__in=categories):
  31. if record.last_read_on > cutoff_date:
  32. categories_dict[record.category_id] = record.last_read_on
  33. if list_type == 'new':
  34. # new threads have no entry in reads table
  35. # AND were started after cutoff date
  36. read_threads = user.threadread_set.filter(
  37. category__in=categories
  38. ).values('thread_id')
  39. condition = Q(last_post_on__lte=cutoff_date)
  40. condition = condition | Q(id__in=read_threads)
  41. if categories_dict:
  42. for category_id, category_cutoff in categories_dict.items():
  43. condition = condition | Q(
  44. category_id=category_id,
  45. last_post_on__lte=category_cutoff,
  46. )
  47. return queryset.exclude(condition)
  48. elif list_type == 'unread':
  49. # unread threads were read in past but have new posts
  50. # after cutoff date
  51. read_threads = user.threadread_set.filter(
  52. category__in=categories,
  53. thread__last_post_on__gt=cutoff_date,
  54. last_read_on__lt=F('thread__last_post_on')
  55. ).values('thread_id')
  56. queryset = queryset.filter(id__in=read_threads)
  57. # unread threads have last reply after read/cutoff date
  58. if categories_dict:
  59. conditions = None
  60. for category_id, category_cutoff in categories_dict.items():
  61. condition = Q(
  62. category_id=category_id,
  63. last_post_on__lte=category_cutoff,
  64. )
  65. if conditions:
  66. conditions = conditions | condition
  67. else:
  68. conditions = condition
  69. return queryset.exclude(conditions)
  70. else:
  71. return queryset
  72. def get_threads_queryset(user, categories, list_type):
  73. queryset = exclude_invisible_threads(user, categories, Thread.objects)
  74. if list_type == 'all':
  75. return queryset
  76. else:
  77. return filter_threads_queryset(user, categories, list_type, queryset)
  78. class ThreadsListMixin(object):
  79. def allow_see_list(self, request, category, list_type):
  80. if request.user.is_anonymous():
  81. if list_type == 'my':
  82. raise PermissionDenied( _("You have to sign in to see list of "
  83. "threads that you have started."))
  84. if list_type == 'new':
  85. raise PermissionDenied(_("You have to sign in to see list of "
  86. "threads you haven't read."))
  87. if list_type == 'unread':
  88. raise PermissionDenied(_("You have to sign in to see list of "
  89. "threads with new replies."))
  90. if list_type == 'subscribed':
  91. raise PermissionDenied(_("You have to sign in to see list of "
  92. "threads you are subscribing."))
  93. if list_type == 'unapproved':
  94. raise PermissionDenied(_("You have to sign in to see list of "
  95. "threads with unapproved posts."))
  96. else:
  97. if (list_type == 'unapproved' and
  98. category.pk not in request.user.acl['can_approve_content']):
  99. raise PermissionDenied(_("You don't have permission to "
  100. "approve content in this category."))
  101. def get_categories(self, request):
  102. return [Category.objects.root_category()] + list(
  103. Category.objects.all_categories().filter(
  104. id__in=request.user.acl['visible_categories']
  105. ).select_related('parent'))
  106. def get_subcategories(self, request, category, all_categories):
  107. if category.is_leaf_node():
  108. return []
  109. visible_categories = request.user.acl['visible_categories']
  110. queryset = category.get_descendants().filter(id__in=visible_categories)
  111. return list(queryset.order_by('lft'))
  112. def get_queryset(self, request, categories, list_type):
  113. # [:1] cos we are cutting off root caregory on forum threads list
  114. # as it includes nedless extra condition to DB filter
  115. if categories[0].special_role:
  116. categories = categories[1:]
  117. queryset = get_threads_queryset(request.user, categories, list_type)
  118. return queryset.order_by('-last_post_id')
  119. def get_extra_context(self, request, category, subcategories, list_type):
  120. return {
  121. 'is_index': not settings.MISAGO_CATEGORIES_ON_INDEX
  122. }