thread.py 4.4 KB

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