threadslist.py 5.6 KB

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