privatethreads.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278
  1. from django.contrib import messages
  2. from django.http import Http404
  3. from django.shortcuts import get_object_or_404, render
  4. from django.utils.translation import ugettext as _
  5. from misago.acl import add_acl
  6. from misago.forums.models import Forum
  7. from misago.threads.models import Thread, ThreadParticipant
  8. from misago.threads.permissions import (allow_use_private_threads,
  9. allow_see_private_thread,
  10. allow_see_private_post,
  11. exclude_invisible_private_threads)
  12. from misago.threads.views import generic
  13. def private_threads_view(klass):
  14. """
  15. decorator for making views check allow_use_private_threads
  16. """
  17. def decorator(f):
  18. def dispatch(self, request, *args, **kwargs):
  19. allow_use_private_threads(request.user)
  20. return f(self, request, *args, **kwargs)
  21. return dispatch
  22. klass.dispatch = decorator(klass.dispatch)
  23. return klass
  24. class PrivateThreadsMixin(object):
  25. """
  26. Mixin is used to make views use different permission tests
  27. """
  28. def get_forum(self, request, lock=False, **kwargs):
  29. forum = Forum.objects.private_threads()
  30. add_acl(request.user, forum)
  31. return forum
  32. def check_forum_permissions(self, request, forum):
  33. add_acl(request.user, forum)
  34. allow_use_private_threads(request.user)
  35. def fetch_thread(self, request, lock=False, select_related=None,
  36. queryset=None, **kwargs):
  37. queryset = queryset or Thread.objects
  38. if lock:
  39. queryset = queryset.select_for_update()
  40. select_related = select_related or []
  41. if not 'forum' in select_related:
  42. select_related.append('forum')
  43. queryset = queryset.select_related(*select_related)
  44. where = {'id': kwargs.get('thread_id')}
  45. thread = get_object_or_404(queryset, **where)
  46. if thread.forum.special_role != 'private_threads':
  47. raise Http404()
  48. return thread
  49. def fetch_thread_participants(self, user, thread):
  50. thread.participants_list = []
  51. thread.participant = None
  52. participants_qs = ThreadParticipant.objects.filter(thread=thread)
  53. participants_qs = participants_qs.select_related('user')
  54. for participant in participants_qs:
  55. participant.thread = thread
  56. thread.participants_list.append(participant)
  57. if participant.user == user:
  58. thread.participant = participant
  59. return thread.participants_list
  60. def check_thread_permissions(self, request, thread):
  61. add_acl(request.user, thread.forum)
  62. add_acl(request.user, thread)
  63. self.fetch_thread_participants(request.user, thread)
  64. allow_see_private_thread(request.user, thread)
  65. allow_use_private_threads(request.user)
  66. def check_post_permissions(self, request, post):
  67. add_acl(request.user, post.forum)
  68. add_acl(request.user, post.thread)
  69. add_acl(request.user, post)
  70. self.fetch_thread_participants(request.user, post.thread)
  71. allow_see_private_post(request.user, post)
  72. allow_see_private_thread(request.user, post.thread)
  73. allow_use_private_threads(request.user)
  74. def exclude_invisible_posts(self, queryset, user, forum, thread):
  75. return queryset
  76. class PrivateThreads(generic.Threads):
  77. def get_queryset(self):
  78. threads_qs = Forum.objects.private_threads().thread_set
  79. return exclude_invisible_private_threads(threads_qs, self.user)
  80. class PrivateThreadsFiltering(generic.ThreadsFiltering):
  81. def get_available_filters(self):
  82. filters = super(PrivateThreadsFiltering, self).get_available_filters()
  83. if self.user.acl['can_moderate_private_threads']:
  84. filters.append({
  85. 'type': 'reported',
  86. 'name': _("With reported posts"),
  87. 'is_label': False,
  88. })
  89. return filters
  90. @private_threads_view
  91. class PrivateThreadsView(generic.ThreadsView):
  92. link_name = 'misago:private_threads'
  93. template = 'misago/privatethreads/list.html'
  94. Threads = PrivateThreads
  95. Filtering = PrivateThreadsFiltering
  96. @private_threads_view
  97. class ThreadParticipantsView(PrivateThreadsMixin, generic.ViewBase):
  98. template = 'misago/privatethreads/participants.html'
  99. def dispatch(self, request, *args, **kwargs):
  100. thread = self.get_thread(request, **kwargs)
  101. if not request.is_ajax():
  102. response = render(request, 'misago/errorpages/wrong_way.html')
  103. response.status_code = 405
  104. return response
  105. participants_qs = thread.threadparticipant_set
  106. participants_qs = participants_qs.select_related('user')
  107. return self.render(request, {
  108. 'participants': participants_qs.order_by('-is_owner', 'user__slug')
  109. })
  110. class PrivateThreadActions(generic.ThreadActions):
  111. def get_available_actions(self, kwargs):
  112. user = kwargs['user']
  113. thread = kwargs['thread']
  114. is_moderator = user.acl['can_moderate_private_threads']
  115. if thread.participant and thread.participant.is_owner:
  116. is_owner = True
  117. else:
  118. is_owner = False
  119. actions = []
  120. if is_moderator and not is_owner:
  121. actions.append({
  122. 'action': 'takeover',
  123. 'icon': 'level-up',
  124. 'name': _("Takeover thread")
  125. })
  126. if is_owner:
  127. actions.append({
  128. 'action': 'participants',
  129. 'icon': 'users',
  130. 'name': _("Edit participants"),
  131. 'is_button': True
  132. })
  133. if is_moderator:
  134. if thread.is_closed:
  135. actions.append({
  136. 'action': 'open',
  137. 'icon': 'unlock-alt',
  138. 'name': _("Open thread")
  139. })
  140. else:
  141. actions.append({
  142. 'action': 'close',
  143. 'icon': 'lock',
  144. 'name': _("Close thread")
  145. })
  146. actions.append({
  147. 'action': 'delete',
  148. 'icon': 'times',
  149. 'name': _("Delete thread"),
  150. 'confirmation': _("Are you sure you want to delete this "
  151. "thread? This action can't be undone.")
  152. })
  153. return actions
  154. def action_takeover(self, request, thread):
  155. ThreadParticipant.objects.set_owner(thread, request.user)
  156. messages.success(request, _("You are now owner of this thread."))
  157. @private_threads_view
  158. class ThreadView(PrivateThreadsMixin, generic.ThreadView):
  159. template = 'misago/privatethreads/thread.html'
  160. ThreadActions = PrivateThreadActions
  161. @private_threads_view
  162. class EditThreadParticipantsView(PrivateThreadsMixin, generic.ViewBase):
  163. template = 'misago/privatethreads/participants_modal.html'
  164. def dispatch(self, request, *args, **kwargs):
  165. thread = self.get_thread(request, **kwargs)
  166. if not request.is_ajax():
  167. response = render(request, 'misago/errorpages/wrong_way.html')
  168. response.status_code = 405
  169. return response
  170. participants_qs = thread.threadparticipant_set
  171. participants_qs = participants_qs.select_related('user')
  172. return self.render(request, {
  173. 'participants': participants_qs.order_by('-is_owner', 'user__slug')
  174. })
  175. """
  176. Generics
  177. """
  178. @private_threads_view
  179. class GotoLastView(PrivateThreadsMixin, generic.GotoLastView):
  180. pass
  181. @private_threads_view
  182. class GotoNewView(PrivateThreadsMixin, generic.GotoNewView):
  183. pass
  184. @private_threads_view
  185. class GotoPostView(PrivateThreadsMixin, generic.GotoPostView):
  186. pass
  187. @private_threads_view
  188. class ReportedPostsListView(PrivateThreadsMixin, generic.ReportedPostsListView):
  189. pass
  190. @private_threads_view
  191. class QuotePostView(PrivateThreadsMixin, generic.QuotePostView):
  192. pass
  193. @private_threads_view
  194. class UnhidePostView(PrivateThreadsMixin, generic.UnhidePostView):
  195. pass
  196. @private_threads_view
  197. class HidePostView(PrivateThreadsMixin, generic.HidePostView):
  198. pass
  199. @private_threads_view
  200. class DeletePostView(PrivateThreadsMixin, generic.DeletePostView):
  201. pass
  202. @private_threads_view
  203. class EventsView(PrivateThreadsMixin, generic.EventsView):
  204. pass
  205. @private_threads_view
  206. class PostingView(PrivateThreadsMixin, generic.PostingView):
  207. pass