privatethreads.py 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. from django.contrib.auth import get_user_model
  2. from django.core.exceptions import PermissionDenied
  3. from django.db.models import Q
  4. from django.http import Http404
  5. from django.utils.translation import ugettext_lazy as _
  6. from misago.acl import add_acl, algebra
  7. from misago.acl.decorators import return_boolean
  8. from misago.acl.models import Role
  9. from misago.categories.models import PRIVATE_THREADS_ROOT_NAME, Category
  10. from misago.core import forms
  11. from ..models import Thread
  12. __all__ = [
  13. 'allow_use_private_threads',
  14. 'can_use_private_threads',
  15. 'allow_see_private_thread',
  16. 'can_see_private_thread',
  17. 'allow_see_private_post',
  18. 'can_see_private_post',
  19. 'allow_change_owner',
  20. 'can_change_owner',
  21. 'allow_add_participants',
  22. 'can_add_participants',
  23. 'allow_remove_participant',
  24. 'can_remove_participant',
  25. 'allow_add_participant',
  26. 'can_add_participant',
  27. 'allow_message_user',
  28. 'can_message_user',
  29. 'exclude_invisible_private_threads',
  30. ]
  31. """
  32. Admin Permissions Form
  33. """
  34. class PermissionsForm(forms.Form):
  35. legend = _("Private threads")
  36. can_use_private_threads = forms.YesNoSwitch(label=_("Can use private threads"))
  37. can_start_private_threads = forms.YesNoSwitch(label=_("Can start private threads"))
  38. max_private_thread_participants = forms.IntegerField(
  39. label=_("Max number of users invited to private thread"),
  40. help_text=_("Enter 0 to don't limit number of participants."),
  41. initial=3,
  42. min_value=0
  43. )
  44. can_add_everyone_to_private_threads = forms.YesNoSwitch(
  45. label=_("Can add everyone to threads"),
  46. help_text=_("Allows user to add users that are blocking him to private threads.")
  47. )
  48. can_report_private_threads = forms.YesNoSwitch(
  49. label=_("Can report private threads"),
  50. help_text=_("Allows user to report private threads they are "
  51. "participating in, making them accessible to moderators.")
  52. )
  53. can_moderate_private_threads = forms.YesNoSwitch(
  54. label=_("Can moderate private threads"),
  55. help_text=_("Allows user to read, reply, edit and delete content "
  56. "in reported private threads.")
  57. )
  58. def change_permissions_form(role):
  59. if isinstance(role, Role) and role.special_role != 'anonymous':
  60. return PermissionsForm
  61. else:
  62. return None
  63. """
  64. ACL Builder
  65. """
  66. def build_acl(acl, roles, key_name):
  67. new_acl = {
  68. 'can_use_private_threads': 0,
  69. 'can_start_private_threads': 0,
  70. 'max_private_thread_participants': 3,
  71. 'can_add_everyone_to_private_threads': 0,
  72. 'can_report_private_threads': 0,
  73. 'can_moderate_private_threads': 0,
  74. }
  75. new_acl.update(acl)
  76. algebra.sum_acls(new_acl, roles=roles, key=key_name,
  77. can_use_private_threads=algebra.greater,
  78. can_start_private_threads=algebra.greater,
  79. max_private_thread_participants=algebra.greater_or_zero,
  80. can_add_everyone_to_private_threads=algebra.greater,
  81. can_report_private_threads=algebra.greater,
  82. can_moderate_private_threads=algebra.greater
  83. )
  84. if not new_acl['can_use_private_threads']:
  85. return new_acl
  86. private_category = Category.objects.private_threads()
  87. new_acl['visible_categories'].append(private_category.pk)
  88. new_acl['browseable_categories'].append(private_category.pk)
  89. if new_acl['can_moderate_private_threads']:
  90. new_acl['can_see_reports'].append(private_category.pk)
  91. category_acl = {
  92. 'can_see': 1,
  93. 'can_browse': 1,
  94. 'can_see_all_threads': 1,
  95. 'can_see_own_threads': 0,
  96. 'can_start_threads': new_acl['can_start_private_threads'],
  97. 'can_reply_threads': 1,
  98. 'can_edit_threads': 1,
  99. 'can_edit_posts': 1,
  100. 'can_hide_own_threads': 0,
  101. 'can_hide_own_posts': 1,
  102. 'thread_edit_time': 0,
  103. 'post_edit_time': 0,
  104. 'can_hide_threads': 0,
  105. 'can_hide_posts': 0,
  106. 'can_protect_posts': 0,
  107. 'can_move_posts': 0,
  108. 'can_merge_posts': 0,
  109. 'can_pin_threads': 0,
  110. 'can_close_threads': 0,
  111. 'can_move_threads': 0,
  112. 'can_merge_threads': 0,
  113. 'can_approve_content': 0,
  114. 'can_report_content': new_acl['can_report_private_threads'],
  115. 'can_see_reports': 0,
  116. 'can_see_posts_likes': 0,
  117. 'can_like_posts': 0,
  118. 'can_hide_events': 0,
  119. }
  120. if new_acl['can_moderate_private_threads']:
  121. category_acl.update({
  122. 'can_edit_threads': 2,
  123. 'can_edit_posts': 2,
  124. 'can_hide_threads': 2,
  125. 'can_hide_posts': 2,
  126. 'can_protect_posts': 1,
  127. 'can_merge_posts': 1,
  128. 'can_see_reports': 1,
  129. 'can_close_threads': 1,
  130. 'can_hide_events': 2,
  131. })
  132. new_acl['categories'][private_category.pk] = category_acl
  133. return new_acl
  134. def add_acl_to_thread(user, thread):
  135. if thread.thread_type.root_name != PRIVATE_THREADS_ROOT_NAME:
  136. return
  137. if not hasattr(thread, 'participant'):
  138. thread.participants_list = []
  139. thread.participant = None
  140. thread.acl.update({
  141. 'can_start_poll': False,
  142. 'can_change_owner': can_change_owner(user, thread),
  143. 'can_add_participants': can_add_participants(user, thread),
  144. })
  145. def register_with(registry):
  146. registry.acl_annotator(Thread, add_acl_to_thread)
  147. """
  148. ACL tests
  149. """
  150. def allow_use_private_threads(user):
  151. if user.is_anonymous():
  152. raise PermissionDenied(_("You have to sign in to use private threads."))
  153. if not user.acl['can_use_private_threads']:
  154. raise PermissionDenied(_("You can't use private threads."))
  155. can_use_private_threads = return_boolean(allow_use_private_threads)
  156. def allow_see_private_thread(user, target):
  157. if user.acl['can_moderate_private_threads']:
  158. can_see_reported = target.has_reported_posts
  159. else:
  160. can_see_reported = False
  161. can_see_participating = user in [p.user for p in target.participants_list]
  162. if not (can_see_participating or can_see_reported):
  163. raise Http404()
  164. can_see_private_thread = return_boolean(allow_see_private_thread)
  165. def allow_see_private_post(user, target):
  166. can_see_reported = user.acl['can_moderate_private_threads']
  167. if not (can_see_reported and target.thread.has_reported_posts):
  168. for participant in target.thread.participants_list:
  169. if participant.user == user and participant.is_removed:
  170. if post.posted_on > target.last_post_on:
  171. raise Http404()
  172. can_see_private_post = return_boolean(allow_see_private_post)
  173. def allow_change_owner(user, target):
  174. is_moderator = user.acl['can_moderate_private_threads']
  175. is_owner = target.participant and target.participant.is_owner
  176. if not (is_owner or is_moderator):
  177. raise PermissionDenied(
  178. _("Only thread owner and moderators can change threads owners."))
  179. if not is_moderator and target.is_closed:
  180. raise PermissionDenied(
  181. _("Only moderators can change closed threads owners."))
  182. can_change_owner = return_boolean(allow_change_owner)
  183. def allow_add_participants(user, target):
  184. is_moderator = user.acl['can_moderate_private_threads']
  185. if not is_moderator:
  186. if not target.participant or not target.participant.is_owner:
  187. raise PermissionDenied(
  188. _("You have to be thread owner to add new participants to it."))
  189. if target.is_closed:
  190. raise PermissionDenied(
  191. _("Only moderators can add participants to closed threads."))
  192. max_participants = user.acl['max_private_thread_participants']
  193. current_participants = len(target.participants_list) - 1
  194. if current_participants >= max_participants:
  195. raise PermissionDenied(
  196. _("You can't add any more new users to this thread."))
  197. can_add_participants = return_boolean(allow_add_participants)
  198. def allow_remove_participant(user, thread, target):
  199. if user.acl['can_moderate_private_threads']:
  200. return
  201. if user == target:
  202. return # we can always remove ourselves
  203. if thread.is_closed:
  204. raise PermissionDenied(
  205. _("Only moderators can remove participants from closed threads."))
  206. if not thread.participant or not thread.participant.is_owner:
  207. raise PermissionDenied(
  208. _("You have to be thread owner to remove participants from it."))
  209. can_remove_participant = return_boolean(allow_remove_participant)
  210. def allow_add_participant(user, target):
  211. message_format = {'user': target.username}
  212. if not can_use_private_threads(target):
  213. raise PermissionDenied(
  214. _("%(user)s can't participate in private threads.") % message_format)
  215. if user.acl['can_add_everyone_to_private_threads']:
  216. return
  217. if user.acl['can_be_blocked'] and target.is_blocking(user):
  218. raise PermissionDenied(_("%(user)s is blocking you.") % message_format)
  219. if target.can_be_messaged_by_nobody:
  220. raise PermissionDenied(
  221. _("%(user)s is not allowing invitations to private threads.") % message_format)
  222. if target.can_be_messaged_by_followed and not target.is_following(user):
  223. message = _("%(user)s limits invitations to private threads to followed users.")
  224. raise PermissionDenied(message % message_format)
  225. can_add_participant = return_boolean(allow_add_participant)
  226. def allow_message_user(user, target):
  227. allow_use_private_threads(user)
  228. if user == target:
  229. raise PermissionDenied(_("You can't message yourself."))
  230. if not user.acl['can_start_private_threads']:
  231. raise PermissionDenied(_("You can't start private threads."))
  232. allow_add_participant(user, target)
  233. can_message_user = return_boolean(allow_message_user)
  234. """
  235. Queryset helpers
  236. """
  237. def exclude_invisible_private_threads(queryset, user):
  238. if user.acl['can_moderate_private_threads']:
  239. see_participating = Q(participants=user)
  240. see_reported = Q(has_reported_posts=True)
  241. return queryset.filter(see_reported | see_participating)
  242. else:
  243. return queryset.filter(participants=user)