privatethreads.py 9.2 KB

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