actions.py 13 KB

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