moderation.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. from rest_framework import serializers
  2. from django.core.exceptions import PermissionDenied, ValidationError
  3. from django.http import Http404
  4. from django.utils import six
  5. from django.utils.translation import ugettext as _, ugettext_lazy, ungettext
  6. from misago.acl import add_acl
  7. from misago.conf import settings
  8. from misago.threads.models import Thread
  9. from misago.threads.permissions import (
  10. allow_merge_post, allow_move_post, can_start_thread, exclude_invisible_posts)
  11. from misago.threads.utils import get_thread_id_from_url
  12. from misago.threads.validators import validate_category, validate_title
  13. POSTS_MERGE_LIMIT = settings.MISAGO_POSTS_PER_PAGE + settings.MISAGO_POSTS_TAIL
  14. POSTS_MOVE_LIMIT = settings.MISAGO_POSTS_PER_PAGE + settings.MISAGO_POSTS_TAIL
  15. __all__ = [
  16. 'MergePostsSerializer',
  17. 'MovePostsSerializer',
  18. 'NewThreadSerializer',
  19. ]
  20. class MergePostsSerializer(serializers.Serializer):
  21. posts = serializers.ListField(
  22. child=serializers.IntegerField(
  23. error_messages={
  24. 'invalid': ugettext_lazy("One or more post ids received were invalid."),
  25. },
  26. ),
  27. error_messages={
  28. 'required': ugettext_lazy("You have to select at least two posts to merge."),
  29. },
  30. )
  31. def validate_posts(self, data):
  32. data = list(set(data))
  33. if len(data) < 2:
  34. raise serializers.ValidationError(_("You have to select at least two posts to merge."))
  35. if len(data) > POSTS_MERGE_LIMIT:
  36. message = ungettext(
  37. "No more than %(limit)s post can be merged at single time.",
  38. "No more than %(limit)s posts can be merged at single time.",
  39. POSTS_MERGE_LIMIT,
  40. )
  41. raise serializers.ValidationError(message % {'limit': POSTS_MERGE_LIMIT})
  42. user = self.context['user']
  43. thread = self.context['thread']
  44. posts_queryset = exclude_invisible_posts(user, thread.category, thread.post_set)
  45. posts_queryset = posts_queryset.filter(id__in=data).order_by('id')
  46. posts = []
  47. for post in posts_queryset:
  48. post.category = thread.category
  49. post.thread = thread
  50. try:
  51. allow_merge_post(user, post)
  52. except PermissionDenied as e:
  53. raise serializers.ValidationError(six.text_type(e))
  54. if not posts:
  55. posts.append(post)
  56. else:
  57. authorship_error = _("Posts made by different users can't be merged.")
  58. if posts[0].poster_id:
  59. if post.poster_id != posts[0].poster_id:
  60. raise serializers.ValidationError(authorship_error)
  61. else:
  62. if post.poster_id or post.poster_name != posts[0].poster_name:
  63. raise serializers.ValidationError(authorship_error)
  64. if posts[0].pk != thread.first_post_id:
  65. if (posts[0].is_hidden != post.is_hidden or
  66. posts[0].is_unapproved != post.is_unapproved):
  67. raise serializers.ValidationError(
  68. _("Posts with different visibility can't be merged.")
  69. )
  70. posts.append(post)
  71. if len(posts) != len(data):
  72. raise serializers.ValidationError(_("One or more posts to merge could not be found."))
  73. self.posts_cache = posts
  74. return data
  75. class MovePostsSerializer(serializers.Serializer):
  76. thread_url = serializers.CharField(
  77. error_messages={
  78. 'required': ugettext_lazy("Enter link to new thread."),
  79. },
  80. )
  81. posts = serializers.ListField(
  82. allow_empty=False,
  83. child=serializers.IntegerField(
  84. error_messages={
  85. 'invalid': ugettext_lazy("One or more post ids received were invalid."),
  86. },
  87. ),
  88. error_messages={
  89. 'empty': ugettext_lazy("You have to specify at least one post to move."),
  90. },
  91. )
  92. def validate_thread_url(self, data):
  93. request = self.context['request']
  94. thread = self.context['thread']
  95. viewmodel = self.context['viewmodel']
  96. new_thread_id = get_thread_id_from_url(request, data)
  97. if not new_thread_id:
  98. raise serializers.ValidationError(_("This is not a valid thread link."))
  99. if new_thread_id == thread.pk:
  100. raise serializers.ValidationError(_("Thread to move posts to is same as current one."))
  101. try:
  102. new_thread = viewmodel(request, new_thread_id).unwrap()
  103. except Http404:
  104. raise serializers.ValidationError(
  105. _(
  106. "The thread you have entered link to doesn't "
  107. "exist or you don't have permission to see it."
  108. )
  109. )
  110. if not new_thread.acl['can_reply']:
  111. raise serializers.ValidationError(_("You can't move posts to threads you can't reply."))
  112. self.new_thread = new_thread
  113. return data
  114. def validate_posts(self, data):
  115. data = list(set(data))
  116. if len(data) > POSTS_MOVE_LIMIT:
  117. message = ungettext(
  118. "No more than %(limit)s post can be moved at single time.",
  119. "No more than %(limit)s posts can be moved at single time.",
  120. POSTS_MOVE_LIMIT,
  121. )
  122. raise serializers.ValidationError(message % {'limit': POSTS_MOVE_LIMIT})
  123. request = self.context['request']
  124. thread = self.context['thread']
  125. posts_queryset = exclude_invisible_posts(request.user, thread.category, thread.post_set)
  126. posts_queryset = posts_queryset.filter(id__in=data).order_by('id')
  127. posts = []
  128. for post in posts_queryset:
  129. post.category = thread.category
  130. post.thread = thread
  131. try:
  132. allow_move_post(request.user, post)
  133. posts.append(post)
  134. except PermissionDenied as e:
  135. raise serializers.ValidationError(six.text_type(e))
  136. if len(posts) != len(data):
  137. raise serializers.ValidationError(_("One or more posts to move could not be found."))
  138. self.posts_cache = posts
  139. return data
  140. class NewThreadSerializer(serializers.Serializer):
  141. title = serializers.CharField()
  142. category = serializers.IntegerField()
  143. weight = serializers.IntegerField(
  144. required=False,
  145. allow_null=True,
  146. max_value=Thread.WEIGHT_GLOBAL,
  147. min_value=Thread.WEIGHT_DEFAULT,
  148. )
  149. is_hidden = serializers.NullBooleanField(required=False)
  150. is_closed = serializers.NullBooleanField(required=False)
  151. def validate_title(self, title):
  152. return validate_title(title)
  153. def validate_category(self, category_id):
  154. self.category = validate_category(self.context, category_id)
  155. if not can_start_thread(self.context, self.category):
  156. raise ValidationError(_("You can't create new threads in selected category."))
  157. return self.category
  158. def validate_weight(self, weight):
  159. try:
  160. add_acl(self.context, self.category)
  161. except AttributeError:
  162. return weight # don't validate weight further if category failed
  163. if weight > self.category.acl.get('can_pin_threads', 0):
  164. if weight == 2:
  165. raise ValidationError(
  166. _("You don't have permission to pin threads globally in this category.")
  167. )
  168. else:
  169. raise ValidationError(
  170. _("You don't have permission to pin threads in this category.")
  171. )
  172. return weight
  173. def validate_is_hidden(self, is_hidden):
  174. try:
  175. add_acl(self.context, self.category)
  176. except AttributeError:
  177. return is_hidden # don't validate hidden further if category failed
  178. if is_hidden and not self.category.acl.get('can_hide_threads'):
  179. raise ValidationError(_("You don't have permission to hide threads in this category."))
  180. return is_hidden
  181. def validate_is_closed(self, is_closed):
  182. try:
  183. add_acl(self.context, self.category)
  184. except AttributeError:
  185. return is_closed # don't validate closed further if category failed
  186. if is_closed and not self.category.acl.get('can_close_threads'):
  187. raise ValidationError(
  188. _("You don't have permission to close threads in this category.")
  189. )
  190. return is_closed