actions.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  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
  11. __all__ = ['ForumActions']
  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': _("Unhide 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. for thread in threads:
  163. moderation.move_thread(request.user, thread, new_forum)
  164. with atomic():
  165. self.forum.synchronize()
  166. self.forum.save()
  167. new_forum.synchronize()
  168. new_forum.save()
  169. changed_threads = len(threads)
  170. message = ungettext(
  171. '%(changed)d thread was moved to "%(forum)s".',
  172. '%(changed)d threads were moved to "%(forum)s".',
  173. changed_threads)
  174. messages.success(request, message % {
  175. 'changed': changed_threads,
  176. 'forum': new_forum.name
  177. })
  178. return None # trigger threads list refresh
  179. if request.is_ajax():
  180. template = self.move_threads_modal_template
  181. else:
  182. template = self.move_threads_full_template
  183. return render(request, template, {
  184. 'form': form,
  185. 'forum': self.forum,
  186. 'path': get_forum_path(self.forum),
  187. 'threads': threads
  188. })
  189. merge_threads_full_template = 'misago/threads/merge/full.html'
  190. merge_threads_modal_template = 'misago/threads/merge/modal.html'
  191. def action_merge(self, request, threads):
  192. if len(threads) == 1:
  193. message = _("You have to select at least two threads to merge.")
  194. raise moderation.ModerationError(message)
  195. form = MergeThreadsForm()
  196. if request.method == "POST" and 'submit' in request.POST:
  197. form = MergeThreadsForm(request.POST)
  198. if form.is_valid():
  199. thread_title = form.cleaned_data['merged_thread_title']
  200. with atomic():
  201. merged_thread = Thread()
  202. merged_thread.forum = self.forum
  203. merged_thread.set_title(
  204. form.cleaned_data['merged_thread_title'])
  205. merged_thread.starter_name = "-"
  206. merged_thread.starter_slug = "-"
  207. merged_thread.last_poster_name = "-"
  208. merged_thread.last_poster_slug = "-"
  209. merged_thread.started_on = timezone.now()
  210. merged_thread.last_post_on = timezone.now()
  211. merged_thread.is_pinned = max(t.is_pinned for t in threads)
  212. merged_thread.is_closed = max(t.is_closed for t in threads)
  213. merged_thread.save()
  214. for thread in threads:
  215. moderation.merge_thread(
  216. request.user, merged_thread, thread)
  217. merged_thread.synchronize()
  218. merged_thread.save()
  219. with atomic():
  220. self.forum.synchronize()
  221. self.forum.save()
  222. changed_threads = len(threads)
  223. message = ungettext(
  224. '%(changed)d thread was merged into "%(thread)s".',
  225. '%(changed)d threads were merged into "%(thread)s".',
  226. changed_threads)
  227. messages.success(request, message % {
  228. 'changed': changed_threads,
  229. 'thread': merged_thread.title
  230. })
  231. return None # trigger threads list refresh
  232. if request.is_ajax():
  233. template = self.merge_threads_modal_template
  234. else:
  235. template = self.merge_threads_full_template
  236. return render(request, template, {
  237. 'form': form,
  238. 'forum': self.forum,
  239. 'path': get_forum_path(self.forum),
  240. 'threads': threads
  241. })
  242. def action_close(self, request, threads):
  243. changed_threads = 0
  244. for thread in threads:
  245. if moderation.close_thread(request.user, thread):
  246. changed_threads += 1
  247. if changed_threads:
  248. message = ungettext(
  249. '%(changed)d thread was closed.',
  250. '%(changed)d threads were closed.',
  251. changed_threads)
  252. messages.success(request, message % {'changed': changed_threads})
  253. else:
  254. message = _("No threads were closed.")
  255. messages.info(request, message)
  256. def action_open(self, request, threads):
  257. changed_threads = 0
  258. for thread in threads:
  259. if moderation.open_thread(request.user, thread):
  260. changed_threads += 1
  261. if changed_threads:
  262. message = ungettext(
  263. '%(changed)d thread was opened.',
  264. '%(changed)d threads were opened.',
  265. changed_threads)
  266. messages.success(request, message % {'changed': changed_threads})
  267. else:
  268. message = _("No threads were opened.")
  269. messages.info(request, message)
  270. def action_unhide(self, request, threads):
  271. changed_threads = 0
  272. for thread in threads:
  273. if moderation.unhide_thread(request.user, thread):
  274. changed_threads += 1
  275. if changed_threads:
  276. with atomic():
  277. self.forum.synchronize()
  278. self.forum.save()
  279. message = ungettext(
  280. '%(changed)d thread was made visible.',
  281. '%(changed)d threads were made visible.',
  282. changed_threads)
  283. messages.success(request, message % {'changed': changed_threads})
  284. else:
  285. message = _("No threads were made visible.")
  286. messages.info(request, message)
  287. def action_hide(self, request, threads):
  288. changed_threads = 0
  289. for thread in threads:
  290. if moderation.hide_thread(request.user, thread):
  291. changed_threads += 1
  292. if changed_threads:
  293. with atomic():
  294. self.forum.synchronize()
  295. self.forum.save()
  296. message = ungettext(
  297. '%(changed)d thread was hidden.',
  298. '%(changed)d threads were hidden.',
  299. changed_threads)
  300. messages.success(request, message % {'changed': changed_threads})
  301. else:
  302. message = _("No threads were hidden.")
  303. messages.info(request, message)
  304. def action_delete(self, request, threads):
  305. changed_threads = 0
  306. for thread in threads:
  307. if moderation.delete_thread(request.user, thread):
  308. changed_threads += 1
  309. if changed_threads:
  310. with atomic():
  311. self.forum.synchronize()
  312. self.forum.save()
  313. message = ungettext(
  314. '%(changed)d thread was deleted.',
  315. '%(changed)d threads were deleted.',
  316. changed_threads)
  317. messages.success(request, message % {'changed': changed_threads})
  318. else:
  319. message = _("No threads were deleted.")
  320. messages.info(request, message)