threads.py 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300
  1. from django import forms
  2. from django.core.exceptions import PermissionDenied
  3. from django.db.models import Q
  4. from django.http import Http404
  5. from django.utils import timezone
  6. from django.utils.translation import ugettext_lazy as _
  7. from django.utils.translation import ungettext
  8. from misago.acl import add_acl, algebra
  9. from misago.acl.decorators import return_boolean
  10. from misago.acl.models import Role
  11. from misago.categories.models import Category, CategoryRole
  12. from misago.categories.permissions import get_categories_roles
  13. from misago.core.forms import YesNoSwitch
  14. from misago.threads.models import Post, Thread
  15. __all__ = [
  16. 'allow_see_thread',
  17. 'can_see_thread',
  18. 'allow_start_thread',
  19. 'can_start_thread',
  20. 'allow_reply_thread',
  21. 'can_reply_thread',
  22. 'allow_edit_thread',
  23. 'can_edit_thread',
  24. 'allow_pin_thread',
  25. 'can_pin_thread',
  26. 'allow_unhide_thread',
  27. 'can_unhide_thread',
  28. 'allow_hide_thread',
  29. 'can_hide_thread',
  30. 'allow_delete_thread',
  31. 'can_delete_thread',
  32. 'allow_move_thread',
  33. 'can_move_thread',
  34. 'allow_merge_thread',
  35. 'can_merge_thread',
  36. 'allow_approve_thread',
  37. 'can_approve_thread',
  38. 'allow_see_post',
  39. 'can_see_post',
  40. 'allow_edit_post',
  41. 'can_edit_post',
  42. 'allow_unhide_post',
  43. 'can_unhide_post',
  44. 'allow_hide_post',
  45. 'can_hide_post',
  46. 'allow_delete_post',
  47. 'can_delete_post',
  48. 'allow_protect_post',
  49. 'can_protect_post',
  50. 'allow_approve_post',
  51. 'can_approve_post',
  52. 'allow_move_post',
  53. 'can_move_post',
  54. 'allow_unhide_event',
  55. 'can_unhide_event',
  56. 'allow_hide_event',
  57. 'can_hide_event',
  58. 'allow_delete_event',
  59. 'can_delete_event',
  60. 'exclude_invisible_threads',
  61. 'exclude_invisible_posts',
  62. ]
  63. class RolePermissionsForm(forms.Form):
  64. legend = _("Threads")
  65. can_see_unapproved_content_lists = YesNoSwitch(
  66. label=_("Can see unapproved content list"),
  67. help_text=_(
  68. 'Allows access to "unapproved" tab on threads lists for '
  69. "easy listing of threads that are unapproved or contain "
  70. "unapproved posts. Despite the tab being available on all "
  71. "threads lists, it will only display threads belonging to "
  72. "categories in which the user has permission to approve "
  73. "content."
  74. ),
  75. )
  76. can_see_reported_content_lists = YesNoSwitch(
  77. label=_("Can see reported content list"),
  78. help_text=_(
  79. 'Allows access to "reported" tab on threads lists for '
  80. "easy listing of threads that contain reported posts. "
  81. "Despite the tab being available on all categories "
  82. "threads lists, it will only display threads belonging to "
  83. "categories in which the user has permission to see posts "
  84. "reports."
  85. ),
  86. )
  87. can_omit_flood_protection = YesNoSwitch(
  88. label=_("Can omit flood protection"),
  89. help_text=_("Allows posting more frequently than flood protection would."),
  90. )
  91. class CategoryPermissionsForm(forms.Form):
  92. legend = _("Threads")
  93. can_see_all_threads = forms.TypedChoiceField(
  94. label=_("Can see threads"),
  95. coerce=int,
  96. initial=0,
  97. choices=[
  98. (0, _("Started threads")),
  99. (1, _("All threads")),
  100. ],
  101. )
  102. can_start_threads = YesNoSwitch(label=_("Can start threads"))
  103. can_reply_threads = YesNoSwitch(label=_("Can reply to threads"))
  104. can_edit_threads = forms.TypedChoiceField(
  105. label=_("Can edit threads"),
  106. coerce=int,
  107. initial=0,
  108. choices=[
  109. (0, _("No")),
  110. (1, _("Own threads")),
  111. (2, _("All threads")),
  112. ],
  113. )
  114. can_hide_own_threads = forms.TypedChoiceField(
  115. label=_("Can hide own threads"),
  116. help_text=_(
  117. "Only threads started within time limit and "
  118. "with no replies can be hidden."
  119. ),
  120. coerce=int,
  121. initial=0,
  122. choices=[
  123. (0, _("No")),
  124. (1, _("Hide threads")),
  125. (2, _("Delete threads")),
  126. ],
  127. )
  128. thread_edit_time = forms.IntegerField(
  129. label=_("Time limit for own threads edits, in minutes"),
  130. help_text=_("Enter 0 to don't limit time for editing own threads."),
  131. initial=0,
  132. min_value=0,
  133. )
  134. can_hide_threads = forms.TypedChoiceField(
  135. label=_("Can hide all threads"),
  136. coerce=int,
  137. initial=0,
  138. choices=[
  139. (0, _("No")),
  140. (1, _("Hide threads")),
  141. (2, _("Delete threads")),
  142. ],
  143. )
  144. can_pin_threads = forms.TypedChoiceField(
  145. label=_("Can pin threads"),
  146. coerce=int,
  147. initial=0,
  148. choices=[
  149. (0, _("No")),
  150. (1, _("Locally")),
  151. (2, _("Globally")),
  152. ],
  153. )
  154. can_close_threads = YesNoSwitch(label=_("Can close threads"))
  155. can_move_threads = YesNoSwitch(label=_("Can move threads"))
  156. can_merge_threads = YesNoSwitch(label=_("Can merge threads"))
  157. can_edit_posts = forms.TypedChoiceField(
  158. label=_("Can edit posts"),
  159. coerce=int,
  160. initial=0,
  161. choices=[
  162. (0, _("No")),
  163. (1, _("Own posts")),
  164. (2, _("All posts")),
  165. ],
  166. )
  167. can_hide_own_posts = forms.TypedChoiceField(
  168. label=_("Can hide own posts"),
  169. help_text=_("Only last posts to thread made within edit time limit can be hidden."),
  170. coerce=int,
  171. initial=0,
  172. choices=[
  173. (0, _("No")),
  174. (1, _("Hide posts")),
  175. (2, _("Delete posts")),
  176. ],
  177. )
  178. post_edit_time = forms.IntegerField(
  179. label=_("Time limit for own post edits, in minutes"),
  180. help_text=_("Enter 0 to don't limit time for editing own posts."),
  181. initial=0,
  182. min_value=0,
  183. )
  184. can_hide_posts = forms.TypedChoiceField(
  185. label=_("Can hide all posts"),
  186. coerce=int,
  187. initial=0,
  188. choices=[
  189. (0, _("No")),
  190. (1, _("Hide posts")),
  191. (2, _("Delete posts")),
  192. ],
  193. )
  194. can_see_posts_likes = forms.TypedChoiceField(
  195. label=_("Can see posts likes"),
  196. coerce=int,
  197. initial=0,
  198. choices=[
  199. (0, _("No")),
  200. (1, _("Number only")),
  201. (2, _("Number and list of likers")),
  202. ],
  203. )
  204. can_like_posts = YesNoSwitch(
  205. label=_("Can like posts"),
  206. help_text=_("Only users with this permission to see likes can like posts."),
  207. )
  208. can_protect_posts = YesNoSwitch(
  209. label=_("Can protect posts"),
  210. help_text=_("Only users with this permission can edit protected posts."),
  211. )
  212. can_move_posts = YesNoSwitch(
  213. label=_("Can move posts"), help_text=_("Will be able to move posts to other threads.")
  214. )
  215. can_merge_posts = YesNoSwitch(label=_("Can merge posts"))
  216. can_approve_content = YesNoSwitch(
  217. label=_("Can approve content"),
  218. help_text=_("Will be able to see and approve unapproved content."),
  219. )
  220. can_report_content = YesNoSwitch(label=_("Can report posts"))
  221. can_see_reports = YesNoSwitch(label=_("Can see reports"))
  222. can_hide_events = forms.TypedChoiceField(
  223. label=_("Can hide events"),
  224. coerce=int,
  225. initial=0,
  226. choices=[
  227. (0, _("No")),
  228. (1, _("Hide events")),
  229. (2, _("Delete events")),
  230. ],
  231. )
  232. require_threads_approval = YesNoSwitch(label=_("Require threads approval"))
  233. require_replies_approval = YesNoSwitch(label=_("Require replies approval"))
  234. require_edits_approval = YesNoSwitch(label=_("Require edits approval"))
  235. def change_permissions_form(role):
  236. if isinstance(role, Role) and role.special_role != 'anonymous':
  237. return RolePermissionsForm
  238. elif isinstance(role, CategoryRole):
  239. return CategoryPermissionsForm
  240. else:
  241. return None
  242. def build_acl(acl, roles, key_name):
  243. acl.update({
  244. 'can_see_unapproved_content_lists': False,
  245. 'can_see_reported_content_lists': False,
  246. 'can_omit_flood_protection': False,
  247. 'can_approve_content': [],
  248. 'can_see_reports': [],
  249. })
  250. acl = algebra.sum_acls(
  251. acl,
  252. roles=roles,
  253. key=key_name,
  254. can_see_unapproved_content_lists=algebra.greater,
  255. can_see_reported_content_lists=algebra.greater,
  256. can_omit_flood_protection=algebra.greater
  257. )
  258. categories_roles = get_categories_roles(roles)
  259. categories = list(Category.objects.all_categories(include_root=True))
  260. for category in categories:
  261. category_acl = acl['categories'].get(category.pk, {'can_browse': 0})
  262. if category_acl['can_browse']:
  263. category_acl = acl['categories'][category.pk] = build_category_acl(
  264. category_acl, category, categories_roles, key_name
  265. )
  266. if category_acl.get('can_approve_content'):
  267. acl['can_approve_content'].append(category.pk)
  268. if category_acl.get('can_see_reports'):
  269. acl['can_see_reports'].append(category.pk)
  270. return acl
  271. def build_category_acl(acl, category, categories_roles, key_name):
  272. category_roles = categories_roles.get(category.pk, [])
  273. final_acl = {
  274. 'can_see_all_threads': 0,
  275. 'can_start_threads': 0,
  276. 'can_reply_threads': 0,
  277. 'can_edit_threads': 0,
  278. 'can_edit_posts': 0,
  279. 'can_hide_own_threads': 0,
  280. 'can_hide_own_posts': 0,
  281. 'thread_edit_time': 0,
  282. 'post_edit_time': 0,
  283. 'can_hide_threads': 0,
  284. 'can_hide_posts': 0,
  285. 'can_protect_posts': 0,
  286. 'can_move_posts': 0,
  287. 'can_merge_posts': 0,
  288. 'can_pin_threads': 0,
  289. 'can_close_threads': 0,
  290. 'can_move_threads': 0,
  291. 'can_merge_threads': 0,
  292. 'can_report_content': 0,
  293. 'can_see_reports': 0,
  294. 'can_see_posts_likes': 0,
  295. 'can_like_posts': 0,
  296. 'can_approve_content': 0,
  297. 'require_threads_approval': 0,
  298. 'require_replies_approval': 0,
  299. 'require_edits_approval': 0,
  300. 'can_hide_events': 0,
  301. }
  302. final_acl.update(acl)
  303. algebra.sum_acls(
  304. final_acl,
  305. roles=category_roles,
  306. key=key_name,
  307. can_see_all_threads=algebra.greater,
  308. can_start_threads=algebra.greater,
  309. can_reply_threads=algebra.greater,
  310. can_edit_threads=algebra.greater,
  311. can_edit_posts=algebra.greater,
  312. can_hide_threads=algebra.greater,
  313. can_hide_posts=algebra.greater,
  314. can_hide_own_threads=algebra.greater,
  315. can_hide_own_posts=algebra.greater,
  316. thread_edit_time=algebra.greater_or_zero,
  317. post_edit_time=algebra.greater_or_zero,
  318. can_protect_posts=algebra.greater,
  319. can_move_posts=algebra.greater,
  320. can_merge_posts=algebra.greater,
  321. can_pin_threads=algebra.greater,
  322. can_close_threads=algebra.greater,
  323. can_move_threads=algebra.greater,
  324. can_merge_threads=algebra.greater,
  325. can_report_content=algebra.greater,
  326. can_see_reports=algebra.greater,
  327. can_see_posts_likes=algebra.greater,
  328. can_like_posts=algebra.greater,
  329. can_approve_content=algebra.greater,
  330. require_threads_approval=algebra.greater,
  331. require_replies_approval=algebra.greater,
  332. require_edits_approval=algebra.greater,
  333. can_hide_events=algebra.greater,
  334. )
  335. return final_acl
  336. def add_acl_to_category(user, category):
  337. category_acl = user.acl_cache['categories'].get(category.pk, {})
  338. category.acl.update({
  339. 'can_see_all_threads': 0,
  340. 'can_see_own_threads': 0,
  341. 'can_start_threads': 0,
  342. 'can_reply_threads': 0,
  343. 'can_edit_threads': 0,
  344. 'can_edit_posts': 0,
  345. 'can_hide_own_threads': 0,
  346. 'can_hide_own_posts': 0,
  347. 'thread_edit_time': 0,
  348. 'post_edit_time': 0,
  349. 'can_hide_threads': 0,
  350. 'can_hide_posts': 0,
  351. 'can_protect_posts': 0,
  352. 'can_move_posts': 0,
  353. 'can_merge_posts': 0,
  354. 'can_pin_threads': 0,
  355. 'can_close_threads': 0,
  356. 'can_move_threads': 0,
  357. 'can_merge_threads': 0,
  358. 'can_report_content': 0,
  359. 'can_see_reports': 0,
  360. 'can_see_posts_likes': 0,
  361. 'can_like_posts': 0,
  362. 'can_approve_content': 0,
  363. 'require_threads_approval': category.require_threads_approval,
  364. 'require_replies_approval': category.require_replies_approval,
  365. 'require_edits_approval': category.require_edits_approval,
  366. 'can_hide_events': 0,
  367. })
  368. algebra.sum_acls(
  369. category.acl,
  370. acls=[category_acl],
  371. can_see_all_threads=algebra.greater,
  372. can_see_posts_likes=algebra.greater,
  373. )
  374. if user.is_authenticated:
  375. algebra.sum_acls(
  376. category.acl,
  377. acls=[category_acl],
  378. can_start_threads=algebra.greater,
  379. can_reply_threads=algebra.greater,
  380. can_edit_threads=algebra.greater,
  381. can_edit_posts=algebra.greater,
  382. can_hide_threads=algebra.greater,
  383. can_hide_posts=algebra.greater,
  384. can_hide_own_threads=algebra.greater,
  385. can_hide_own_posts=algebra.greater,
  386. thread_edit_time=algebra.greater_or_zero,
  387. post_edit_time=algebra.greater_or_zero,
  388. can_protect_posts=algebra.greater,
  389. can_move_posts=algebra.greater,
  390. can_merge_posts=algebra.greater,
  391. can_pin_threads=algebra.greater,
  392. can_close_threads=algebra.greater,
  393. can_move_threads=algebra.greater,
  394. can_merge_threads=algebra.greater,
  395. can_report_content=algebra.greater,
  396. can_see_reports=algebra.greater,
  397. can_like_posts=algebra.greater,
  398. can_approve_content=algebra.greater,
  399. require_threads_approval=algebra.greater,
  400. require_replies_approval=algebra.greater,
  401. require_edits_approval=algebra.greater,
  402. can_hide_events=algebra.greater,
  403. )
  404. if user.acl_cache['can_approve_content']:
  405. category.acl.update({
  406. 'require_threads_approval': 0,
  407. 'require_replies_approval': 0,
  408. 'require_edits_approval': 0,
  409. })
  410. category.acl['can_see_own_threads'] = not category.acl['can_see_all_threads']
  411. def add_acl_to_thread(user, thread):
  412. category_acl = user.acl_cache['categories'].get(thread.category_id, {})
  413. thread.acl.update({
  414. 'can_reply': can_reply_thread(user, thread),
  415. 'can_edit': can_edit_thread(user, thread),
  416. 'can_pin': can_pin_thread(user, thread),
  417. 'can_pin_globally': False,
  418. 'can_hide': can_hide_thread(user, thread),
  419. 'can_unhide': can_unhide_thread(user, thread),
  420. 'can_delete': can_delete_thread(user, thread),
  421. 'can_close': category_acl.get('can_close_threads', False),
  422. 'can_move': can_move_thread(user, thread),
  423. 'can_merge': can_merge_thread(user, thread),
  424. 'can_move_posts': category_acl.get('can_move_posts', False),
  425. 'can_merge_posts': category_acl.get('can_merge_posts', False),
  426. 'can_approve': can_approve_thread(user, thread),
  427. 'can_see_reports': category_acl.get('can_see_reports', False),
  428. })
  429. if thread.acl['can_pin'] and category_acl.get('can_pin_threads') == 2:
  430. thread.acl['can_pin_globally'] = True
  431. def add_acl_to_post(user, post):
  432. if post.is_event:
  433. add_acl_to_event(user, post)
  434. else:
  435. add_acl_to_reply(user, post)
  436. def add_acl_to_event(user, event):
  437. can_hide_events = 0
  438. if user.is_authenticated:
  439. category_acl = user.acl_cache['categories'].get(
  440. event.category_id, {
  441. 'can_hide_events': 0,
  442. }
  443. )
  444. can_hide_events = category_acl['can_hide_events']
  445. event.acl.update({
  446. 'can_see_hidden': can_hide_events > 0,
  447. 'can_hide': can_hide_event(user, event),
  448. 'can_delete': can_delete_event(user, event),
  449. })
  450. def add_acl_to_reply(user, post):
  451. category_acl = user.acl_cache['categories'].get(post.category_id, {})
  452. post.acl.update({
  453. 'can_reply': can_reply_thread(user, post.thread),
  454. 'can_edit': can_edit_post(user, post),
  455. 'can_see_hidden': post.is_first_post or category_acl.get('can_hide_posts'),
  456. 'can_unhide': can_unhide_post(user, post),
  457. 'can_hide': can_hide_post(user, post),
  458. 'can_delete': can_delete_post(user, post),
  459. 'can_protect': can_protect_post(user, post),
  460. 'can_approve': can_approve_post(user, post),
  461. 'can_move': can_move_post(user, post),
  462. 'can_report': category_acl.get('can_report_content', False),
  463. 'can_see_reports': category_acl.get('can_see_reports', False),
  464. 'can_see_likes': category_acl.get('can_see_posts_likes', 0),
  465. 'can_like': False,
  466. })
  467. if not post.acl['can_see_hidden']:
  468. post.acl['can_see_hidden'] = post.id == post.thread.first_post_id
  469. if user.is_authenticated and post.acl['can_see_likes']:
  470. post.acl['can_like'] = category_acl.get('can_like_posts', False)
  471. def register_with(registry):
  472. registry.acl_annotator(Category, add_acl_to_category)
  473. registry.acl_annotator(Thread, add_acl_to_thread)
  474. registry.acl_annotator(Post, add_acl_to_post)
  475. def allow_see_thread(user, target):
  476. category_acl = user.acl_cache['categories'].get(
  477. target.category_id, {
  478. 'can_see': False,
  479. 'can_browse': False,
  480. }
  481. )
  482. if not (category_acl['can_see'] and category_acl['can_browse']):
  483. raise Http404()
  484. if target.is_hidden and (user.is_anonymous or not category_acl['can_hide_threads']):
  485. raise Http404()
  486. if user.is_anonymous or user.pk != target.starter_id:
  487. if not category_acl['can_see_all_threads']:
  488. raise Http404()
  489. if target.is_unapproved and not category_acl['can_approve_content']:
  490. raise Http404()
  491. can_see_thread = return_boolean(allow_see_thread)
  492. def allow_start_thread(user, target):
  493. if user.is_anonymous:
  494. raise PermissionDenied(_("You have to sign in to start threads."))
  495. category_acl = user.acl_cache['categories'].get(
  496. target.pk, {
  497. 'can_start_threads': False,
  498. }
  499. )
  500. if not category_acl['can_start_threads']:
  501. raise PermissionDenied(
  502. _("You don't have permission to start new threads in this category.")
  503. )
  504. if target.is_closed and not category_acl['can_close_threads']:
  505. raise PermissionDenied(_("This category is closed. You can't start new threads in it."))
  506. can_start_thread = return_boolean(allow_start_thread)
  507. def allow_reply_thread(user, target):
  508. if user.is_anonymous:
  509. raise PermissionDenied(_("You have to sign in to reply threads."))
  510. category_acl = user.acl_cache['categories'].get(
  511. target.category_id, {
  512. 'can_reply_threads': False,
  513. }
  514. )
  515. if not category_acl['can_reply_threads']:
  516. raise PermissionDenied(_("You can't reply to threads in this category."))
  517. if not category_acl['can_close_threads']:
  518. if target.category.is_closed:
  519. raise PermissionDenied(_("This category is closed. You can't reply to threads in it."))
  520. if target.is_closed:
  521. raise PermissionDenied(_("You can't reply to closed threads in this category."))
  522. can_reply_thread = return_boolean(allow_reply_thread)
  523. def allow_edit_thread(user, target):
  524. if user.is_anonymous:
  525. raise PermissionDenied(_("You have to sign in to edit threads."))
  526. category_acl = user.acl_cache['categories'].get(
  527. target.category_id, {
  528. 'can_edit_threads': False,
  529. }
  530. )
  531. if not category_acl['can_edit_threads']:
  532. raise PermissionDenied(_("You can't edit threads in this category."))
  533. if category_acl['can_edit_threads'] == 1:
  534. if target.starter_id != user.pk:
  535. raise PermissionDenied(_("You can't edit other users threads in this category."))
  536. if not has_time_to_edit_thread(user, target):
  537. message = ungettext(
  538. "You can't edit threads that are older than %(minutes)s minute.",
  539. "You can't edit threads that are older than %(minutes)s minutes.",
  540. category_acl['thread_edit_time']
  541. )
  542. raise PermissionDenied(message % {'minutes': category_acl['thread_edit_time']})
  543. if not category_acl['can_close_threads']:
  544. if target.category.is_closed:
  545. raise PermissionDenied(_("This category is closed. You can't edit threads in it."))
  546. if target.is_closed:
  547. raise PermissionDenied(_("This thread is closed. You can't edit it."))
  548. can_edit_thread = return_boolean(allow_edit_thread)
  549. def allow_pin_thread(user, target):
  550. if user.is_anonymous:
  551. raise PermissionDenied(_("You have to sign in to change threads weights."))
  552. category_acl = user.acl_cache['categories'].get(
  553. target.category_id, {
  554. 'can_pin_threads': 0,
  555. }
  556. )
  557. if not category_acl['can_pin_threads']:
  558. raise PermissionDenied(_("You can't change threads weights in this category."))
  559. if not category_acl['can_close_threads']:
  560. if target.category.is_closed:
  561. raise PermissionDenied(_("This category is closed. You can't change threads weights in it."))
  562. if target.is_closed:
  563. raise PermissionDenied(_("This thread is closed. You can't change its weight."))
  564. can_pin_thread = return_boolean(allow_pin_thread)
  565. def allow_unhide_thread(user, target):
  566. if user.is_anonymous:
  567. raise PermissionDenied(_("You have to sign in to hide threads."))
  568. category_acl = user.acl_cache['categories'].get(
  569. target.category_id, {
  570. 'can_close_threads': False,
  571. }
  572. )
  573. if not category_acl['can_close_threads']:
  574. if target.category.is_closed:
  575. raise PermissionDenied(_("This category is closed. You can't reveal threads in it."))
  576. if target.is_closed:
  577. raise PermissionDenied(_("This thread is closed. You can't reveal it."))
  578. can_unhide_thread = return_boolean(allow_unhide_thread)
  579. def allow_hide_thread(user, target):
  580. if user.is_anonymous:
  581. raise PermissionDenied(_("You have to sign in to hide threads."))
  582. category_acl = user.acl_cache['categories'].get(
  583. target.category_id, {
  584. 'can_hide_threads': 0,
  585. 'can_hide_own_threads': 0,
  586. }
  587. )
  588. if not category_acl['can_hide_threads'] and not category_acl['can_hide_own_threads']:
  589. raise PermissionDenied(_("You can't hide threads in this category."))
  590. if not category_acl['can_hide_threads'] and category_acl['can_hide_own_threads']:
  591. if user.id != target.starter_id:
  592. raise PermissionDenied(_("You can't hide other users theads in this category."))
  593. if not has_time_to_edit_thread(user, target):
  594. message = ungettext(
  595. "You can't hide threads that are older than %(minutes)s minute.",
  596. "You can't hide threads that are older than %(minutes)s minutes.",
  597. category_acl['thread_edit_time'],
  598. )
  599. raise PermissionDenied(message % {'minutes': category_acl['thread_edit_time']})
  600. if not category_acl['can_close_threads']:
  601. if target.category.is_closed:
  602. raise PermissionDenied(_("This category is closed. You can't hide threads in it."))
  603. if target.is_closed:
  604. raise PermissionDenied(_("This thread is closed. You can't hide it."))
  605. can_hide_thread = return_boolean(allow_hide_thread)
  606. def allow_delete_thread(user, target):
  607. if user.is_anonymous:
  608. raise PermissionDenied(_("You have to sign in to delete threads."))
  609. category_acl = user.acl_cache['categories'].get(
  610. target.category_id, {
  611. 'can_hide_threads': 0,
  612. 'can_hide_own_threads': 0,
  613. }
  614. )
  615. if category_acl['can_hide_threads'] != 2 and category_acl['can_hide_own_threads'] != 2:
  616. raise PermissionDenied(_("You can't delete threads in this category."))
  617. if category_acl['can_hide_threads'] != 2 and category_acl['can_hide_own_threads'] == 2:
  618. if user.id != target.starter_id:
  619. raise PermissionDenied(_("You can't delete other users theads in this category."))
  620. if not has_time_to_edit_thread(user, target):
  621. message = ungettext(
  622. "You can't delete threads that are older than %(minutes)s minute.",
  623. "You can't delete threads that are older than %(minutes)s minutes.",
  624. category_acl['thread_edit_time'],
  625. )
  626. raise PermissionDenied(message % {'minutes': category_acl['thread_edit_time']})
  627. if not category_acl['can_close_threads']:
  628. if target.category.is_closed:
  629. raise PermissionDenied(_("This category is closed. You can't delete threads in it."))
  630. if target.is_closed:
  631. raise PermissionDenied(_("This thread is closed. You can't delete it."))
  632. can_delete_thread = return_boolean(allow_delete_thread)
  633. def allow_move_thread(user, target):
  634. if user.is_anonymous:
  635. raise PermissionDenied(_("You have to sign in to move threads."))
  636. category_acl = user.acl_cache['categories'].get(
  637. target.category_id, {
  638. 'can_move_threads': 0,
  639. }
  640. )
  641. if not category_acl['can_move_threads']:
  642. raise PermissionDenied(_("You can't move threads in this category."))
  643. if not category_acl['can_close_threads']:
  644. if target.category.is_closed:
  645. raise PermissionDenied(_("This category is closed. You can't move it's threads."))
  646. if target.is_closed:
  647. raise PermissionDenied(_("This thread is closed. You can't move it."))
  648. can_move_thread = return_boolean(allow_move_thread)
  649. def allow_merge_thread(user, target, otherthread=False):
  650. if user.is_anonymous:
  651. raise PermissionDenied(_("You have to sign in to merge threads."))
  652. category_acl = user.acl_cache['categories'].get(
  653. target.category_id, {
  654. 'can_merge_threads': 0,
  655. }
  656. )
  657. if not category_acl['can_merge_threads']:
  658. if otherthread:
  659. raise PermissionDenied(_("Other thread can't be merged with."))
  660. raise PermissionDenied(_("You can't merge threads in this category."))
  661. if not category_acl['can_close_threads']:
  662. if target.category.is_closed:
  663. if otherthread:
  664. raise PermissionDenied(_("Other thread's category is closed. You can't merge with it."))
  665. raise PermissionDenied(_("This category is closed. You can't merge it's threads."))
  666. if target.is_closed:
  667. if otherthread:
  668. raise PermissionDenied(_("Other thread is closed and can't be merged with."))
  669. raise PermissionDenied(_("This thread is closed. You can't merge it with other threads."))
  670. can_merge_thread = return_boolean(allow_merge_thread)
  671. def allow_approve_thread(user, target):
  672. if user.is_anonymous:
  673. raise PermissionDenied(_("You have to sign in to approve threads."))
  674. category_acl = user.acl_cache['categories'].get(
  675. target.category_id, {
  676. 'can_approve_content': 0,
  677. }
  678. )
  679. if not category_acl['can_approve_content']:
  680. raise PermissionDenied(_("You can't approve threads in this category."))
  681. if not category_acl['can_close_threads']:
  682. if target.category.is_closed:
  683. raise PermissionDenied(_("This category is closed. You can't approve threads in it."))
  684. if target.is_closed:
  685. raise PermissionDenied(_("This thread is closed. You can't approve it."))
  686. can_approve_thread = return_boolean(allow_approve_thread)
  687. def allow_see_post(user, target):
  688. category_acl = user.acl_cache['categories'].get(
  689. target.category_id, {
  690. 'can_approve_content': False,
  691. 'can_hide_events': False,
  692. }
  693. )
  694. if not target.is_event and target.is_unapproved:
  695. if user.is_anonymous:
  696. raise Http404()
  697. if not category_acl['can_approve_content'] and user.id != target.poster_id:
  698. raise Http404()
  699. if target.is_event and target.is_hidden and not category_acl['can_hide_events']:
  700. raise Http404()
  701. can_see_post = return_boolean(allow_see_post)
  702. def allow_edit_post(user, target):
  703. if user.is_anonymous:
  704. raise PermissionDenied(_("You have to sign in to edit posts."))
  705. if target.is_event:
  706. raise PermissionDenied(_("Events can't be edited."))
  707. category_acl = user.acl_cache['categories'].get(target.category_id, {'can_edit_posts': False})
  708. if not category_acl['can_edit_posts']:
  709. raise PermissionDenied(_("You can't edit posts in this category."))
  710. if target.is_hidden and not target.is_first_post and not category_acl['can_hide_posts']:
  711. raise PermissionDenied(_("This post is hidden, you can't edit it."))
  712. if category_acl['can_edit_posts'] == 1:
  713. if target.poster_id != user.pk:
  714. raise PermissionDenied(_("You can't edit other users posts in this category."))
  715. if target.is_protected and not category_acl['can_protect_posts']:
  716. raise PermissionDenied(_("This post is protected. You can't edit it."))
  717. if not has_time_to_edit_post(user, target):
  718. message = ungettext(
  719. "You can't edit posts that are older than %(minutes)s minute.",
  720. "You can't edit posts that are older than %(minutes)s minutes.",
  721. category_acl['post_edit_time'],
  722. )
  723. raise PermissionDenied(message % {'minutes': category_acl['post_edit_time']})
  724. if not category_acl['can_close_threads']:
  725. if target.category.is_closed:
  726. raise PermissionDenied(_("This category is closed. You can't edit posts in it."))
  727. if target.thread.is_closed:
  728. raise PermissionDenied(_("This thread is closed. You can't edit posts in it."))
  729. can_edit_post = return_boolean(allow_edit_post)
  730. def allow_unhide_post(user, target):
  731. if user.is_anonymous:
  732. raise PermissionDenied(_("You have to sign in to reveal posts."))
  733. category_acl = user.acl_cache['categories'].get(
  734. target.category_id, {
  735. 'can_hide_posts': 0,
  736. 'can_hide_own_posts': 0,
  737. }
  738. )
  739. if not category_acl['can_hide_posts']:
  740. if not category_acl['can_hide_own_posts']:
  741. raise PermissionDenied(_("You can't reveal posts in this category."))
  742. if user.id != target.poster_id:
  743. raise PermissionDenied(_("You can't reveal other users posts in this category."))
  744. if target.is_protected and not category_acl['can_protect_posts']:
  745. raise PermissionDenied(_("This post is protected. You can't reveal it."))
  746. if not has_time_to_edit_post(user, target):
  747. message = ungettext(
  748. "You can't reveal posts that are older than %(minutes)s minute.",
  749. "You can't reveal posts that are older than %(minutes)s minutes.",
  750. category_acl['post_edit_time'],
  751. )
  752. raise PermissionDenied(message % {'minutes': category_acl['post_edit_time']})
  753. if target.is_first_post:
  754. raise PermissionDenied(_("You can't reveal thread's first post."))
  755. if not category_acl['can_close_threads']:
  756. if target.category.is_closed:
  757. raise PermissionDenied(_("This category is closed. You can't reveal posts in it."))
  758. if target.thread.is_closed:
  759. raise PermissionDenied(_("This thread is closed. You can't reveal posts in it."))
  760. can_unhide_post = return_boolean(allow_unhide_post)
  761. def allow_hide_post(user, target):
  762. if user.is_anonymous:
  763. raise PermissionDenied(_("You have to sign in to hide posts."))
  764. category_acl = user.acl_cache['categories'].get(
  765. target.category_id, {
  766. 'can_hide_posts': 0,
  767. 'can_hide_own_posts': 0,
  768. }
  769. )
  770. if not category_acl['can_hide_posts']:
  771. if not category_acl['can_hide_own_posts']:
  772. raise PermissionDenied(_("You can't hide posts in this category."))
  773. if user.id != target.poster_id:
  774. raise PermissionDenied(_("You can't hide other users posts in this category."))
  775. if target.is_protected and not category_acl['can_protect_posts']:
  776. raise PermissionDenied(_("This post is protected. You can't hide it."))
  777. if not has_time_to_edit_post(user, target):
  778. message = ungettext(
  779. "You can't hide posts that are older than %(minutes)s minute.",
  780. "You can't hide posts that are older than %(minutes)s minutes.",
  781. category_acl['post_edit_time'],
  782. )
  783. raise PermissionDenied(message % {'minutes': category_acl['post_edit_time']})
  784. if target.is_first_post:
  785. raise PermissionDenied(_("You can't hide thread's first post."))
  786. if not category_acl['can_close_threads']:
  787. if target.category.is_closed:
  788. raise PermissionDenied(_("This category is closed. You can't hide posts in it."))
  789. if target.thread.is_closed:
  790. raise PermissionDenied(_("This thread is closed. You can't hide posts in it."))
  791. can_hide_post = return_boolean(allow_hide_post)
  792. def allow_delete_post(user, target):
  793. if user.is_anonymous:
  794. raise PermissionDenied(_("You have to sign in to delete posts."))
  795. category_acl = user.acl_cache['categories'].get(
  796. target.category_id, {
  797. 'can_hide_posts': 0,
  798. 'can_hide_own_posts': 0,
  799. }
  800. )
  801. if category_acl['can_hide_posts'] != 2:
  802. if category_acl['can_hide_own_posts'] != 2:
  803. raise PermissionDenied(_("You can't delete posts in this category."))
  804. if user.id != target.poster_id:
  805. raise PermissionDenied(_("You can't delete other users posts in this category."))
  806. if target.is_protected and not category_acl['can_protect_posts']:
  807. raise PermissionDenied(_("This post is protected. You can't delete it."))
  808. if not has_time_to_edit_post(user, target):
  809. message = ungettext(
  810. "You can't delete posts that are older than %(minutes)s minute.",
  811. "You can't delete posts that are older than %(minutes)s minutes.",
  812. category_acl['post_edit_time'],
  813. )
  814. raise PermissionDenied(message % {'minutes': category_acl['post_edit_time']})
  815. if target.is_first_post:
  816. raise PermissionDenied(_("You can't delete thread's first post."))
  817. if not category_acl['can_close_threads']:
  818. if target.category.is_closed:
  819. raise PermissionDenied(_("This category is closed. You can't delete posts in it."))
  820. if target.thread.is_closed:
  821. raise PermissionDenied(_("This thread is closed. You can't delete posts in it."))
  822. can_delete_post = return_boolean(allow_delete_post)
  823. def allow_protect_post(user, target):
  824. if user.is_anonymous:
  825. raise PermissionDenied(_("You have to sign in to protect posts."))
  826. category_acl = user.acl_cache['categories'].get(
  827. target.category_id, {'can_protect_posts': False}
  828. )
  829. if not category_acl['can_protect_posts']:
  830. raise PermissionDenied(_("You can't protect posts in this category."))
  831. if not can_edit_post(user, target):
  832. raise PermissionDenied(_("You can't protect posts you can't edit."))
  833. can_protect_post = return_boolean(allow_protect_post)
  834. def allow_approve_post(user, target):
  835. if user.is_anonymous:
  836. raise PermissionDenied(_("You have to sign in to approve posts."))
  837. category_acl = user.acl_cache['categories'].get(
  838. target.category_id, {'can_approve_content': False}
  839. )
  840. if not category_acl['can_approve_content']:
  841. raise PermissionDenied(_("You can't approve posts in this category."))
  842. if target.is_first_post:
  843. raise PermissionDenied(_("You can't approve thread's first post."))
  844. if not target.is_first_post and not category_acl['can_hide_posts'] and target.is_hidden:
  845. raise PermissionDenied(_("You can't approve posts the content you can't see."))
  846. if not category_acl['can_close_threads']:
  847. if target.category.is_closed:
  848. raise PermissionDenied(_("This category is closed. You can't approve posts in it."))
  849. if target.thread.is_closed:
  850. raise PermissionDenied(_("This thread is closed. You can't approve posts in it."))
  851. can_approve_post = return_boolean(allow_approve_post)
  852. def allow_move_post(user, target):
  853. if user.is_anonymous:
  854. raise PermissionDenied(_("You have to sign in to move posts."))
  855. category_acl = user.acl_cache['categories'].get(
  856. target.category_id, {
  857. 'can_move_posts': False,
  858. }
  859. )
  860. if not category_acl['can_move_posts']:
  861. raise PermissionDenied(_("You can't move posts in this category."))
  862. if target.is_event:
  863. raise PermissionDenied(_("Events can't be moved."))
  864. if target.is_first_post:
  865. raise PermissionDenied(_("You can't move thread's first post."))
  866. if not category_acl['can_hide_posts'] and target.is_hidden:
  867. raise PermissionDenied(_("You can't move posts the content you can't see."))
  868. if not category_acl['can_close_threads']:
  869. if target.category.is_closed:
  870. raise PermissionDenied(_("This category is closed. You can't move posts in it."))
  871. if target.thread.is_closed:
  872. raise PermissionDenied(_("This thread is closed. You can't move posts in it."))
  873. can_move_post = return_boolean(allow_move_post)
  874. def allow_unhide_event(user, target):
  875. if user.is_anonymous:
  876. raise PermissionDenied(_("You have to sign in to reveal events."))
  877. category_acl = user.acl_cache['categories'].get(
  878. target.category_id, {
  879. 'can_hide_events': 0,
  880. }
  881. )
  882. if not category_acl['can_hide_events']:
  883. raise PermissionDenied(_("You can't reveal events in this category."))
  884. if not category_acl['can_close_threads']:
  885. if target.category.is_closed:
  886. raise PermissionDenied(_("This category is closed. You can't reveal events in it."))
  887. if target.thread.is_closed:
  888. raise PermissionDenied(_("This thread is closed. You can't reveal events in it."))
  889. can_unhide_event = return_boolean(allow_unhide_event)
  890. def allow_hide_event(user, target):
  891. if user.is_anonymous:
  892. raise PermissionDenied(_("You have to sign in to hide events."))
  893. category_acl = user.acl_cache['categories'].get(
  894. target.category_id, {
  895. 'can_hide_events': 0,
  896. }
  897. )
  898. if not category_acl['can_hide_events']:
  899. raise PermissionDenied(_("You can't hide events in this category."))
  900. if not category_acl['can_close_threads']:
  901. if target.category.is_closed:
  902. raise PermissionDenied(_("This category is closed. You can't hide events in it."))
  903. if target.thread.is_closed:
  904. raise PermissionDenied(_("This thread is closed. You can't hide events in it."))
  905. can_hide_event = return_boolean(allow_hide_event)
  906. def allow_delete_event(user, target):
  907. if user.is_anonymous:
  908. raise PermissionDenied(_("You have to sign in to delete events."))
  909. category_acl = user.acl_cache['categories'].get(
  910. target.category_id, {
  911. 'can_hide_events': 0,
  912. }
  913. )
  914. if category_acl['can_hide_events'] != 2:
  915. raise PermissionDenied(_("You can't delete events in this category."))
  916. if not category_acl['can_close_threads']:
  917. if target.category.is_closed:
  918. raise PermissionDenied(_("This category is closed. You can't delete events in it."))
  919. if target.thread.is_closed:
  920. raise PermissionDenied(_("This thread is closed. You can't delete events in it."))
  921. can_delete_event = return_boolean(allow_delete_event)
  922. def can_change_owned_thread(user, target):
  923. if user.is_anonymous or user.pk != target.starter_id:
  924. return False
  925. if target.category.is_closed or target.is_closed:
  926. return False
  927. return has_time_to_edit_thread(user, target)
  928. def has_time_to_edit_thread(user, target):
  929. edit_time = user.acl_cache['categories'].get(target.category_id, {}).get('thread_edit_time', 0)
  930. if edit_time:
  931. diff = timezone.now() - target.started_on
  932. diff_minutes = int(diff.total_seconds() / 60)
  933. return diff_minutes < edit_time
  934. else:
  935. return True
  936. def has_time_to_edit_post(user, target):
  937. edit_time = user.acl_cache['categories'].get(target.category_id, {}).get('post_edit_time', 0)
  938. if edit_time:
  939. diff = timezone.now() - target.posted_on
  940. diff_minutes = int(diff.total_seconds() / 60)
  941. return diff_minutes < edit_time
  942. else:
  943. return True
  944. def exclude_invisible_threads(user, categories, queryset):
  945. show_all = []
  946. show_accepted_visible = []
  947. show_accepted = []
  948. show_visible = []
  949. show_owned = []
  950. show_owned_visible = []
  951. for category in categories:
  952. add_acl(user, category)
  953. if not (category.acl['can_see'] and category.acl['can_browse']):
  954. continue
  955. can_hide = category.acl['can_hide_threads']
  956. if category.acl['can_see_all_threads']:
  957. can_mod = category.acl['can_approve_content']
  958. if can_mod and can_hide:
  959. show_all.append(category)
  960. elif user.is_authenticated:
  961. if not can_mod and not can_hide:
  962. show_accepted_visible.append(category)
  963. elif not can_mod:
  964. show_accepted.append(category)
  965. elif not can_hide:
  966. show_visible.append(category)
  967. else:
  968. show_accepted_visible.append(category)
  969. elif user.is_authenticated:
  970. if can_hide:
  971. show_owned.append(category)
  972. else:
  973. show_owned_visible.append(category)
  974. conditions = None
  975. if show_all:
  976. conditions = Q(category__in=show_all)
  977. if show_accepted_visible:
  978. if user.is_authenticated:
  979. condition = Q(
  980. Q(starter=user) | Q(is_unapproved=False),
  981. category__in=show_accepted_visible,
  982. is_hidden=False,
  983. )
  984. else:
  985. condition = Q(
  986. category__in=show_accepted_visible,
  987. is_hidden=False,
  988. is_unapproved=False,
  989. )
  990. if conditions:
  991. conditions = conditions | condition
  992. else:
  993. conditions = condition
  994. if show_accepted:
  995. condition = Q(
  996. Q(starter=user) | Q(is_unapproved=False),
  997. category__in=show_accepted,
  998. )
  999. if conditions:
  1000. conditions = conditions | condition
  1001. else:
  1002. conditions = condition
  1003. if show_visible:
  1004. condition = Q(category__in=show_visible, is_hidden=False)
  1005. if conditions:
  1006. conditions = conditions | condition
  1007. else:
  1008. conditions = condition
  1009. if show_owned:
  1010. condition = Q(category__in=show_owned, starter=user)
  1011. if conditions:
  1012. conditions = conditions | condition
  1013. else:
  1014. conditions = condition
  1015. if show_owned_visible:
  1016. condition = Q(
  1017. category__in=show_owned_visible,
  1018. starter=user,
  1019. is_hidden=False,
  1020. )
  1021. if conditions:
  1022. conditions = conditions | condition
  1023. else:
  1024. conditions = condition
  1025. if conditions:
  1026. return queryset.filter(conditions)
  1027. else:
  1028. return Thread.objects.none()
  1029. def exclude_invisible_posts(user, category, queryset):
  1030. if not category.acl['can_approve_content']:
  1031. if user.is_authenticated:
  1032. queryset = queryset.filter(Q(is_unapproved=False) | Q(poster=user))
  1033. else:
  1034. queryset = queryset.exclude(is_unapproved=True)
  1035. if not category.acl['can_hide_events']:
  1036. queryset = queryset.exclude(is_event=True, is_hidden=True)
  1037. return queryset