threadposts.py 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. from django.core.exceptions import PermissionDenied
  2. from django.db import transaction
  3. from django.utils.translation import ugettext as _
  4. from rest_framework import viewsets
  5. from rest_framework.decorators import detail_route, list_route
  6. from rest_framework.response import Response
  7. from misago.core.shortcuts import get_int_or_404
  8. from misago.users.online.utils import make_users_status_aware
  9. from ..models import Post
  10. from ..moderation import posts as moderation
  11. from ..permissions.threads import (
  12. allow_delete_event, allow_delete_post, allow_edit_post, allow_reply_thread)
  13. from ..serializers import PostSerializer
  14. from ..viewmodels.post import ThreadPost
  15. from ..viewmodels.posts import ThreadPosts
  16. from ..viewmodels.thread import ForumThread
  17. from .postingendpoint import PostingEndpoint
  18. class ViewSet(viewsets.ViewSet):
  19. thread = None
  20. posts = None
  21. post = None
  22. def get_thread(self, request, pk, read_aware=True, subscription_aware=True, select_for_update=False):
  23. return self.thread(
  24. request,
  25. get_int_or_404(pk),
  26. None,
  27. read_aware,
  28. subscription_aware,
  29. select_for_update
  30. )
  31. def get_thread_for_update(self, request, pk):
  32. return self.get_thread(
  33. request, pk,
  34. read_aware=False,
  35. subscription_aware=False,
  36. select_for_update=True
  37. )
  38. def get_posts(self, request, thread, page):
  39. return self.posts(request, thread, page)
  40. def get_post(self, request, thread, pk, select_for_update=False):
  41. return self.post(request, thread, get_int_or_404(pk), select_for_update)
  42. def get_post_for_update(self, request, thread, pk):
  43. return self.get_post(request, thread, pk, select_for_update=True)
  44. def list(self, request, thread_pk):
  45. page = get_int_or_404(request.query_params.get('page', 0))
  46. if page == 1:
  47. page = 0 # api allows explicit first page
  48. thread = self.get_thread(request, thread_pk)
  49. posts = self.get_posts(request, thread, page)
  50. data = thread.get_frontend_context()
  51. data['post_set'] = posts.get_frontend_context()
  52. return Response(data)
  53. @transaction.atomic
  54. def create(self, request, thread_pk):
  55. thread = self.get_thread_for_update(request, thread_pk).thread
  56. allow_reply_thread(request.user, thread)
  57. post = Post(thread=thread, category=thread.category)
  58. # Put them through posting pipeline
  59. posting = PostingEndpoint(
  60. request,
  61. PostingEndpoint.REPLY,
  62. thread=thread,
  63. post=post
  64. )
  65. if posting.is_valid():
  66. user_posts = request.user.posts
  67. posting.save()
  68. # setup extra data for serialization
  69. post.is_read = False
  70. post.is_new = True
  71. post.poster.posts = user_posts + 1
  72. make_users_status_aware(request.user, [post.poster])
  73. return Response(PostSerializer(post).data)
  74. else:
  75. return Response(posting.errors, status=400)
  76. @transaction.atomic
  77. def update(self, request, thread_pk, pk):
  78. thread = self.get_thread_for_update(request, thread_pk)
  79. post = self.get_post_for_update(request, thread, pk).post
  80. allow_edit_post(request.user, post)
  81. posting = PostingEndpoint(
  82. request,
  83. PostingEndpoint.EDIT,
  84. thread=thread.thread,
  85. post=post
  86. )
  87. if posting.is_valid():
  88. post_edits = post.edits
  89. posting.save()
  90. post.is_read = True
  91. post.is_new = False
  92. post.edits = post_edits + 1
  93. if post.poster:
  94. make_users_status_aware(request.user, [post.poster])
  95. return Response(PostSerializer(post).data)
  96. else:
  97. return Response(posting.errors, status=400)
  98. return Response({})
  99. @transaction.atomic
  100. def delete(self, request, thread_pk, pk):
  101. thread = self.get_thread_for_update(request, thread_pk)
  102. post = self.get_post_for_update(request, thread, pk).post
  103. if post.is_event:
  104. allow_delete_event(request.user, post)
  105. else:
  106. allow_delete_post(request.user, post)
  107. moderation.delete_post(request.user, post)
  108. thread.thread.synchronize()
  109. thread.thread.save()
  110. thread.category.synchronize()
  111. thread.category.save()
  112. return Response({})
  113. @detail_route(methods=['get'], url_path='editor')
  114. def post_editor(self, request, thread_pk, pk):
  115. thread = self.thread(request, get_int_or_404(thread_pk))
  116. post = self.post(request, thread, get_int_or_404(pk)).post
  117. allow_edit_post(request.user, post)
  118. return Response({
  119. 'id': post.pk,
  120. 'api': post.get_api_url(),
  121. 'post': post.original,
  122. 'can_protect': bool(thread.category.acl['can_protect_posts']),
  123. 'is_protected': post.is_protected,
  124. 'poster': post.poster_name
  125. })
  126. @list_route(methods=['get'], url_path='editor')
  127. def reply_editor(self, request, thread_pk):
  128. thread = self.thread(request, get_int_or_404(thread_pk))
  129. allow_reply_thread(request.user, thread.thread)
  130. if 'reply' in request.query_params:
  131. reply_to = self.post(request, thread, get_int_or_404(request.query_params['reply'])).post
  132. if reply_to.is_event:
  133. raise PermissionDenied(_("You can't reply to events."))
  134. if reply_to.is_hidden and not reply_to.acl['can_see_hidden']:
  135. raise PermissionDenied(_("You can't reply to hidden posts."))
  136. return Response({
  137. 'id': reply_to.pk,
  138. 'post': reply_to.original,
  139. 'poster': reply_to.poster_name
  140. })
  141. else:
  142. return Response({})
  143. class ThreadPostsViewSet(ViewSet):
  144. thread = ForumThread
  145. posts = ThreadPosts
  146. post = ThreadPost