actions.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. from django.contrib import messages
  2. from django.db.transaction import atomic
  3. from django.shortcuts import render
  4. from django.utils import timezone
  5. from django.utils.translation import ugettext_lazy, ugettext as _, ungettext
  6. from misago.forums.lists import get_forum_path
  7. from misago.threads import moderation
  8. from misago.threads.forms.moderation import MergeThreadsForm, MoveThreadsForm
  9. from misago.threads.models import Thread
  10. from misago.threads.views.generic.threads import Actions, ReloadAfterDelete
  11. __all__ = ['ForumActions', 'ReloadAfterDelete']
  12. class ForumActions(Actions):
  13. def get_available_actions(self, kwargs):
  14. self.forum = kwargs['forum']
  15. actions = []
  16. if self.forum.acl['can_change_threads_labels'] == 2:
  17. for label in self.forum.labels:
  18. actions.append({
  19. 'action': 'label:%s' % label.slug,
  20. 'icon': 'tag',
  21. 'name': _('Label as "%(label)s"') % {'label': label.name}
  22. })
  23. if self.forum.labels:
  24. actions.append({
  25. 'action': 'unlabel',
  26. 'icon': 'times-circle',
  27. 'name': _("Remove labels")
  28. })
  29. if self.forum.acl['can_pin_threads']:
  30. actions.append({
  31. 'action': 'pin',
  32. 'icon': 'star',
  33. 'name': _("Pin threads")
  34. })
  35. actions.append({
  36. 'action': 'unpin',
  37. 'icon': 'circle',
  38. 'name': _("Unpin threads")
  39. })
  40. if self.forum.acl['can_review_moderated_content']:
  41. actions.append({
  42. 'action': 'approve',
  43. 'icon': 'check',
  44. 'name': _("Approve threads")
  45. })
  46. if self.forum.acl['can_move_threads']:
  47. actions.append({
  48. 'action': 'move',
  49. 'icon': 'arrow-right',
  50. 'name': _("Move threads")
  51. })
  52. if self.forum.acl['can_merge_threads']:
  53. actions.append({
  54. 'action': 'merge',
  55. 'icon': 'reply-all',
  56. 'name': _("Merge threads")
  57. })
  58. if self.forum.acl['can_close_threads']:
  59. actions.append({
  60. 'action': 'open',
  61. 'icon': 'unlock-alt',
  62. 'name': _("Open threads")
  63. })
  64. actions.append({
  65. 'action': 'close',
  66. 'icon': 'lock',
  67. 'name': _("Close threads")
  68. })
  69. if self.forum.acl['can_hide_threads']:
  70. actions.append({
  71. 'action': 'unhide',
  72. 'icon': 'eye',
  73. 'name': _("Reveal threads")
  74. })
  75. actions.append({
  76. 'action': 'hide',
  77. 'icon': 'eye-slash',
  78. 'name': _("Hide threads")
  79. })
  80. if self.forum.acl['can_hide_threads'] == 2:
  81. actions.append({
  82. 'action': 'delete',
  83. 'icon': 'times',
  84. 'name': _("Delete threads"),
  85. 'confirmation': _("Are you sure you want to delete selected "
  86. "threads? This action can't be undone.")
  87. })
  88. return actions
  89. def action_label(self, request, threads, label_slug):
  90. for label in self.forum.labels:
  91. if label.slug == label_slug:
  92. break
  93. else:
  94. raise moderation.ModerationError(self.invalid_action_message)
  95. changed_threads = 0
  96. for thread in threads:
  97. if moderation.label_thread(request.user, thread, label):
  98. changed_threads += 1
  99. if changed_threads:
  100. message = ungettext(
  101. '%(changed)d thread was labeled "%(label)s".',
  102. '%(changed)d threads were labeled "%(label)s".',
  103. changed_threads)
  104. messages.success(request, message % {
  105. 'changed': changed_threads,
  106. 'label': label.name
  107. })
  108. else:
  109. message = _("No threads were labeled.")
  110. messages.info(request, message)
  111. def action_unlabel(self, request, threads):
  112. changed_threads = 0
  113. for thread in threads:
  114. if moderation.unlabel_thread(request.user, thread):
  115. changed_threads += 1
  116. if changed_threads:
  117. message = ungettext(
  118. '%(changed)d thread label was removed.',
  119. '%(changed)d threads labels were removed.',
  120. changed_threads)
  121. messages.success(request, message % {'changed': changed_threads})
  122. else:
  123. message = _("No threads were unlabeled.")
  124. messages.info(request, message)
  125. def action_pin(self, request, threads):
  126. changed_threads = 0
  127. for thread in threads:
  128. if moderation.pin_thread(request.user, thread):
  129. changed_threads += 1
  130. if changed_threads:
  131. message = ungettext(
  132. '%(changed)d thread was pinned.',
  133. '%(changed)d threads were pinned.',
  134. changed_threads)
  135. messages.success(request, message % {'changed': changed_threads})
  136. else:
  137. message = _("No threads were pinned.")
  138. messages.info(request, message)
  139. def action_unpin(self, request, threads):
  140. changed_threads = 0
  141. for thread in threads:
  142. if moderation.unpin_thread(request.user, thread):
  143. changed_threads += 1
  144. if changed_threads:
  145. message = ungettext(
  146. '%(changed)d thread was unpinned.',
  147. '%(changed)d threads were unpinned.',
  148. changed_threads)
  149. messages.success(request, message % {'changed': changed_threads})
  150. else:
  151. message = _("No threads were unpinned.")
  152. messages.info(request, message)
  153. move_threads_full_template = 'misago/threads/move/full.html'
  154. move_threads_modal_template = 'misago/threads/move/modal.html'
  155. def action_move(self, request, threads):
  156. form = MoveThreadsForm(acl=request.user.acl, forum=self.forum)
  157. if request.method == "POST" and 'submit' in request.POST:
  158. form = MoveThreadsForm(
  159. request.POST, acl=request.user.acl, forum=self.forum)
  160. if form.is_valid():
  161. new_forum = form.cleaned_data['new_forum']
  162. with atomic():
  163. for thread in threads:
  164. moderation.move_thread(request.user, thread, new_forum)
  165. self.forum.lock()
  166. new_forum.lock()
  167. self.forum.synchronize()
  168. self.forum.save()
  169. new_forum.synchronize()
  170. new_forum.save()
  171. changed_threads = len(threads)
  172. message = ungettext(
  173. '%(changed)d thread was moved to "%(forum)s".',
  174. '%(changed)d threads were moved to "%(forum)s".',
  175. changed_threads)
  176. messages.success(request, message % {
  177. 'changed': changed_threads,
  178. 'forum': new_forum.name
  179. })
  180. return None # trigger threads list refresh
  181. if request.is_ajax():
  182. template = self.move_threads_modal_template
  183. else:
  184. template = self.move_threads_full_template
  185. return render(request, template, {
  186. 'form': form,
  187. 'forum': self.forum,
  188. 'path': get_forum_path(self.forum),
  189. 'threads': threads
  190. })
  191. merge_threads_full_template = 'misago/threads/merge/full.html'
  192. merge_threads_modal_template = 'misago/threads/merge/modal.html'
  193. def action_merge(self, request, threads):
  194. if len(threads) == 1:
  195. message = _("You have to select at least two threads to merge.")
  196. raise moderation.ModerationError(message)
  197. form = MergeThreadsForm()
  198. if request.method == "POST" and 'submit' in request.POST:
  199. form = MergeThreadsForm(request.POST)
  200. if form.is_valid():
  201. thread_title = form.cleaned_data['merged_thread_title']
  202. with atomic():
  203. merged_thread = Thread()
  204. merged_thread.forum = self.forum
  205. merged_thread.set_title(
  206. form.cleaned_data['merged_thread_title'])
  207. merged_thread.starter_name = "-"
  208. merged_thread.starter_slug = "-"
  209. merged_thread.last_poster_name = "-"
  210. merged_thread.last_poster_slug = "-"
  211. merged_thread.started_on = timezone.now()
  212. merged_thread.last_post_on = timezone.now()
  213. merged_thread.is_pinned = max(t.is_pinned for t in threads)
  214. merged_thread.is_closed = max(t.is_closed for t in threads)
  215. merged_thread.save()
  216. for thread in threads:
  217. moderation.merge_thread(
  218. request.user, merged_thread, thread)
  219. merged_thread.synchronize()
  220. merged_thread.save()
  221. self.forum.lock()
  222. self.forum.synchronize()
  223. self.forum.save()
  224. changed_threads = len(threads)
  225. message = ungettext(
  226. '%(changed)d thread was merged into "%(thread)s".',
  227. '%(changed)d threads were merged into "%(thread)s".',
  228. changed_threads)
  229. messages.success(request, message % {
  230. 'changed': changed_threads,
  231. 'thread': merged_thread.title
  232. })
  233. return None # trigger threads list refresh
  234. if request.is_ajax():
  235. template = self.merge_threads_modal_template
  236. else:
  237. template = self.merge_threads_full_template
  238. return render(request, template, {
  239. 'form': form,
  240. 'forum': self.forum,
  241. 'path': get_forum_path(self.forum),
  242. 'threads': threads
  243. })
  244. def action_close(self, request, threads):
  245. changed_threads = 0
  246. for thread in threads:
  247. if moderation.close_thread(request.user, thread):
  248. changed_threads += 1
  249. if changed_threads:
  250. message = ungettext(
  251. '%(changed)d thread was closed.',
  252. '%(changed)d threads were closed.',
  253. changed_threads)
  254. messages.success(request, message % {'changed': changed_threads})
  255. else:
  256. message = _("No threads were closed.")
  257. messages.info(request, message)
  258. def action_open(self, request, threads):
  259. changed_threads = 0
  260. for thread in threads:
  261. if moderation.open_thread(request.user, thread):
  262. changed_threads += 1
  263. if changed_threads:
  264. message = ungettext(
  265. '%(changed)d thread was opened.',
  266. '%(changed)d threads were opened.',
  267. changed_threads)
  268. messages.success(request, message % {'changed': changed_threads})
  269. else:
  270. message = _("No threads were opened.")
  271. messages.info(request, message)
  272. def action_unhide(self, request, threads):
  273. changed_threads = 0
  274. for thread in threads:
  275. if moderation.unhide_thread(request.user, thread):
  276. changed_threads += 1
  277. if changed_threads:
  278. with atomic():
  279. self.forum.lock()
  280. self.forum.synchronize()
  281. self.forum.save()
  282. message = ungettext(
  283. '%(changed)d thread was made visible.',
  284. '%(changed)d threads were made visible.',
  285. changed_threads)
  286. messages.success(request, message % {'changed': changed_threads})
  287. else:
  288. message = _("No threads were made visible.")
  289. messages.info(request, message)
  290. def action_hide(self, request, threads):
  291. changed_threads = 0
  292. for thread in threads:
  293. if moderation.hide_thread(request.user, thread):
  294. changed_threads += 1
  295. if changed_threads:
  296. with atomic():
  297. self.forum.lock()
  298. self.forum.synchronize()
  299. self.forum.save()
  300. if changed_threads:
  301. message = ungettext(
  302. '%(changed)d thread was hidden.',
  303. '%(changed)d threads were hidden.',
  304. changed_threads)
  305. messages.success(request, message % {'changed': changed_threads})
  306. else:
  307. message = _("No threads were hidden.")
  308. messages.info(request, message)
  309. def action_delete(self, request, threads):
  310. changed_threads = 0
  311. for thread in threads:
  312. if moderation.delete_thread(request.user, thread):
  313. changed_threads += 1
  314. if changed_threads:
  315. with atomic():
  316. self.forum.lock()
  317. self.forum.synchronize()
  318. self.forum.save()
  319. if changed_threads:
  320. message = ungettext(
  321. '%(changed)d thread was deleted.',
  322. '%(changed)d threads were deleted.',
  323. changed_threads)
  324. messages.success(request, message % {'changed': changed_threads})
  325. return ReloadAfterDelete()
  326. else:
  327. message = _("No threads were deleted.")
  328. messages.info(request, message)