generic.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. """
  2. Module with basic views for use by inheriting actions
  3. """
  4. from django.contrib import messages
  5. from django.db.transaction import atomic
  6. from django.shortcuts import redirect, render
  7. from django.views.generic import View
  8. from misago.acl import add_acl
  9. from misago.core.shortcuts import get_object_or_404, paginate, validate_slug
  10. from misago.forums.lists import get_forums_list, get_forum_path
  11. from misago.forums.models import Forum
  12. from misago.forums.permissions import allow_see_forum, allow_browse_forum
  13. from misago.threads.forms.posting import (InterruptChanges, EditorFormset,
  14. START, REPLY, EDIT)
  15. from misago.threads.models import Thread, Post
  16. from misago.threads.permissions import allow_see_thread, allow_start_thread
  17. class ForumMixin(object):
  18. """
  19. Mixin for getting forums
  20. """
  21. def get_forum(self, request, lock=False, **kwargs):
  22. forum = self.fetch_forum(request, lock, **kwargs)
  23. self.check_forum_permissions(request, forum)
  24. if kwargs.get('forum_slug'):
  25. validate_slug(forum, kwargs.get('forum_slug'))
  26. return forum
  27. def fetch_forum(self, request, lock=False, **kwargs):
  28. queryset = Forum.objects
  29. if lock:
  30. queryset = queryset.select_for_update()
  31. return get_object_or_404(
  32. queryset, id=kwargs.get('forum_id'), role='forum')
  33. def check_forum_permissions(self, request, forum):
  34. add_acl(request.user, forum)
  35. allow_see_forum(request.user, forum)
  36. allow_browse_forum(request.user, forum)
  37. class ThreadMixin(object):
  38. """
  39. Mixin for getting thread
  40. """
  41. def get_thread(self, request, lock=False, **kwargs):
  42. thread = self.fetch_thread(request, lock, **kwargs)
  43. self.check_thread_permissions(request, thread)
  44. if kwargs.get('thread_slug'):
  45. validate_slug(thread, kwargs.get('thread_slug'))
  46. return thread
  47. def fetch_thread(self, request, lock=False, **kwargs):
  48. queryset = Thread.objects
  49. if lock:
  50. queryset = queryset.select_for_update()
  51. return get_object_or_404(queryset, id=kwargs.get('thread_id'))
  52. def check_thread_permissions(self, request, thread):
  53. add_acl(request.user, thread)
  54. allow_see_thread(request.user, thread)
  55. class ViewBase(ForumMixin, View):
  56. templates_dir = ''
  57. template = ''
  58. def final_template(self):
  59. return '%s/%s' % (self.templates_dir, self.template)
  60. def process_context(self, request, context):
  61. """
  62. Simple hook for extending and manipulating template context.
  63. """
  64. return context
  65. def render(self, request, context=None, template=None):
  66. context = self.process_context(request, context or {})
  67. template = template or self.final_template()
  68. return render(request, template, context)
  69. class ForumView(ViewBase):
  70. """
  71. Basic view for threads lists
  72. """
  73. template = 'list.html'
  74. def get_threads(self, request, forum, **kwargs):
  75. return forum.thread_set
  76. def dispatch(self, request, *args, **kwargs):
  77. forum = self.get_forum(request, **kwargs)
  78. forum.subforums = get_forums_list(request.user, forum)
  79. threads = self.get_threads(request, forum, **kwargs)
  80. return self.render(request, {
  81. 'forum': forum,
  82. 'path': get_forum_path(forum),
  83. })
  84. class ThreadView(ViewBase):
  85. """
  86. Basic view for threads
  87. """
  88. def fetch_thread(self, request, **kwargs):
  89. pass
  90. def dispatch(self, request, *args, **kwargs):
  91. thread = self.fetch_thread(request, **kwargs)
  92. class PostView(ViewBase):
  93. """
  94. Basic view for posts
  95. """
  96. def fetch_post(self, request, **kwargs):
  97. pass
  98. def dispatch(self, request, *args, **kwargs):
  99. post = self.fetch_post(request, **kwargs)
  100. class EditorView(ViewBase):
  101. """
  102. Basic view for starting/replying/editing
  103. """
  104. template = 'editor.html'
  105. def find_mode(self, request, *args, **kwargs):
  106. """
  107. First step: guess from request what kind of view we are
  108. """
  109. is_post = request.method == 'POST'
  110. if 'forum_id' in kwargs:
  111. mode = START
  112. user = request.user
  113. forum = self.get_forum(request, lock=is_post, **kwargs)
  114. thread = Thread(forum=forum, starter=user, last_poster=user)
  115. post = Post(forum=forum, thread=thread, poster=user)
  116. quote = Post(0)
  117. elif 'thread_id' in kwargs:
  118. thread = self.get_thread(request, lock=is_post, **kwargs)
  119. forum = thread.forum
  120. return mode, forum, thread, post, quote
  121. def allow_mode(self, user, mode, forum, thread, post, quote):
  122. """
  123. Second step: check start/reply/edit permissions
  124. """
  125. if mode == START:
  126. self.allow_start(user, forum)
  127. if mode == REPLY:
  128. self.allow_reply(user, forum, thread, quote)
  129. if mode == EDIT:
  130. self.allow_edit(user, forum, thread, post)
  131. def allow_start(self, user, forum):
  132. allow_start_thread(user, forum)
  133. def allow_reply(self, user, forum, thread, quote):
  134. raise NotImplementedError()
  135. def allow_edit(self, user, forum, thread, post):
  136. raise NotImplementedError()
  137. def dispatch(self, request, *args, **kwargs):
  138. if request.method == 'POST':
  139. with atomic():
  140. return self.real_dispatch(request, *args, **kwargs)
  141. else:
  142. return self.real_dispatch(request, *args, **kwargs)
  143. def real_dispatch(self, request, *args, **kwargs):
  144. mode_context = self.find_mode(request, *args, **kwargs)
  145. self.allow_mode(request.user, *mode_context)
  146. mode, forum, thread, post, quote = mode_context
  147. formset = EditorFormset(request=request,
  148. mode=mode,
  149. user=request.user,
  150. forum=forum,
  151. thread=thread,
  152. post=post,
  153. quote=quote)
  154. if request.method == 'POST':
  155. if 'submit' in request.POST and formset.is_valid():
  156. try:
  157. formset.save()
  158. return redirect('misago:index')
  159. except InterruptChanges as e:
  160. messages.error(request, e.message)
  161. else:
  162. formset.update()
  163. return self.render(request, {
  164. 'mode': mode,
  165. 'formset': formset,
  166. 'forms': formset.get_forms_list(),
  167. 'main_forms': formset.get_main_forms(),
  168. 'supporting_forms': formset.get_supporting_forms(),
  169. 'forum': forum,
  170. 'path': get_forum_path(forum),
  171. 'thread': thread,
  172. 'post': post,
  173. 'quote': quote
  174. })