thread.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. from django.shortcuts import get_object_or_404
  2. from django.utils.translation import gettext as _
  3. from misago.acl import add_acl
  4. from misago.categories import PRIVATE_THREADS_ROOT_NAME, THREADS_ROOT_NAME
  5. from misago.categories.models import Category
  6. from misago.core.shortcuts import validate_slug
  7. from misago.core.viewmodel import ViewModel as BaseViewModel
  8. from misago.readtracker.threadstracker import make_read_aware
  9. from misago.threads.models import Poll, Thread
  10. from misago.threads.participants import make_participants_aware
  11. from misago.threads.permissions import (
  12. allow_see_private_thread, allow_see_thread, allow_use_private_threads)
  13. from misago.threads.serializers import PrivateThreadSerializer, ThreadSerializer
  14. from misago.threads.subscriptions import make_subscription_aware
  15. from misago.threads.threadtypes import trees_map
  16. __all__ = ['ForumThread', 'PrivateThread']
  17. BASE_RELATIONS = [
  18. 'category',
  19. 'poll',
  20. 'starter',
  21. 'starter__rank',
  22. 'starter__ban_cache',
  23. 'starter__online_tracker',
  24. ]
  25. class ViewModel(BaseViewModel):
  26. def __init__(
  27. self,
  28. request,
  29. pk,
  30. slug=None,
  31. read_aware=False,
  32. subscription_aware=False,
  33. poll_votes_aware=False,
  34. select_for_update=False
  35. ):
  36. model = self.get_thread(request, pk, slug, select_for_update)
  37. model.path = self.get_thread_path(model.category)
  38. add_acl(request.user, model.category)
  39. add_acl(request.user, model)
  40. if read_aware:
  41. make_read_aware(request.user, model)
  42. if subscription_aware:
  43. make_subscription_aware(request.user, model)
  44. self._model = model
  45. try:
  46. self._poll = model.poll
  47. add_acl(request.user, self._poll)
  48. if poll_votes_aware:
  49. self._poll.make_choices_votes_aware(request.user)
  50. except Poll.DoesNotExist:
  51. self._poll = None
  52. @property
  53. def poll(self):
  54. return self._poll
  55. def get_thread(self, request, pk, slug=None, select_for_update=False):
  56. raise NotImplementedError(
  57. 'Thread view model has to implement get_thread(request, pk, slug=None)'
  58. )
  59. def get_thread_path(self, category):
  60. thread_path = []
  61. if category.level:
  62. categories = Category.objects.filter(
  63. tree_id=category.tree_id, lft__lte=category.lft, rght__gte=category.rght
  64. ).order_by('level')
  65. thread_path = list(categories)
  66. else:
  67. thread_path = [category]
  68. thread_path[0].name = self.get_root_name()
  69. return thread_path
  70. def get_root_name(self):
  71. raise NotImplementedError("Thread view model has to implement get_root_name()")
  72. def get_frontend_context(self):
  73. raise NotImplementedError("Thread view model has to implement get_frontend_context()")
  74. def get_template_context(self):
  75. return {
  76. 'thread': self._model,
  77. 'poll': self._poll,
  78. 'category': self._model.category,
  79. 'breadcrumbs': self._model.path,
  80. }
  81. class ForumThread(ViewModel):
  82. def get_thread(self, request, pk, slug=None, select_for_update=False):
  83. if select_for_update:
  84. queryset = Thread.objects.select_for_update()
  85. else:
  86. queryset = Thread.objects.select_related(*BASE_RELATIONS)
  87. thread = get_object_or_404(
  88. queryset,
  89. pk=pk,
  90. category__tree_id=trees_map.get_tree_id_for_root(THREADS_ROOT_NAME),
  91. )
  92. allow_see_thread(request.user, thread)
  93. if slug:
  94. validate_slug(thread, slug)
  95. return thread
  96. def get_root_name(self):
  97. return _("Threads")
  98. def get_frontend_context(self):
  99. return ThreadSerializer(self._model).data
  100. class PrivateThread(ViewModel):
  101. def get_thread(self, request, pk, slug=None, select_for_update=False):
  102. allow_use_private_threads(request.user)
  103. if select_for_update:
  104. queryset = Thread.objects.select_for_update()
  105. else:
  106. queryset = Thread.objects.select_related(*BASE_RELATIONS)
  107. thread = get_object_or_404(
  108. queryset,
  109. pk=pk,
  110. category__tree_id=trees_map.get_tree_id_for_root(PRIVATE_THREADS_ROOT_NAME),
  111. )
  112. make_participants_aware(request.user, thread)
  113. allow_see_private_thread(request.user, thread)
  114. if slug:
  115. validate_slug(thread, slug)
  116. return thread
  117. def get_root_name(self):
  118. return _("Private threads")
  119. def get_frontend_context(self):
  120. return PrivateThreadSerializer(self._model).data