test_forumthreads_view.py 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345
  1. from django.contrib.auth import get_user_model
  2. from django.core.urlresolvers import reverse
  3. from django.utils.translation import ugettext as _
  4. from misago.acl import add_acl
  5. from misago.acl.testutils import override_acl
  6. from misago.forums.models import Forum
  7. from misago.users.testutils import AuthenticatedUserTestCase
  8. from misago.threads import testutils
  9. from misago.threads.models import Thread, Label
  10. from misago.threads.moderation import ModerationError
  11. from misago.threads.views.generic.forum import (ForumActions, ForumFiltering,
  12. ForumThreads)
  13. class ForumViewHelperTestCase(AuthenticatedUserTestCase):
  14. def setUp(self):
  15. super(ForumViewHelperTestCase, self).setUp()
  16. self.forum = Forum.objects.all_forums().filter(role="forum")[:1][0]
  17. self.forum.labels = []
  18. def override_acl(self, new_acl):
  19. new_acl.update({'can_browse': True})
  20. forums_acl = self.user.acl
  21. forums_acl['visible_forums'].append(self.forum.pk)
  22. forums_acl['forums'][self.forum.pk] = new_acl
  23. override_acl(self.user, forums_acl)
  24. self.forum.acl = {}
  25. add_acl(self.user, self.forum)
  26. class MockRequest(object):
  27. def __init__(self, user, method='GET', POST=None):
  28. self.POST = POST or {}
  29. self.user = user
  30. self.session = {}
  31. self.path = '/forum/fake-forum-1/'
  32. class ActionsTests(ForumViewHelperTestCase):
  33. def setUp(self):
  34. super(ActionsTests, self).setUp()
  35. self.user._misago_real_ip = '127.0.0.1'
  36. Label.objects.clear_cache()
  37. def tearDown(self):
  38. super(ActionsTests, self).tearDown()
  39. Label.objects.clear_cache()
  40. def test_label_actions(self):
  41. """ForumActions initializes list with label actions"""
  42. self.override_acl({
  43. 'can_change_threads_labels': 0,
  44. })
  45. actions = ForumActions(user=self.user, forum=self.forum)
  46. self.assertEqual(actions.available_actions, [])
  47. self.override_acl({
  48. 'can_change_threads_labels': 1,
  49. })
  50. actions = ForumActions(user=self.user, forum=self.forum)
  51. self.assertEqual(actions.available_actions, [])
  52. self.override_acl({
  53. 'can_change_threads_labels': 2,
  54. })
  55. actions = ForumActions(user=self.user, forum=self.forum)
  56. self.assertEqual(actions.available_actions, [])
  57. label = Label.objects.create(name="Mock Label", slug="mock-label")
  58. self.forum.labels = [label]
  59. actions = ForumActions(user=self.user, forum=self.forum)
  60. self.assertEqual(actions.available_actions, [
  61. {
  62. 'action': 'label:%s' % label.slug,
  63. 'icon': 'tag',
  64. 'name': _('Label as "%(label)s"') % {'label': label.name}
  65. },
  66. {
  67. 'action': 'unlabel',
  68. 'icon': 'times-circle',
  69. 'name': _("Remove labels")
  70. },
  71. ])
  72. def test_weight_actions(self):
  73. """ForumActions initializes list with available weights"""
  74. self.override_acl({
  75. 'can_change_threads_weight': 0,
  76. })
  77. actions = ForumActions(user=self.user, forum=self.forum)
  78. self.assertEqual(actions.available_actions, [])
  79. self.override_acl({
  80. 'can_change_threads_weight': 1,
  81. })
  82. actions = ForumActions(user=self.user, forum=self.forum)
  83. self.assertEqual(actions.available_actions, [
  84. {
  85. 'action': 'pin',
  86. 'icon': 'bookmark',
  87. 'name': _("Change to pinned")
  88. },
  89. {
  90. 'action': 'reset',
  91. 'icon': 'circle',
  92. 'name': _("Reset weight")
  93. },
  94. ])
  95. self.override_acl({
  96. 'can_change_threads_weight': 2,
  97. })
  98. actions = ForumActions(user=self.user, forum=self.forum)
  99. self.assertEqual(actions.available_actions, [
  100. {
  101. 'action': 'announce',
  102. 'icon': 'star',
  103. 'name': _("Change to announcements")
  104. },
  105. {
  106. 'action': 'pin',
  107. 'icon': 'bookmark',
  108. 'name': _("Change to pinned")
  109. },
  110. {
  111. 'action': 'reset',
  112. 'icon': 'circle',
  113. 'name': _("Reset weight")
  114. },
  115. ])
  116. def test_approve_action(self):
  117. """ForumActions initializes list with approve threads action"""
  118. self.override_acl({
  119. 'can_review_moderated_content': 0,
  120. })
  121. actions = ForumActions(user=self.user, forum=self.forum)
  122. self.assertEqual(actions.available_actions, [])
  123. self.override_acl({
  124. 'can_review_moderated_content': 1,
  125. })
  126. actions = ForumActions(user=self.user, forum=self.forum)
  127. self.assertEqual(actions.available_actions, [
  128. {
  129. 'action': 'approve',
  130. 'icon': 'check',
  131. 'name': _("Approve threads")
  132. },
  133. ])
  134. def test_move_action(self):
  135. """ForumActions initializes list with move threads action"""
  136. self.override_acl({
  137. 'can_move_threads': 0,
  138. })
  139. actions = ForumActions(user=self.user, forum=self.forum)
  140. self.assertEqual(actions.available_actions, [])
  141. self.override_acl({
  142. 'can_move_threads': 1,
  143. })
  144. actions = ForumActions(user=self.user, forum=self.forum)
  145. self.assertEqual(actions.available_actions, [
  146. {
  147. 'action': 'move',
  148. 'icon': 'arrow-right',
  149. 'name': _("Move threads")
  150. },
  151. ])
  152. def test_merge_action(self):
  153. """ForumActions initializes list with merge threads action"""
  154. self.override_acl({
  155. 'can_merge_threads': 0,
  156. })
  157. actions = ForumActions(user=self.user, forum=self.forum)
  158. self.assertEqual(actions.available_actions, [])
  159. self.override_acl({
  160. 'can_merge_threads': 1,
  161. })
  162. actions = ForumActions(user=self.user, forum=self.forum)
  163. self.assertEqual(actions.available_actions, [
  164. {
  165. 'action': 'merge',
  166. 'icon': 'reply-all',
  167. 'name': _("Merge threads")
  168. },
  169. ])
  170. def test_close_open_actions(self):
  171. """ForumActions initializes list with close and open actions"""
  172. self.override_acl({
  173. 'can_close_threads': 0,
  174. })
  175. actions = ForumActions(user=self.user, forum=self.forum)
  176. self.assertEqual(actions.available_actions, [])
  177. self.override_acl({
  178. 'can_close_threads': 1,
  179. })
  180. actions = ForumActions(user=self.user, forum=self.forum)
  181. self.assertEqual(actions.available_actions, [
  182. {
  183. 'action': 'open',
  184. 'icon': 'unlock-alt',
  185. 'name': _("Open threads")
  186. },
  187. {
  188. 'action': 'close',
  189. 'icon': 'lock',
  190. 'name': _("Close threads")
  191. },
  192. ])
  193. def test_hide_delete_actions(self):
  194. """ForumActions initializes list with hide/delete actions"""
  195. self.override_acl({
  196. 'can_hide_threads': 0,
  197. })
  198. actions = ForumActions(user=self.user, forum=self.forum)
  199. self.assertEqual(actions.available_actions, [])
  200. self.override_acl({
  201. 'can_hide_threads': 1,
  202. })
  203. actions = ForumActions(user=self.user, forum=self.forum)
  204. self.assertEqual(actions.available_actions, [
  205. {
  206. 'action': 'unhide',
  207. 'icon': 'eye',
  208. 'name': _("Unhide threads")
  209. },
  210. {
  211. 'action': 'hide',
  212. 'icon': 'eye-slash',
  213. 'name': _("Hide threads")
  214. },
  215. ])
  216. self.override_acl({
  217. 'can_hide_threads': 2,
  218. })
  219. actions = ForumActions(user=self.user, forum=self.forum)
  220. self.assertEqual(actions.available_actions, [
  221. {
  222. 'action': 'unhide',
  223. 'icon': 'eye',
  224. 'name': _("Unhide threads")
  225. },
  226. {
  227. 'action': 'hide',
  228. 'icon': 'eye-slash',
  229. 'name': _("Hide threads")
  230. },
  231. {
  232. 'action': 'delete',
  233. 'icon': 'times',
  234. 'name': _("Delete threads"),
  235. 'confirmation': _("Are you sure you want to delete selected "
  236. "threads? This action can't be undone.")
  237. },
  238. ])
  239. class ForumFilteringTests(ForumViewHelperTestCase):
  240. def setUp(self):
  241. super(ForumFilteringTests, self).setUp()
  242. Label.objects.clear_cache()
  243. def tearDown(self):
  244. super(ForumFilteringTests, self).tearDown()
  245. Label.objects.clear_cache()
  246. def test_get_available_filters(self):
  247. """get_available_filters returns filters varying on forum acl"""
  248. default_acl = {
  249. 'can_see_all_threads': False,
  250. 'can_see_reports': False,
  251. 'can_review_moderated_content': False,
  252. }
  253. cases = (
  254. ('can_see_all_threads', 'my-threads'),
  255. ('can_see_reports', 'reported'),
  256. ('can_review_moderated_content', 'moderated-threads'),
  257. )
  258. for permission, filter_type in cases:
  259. self.override_acl(default_acl)
  260. filtering = ForumFiltering(self.forum, 'misago:forum', {
  261. 'forum_id': self.forum.id,
  262. 'forum_slug': self.forum.slug,
  263. })
  264. available_filters = filtering.get_available_filters()
  265. available_filters = [f['type'] for f in available_filters]
  266. self.assertNotIn(filter_type, available_filters)
  267. acl = default_acl.copy()
  268. acl[permission] = True
  269. self.override_acl(acl)
  270. filtering = ForumFiltering(self.forum, 'misago:forum', {
  271. 'forum_id': self.forum.id,
  272. 'forum_slug': self.forum.slug,
  273. })
  274. available_filters = filtering.get_available_filters()
  275. available_filters = [f['type'] for f in available_filters]
  276. self.assertIn(filter_type, available_filters)
  277. self.forum.labels = [
  278. Label(name='Label A', slug='label-a'),
  279. Label(name='Label B', slug='label-b'),
  280. Label(name='Label C', slug='label-c'),
  281. Label(name='Label D', slug='label-d'),
  282. ]
  283. self.override_acl(default_acl)
  284. ForumFiltering(self.forum, 'misago:forum', {
  285. 'forum_id': self.forum.id,
  286. 'forum_slug': self.forum.slug,
  287. })
  288. available_filters = filtering.get_available_filters()
  289. available_filters = [f['type'] for f in available_filters]
  290. self.assertEqual(len(available_filters), len(self.forum.labels))
  291. for label in self.forum.labels:
  292. self.assertIn(label.slug, available_filters)
  293. def test_clean_kwargs(self):
  294. """clean_kwargs cleans kwargs"""
  295. self.override_acl({
  296. 'can_see_all_threads': True,
  297. 'can_see_reports': True,
  298. 'can_review_moderated_content': True,
  299. })
  300. filtering = ForumFiltering(self.forum, 'misago:forum', {
  301. 'forum_id': self.forum.id,
  302. 'forum_slug': self.forum.slug,
  303. })
  304. available_filters = filtering.get_available_filters()
  305. available_filters = [f['type'] for f in available_filters]
  306. clean_kwargs = filtering.clean_kwargs({'test': 'kwarg'})
  307. self.assertEqual(clean_kwargs, {'test': 'kwarg'})
  308. clean_kwargs = filtering.clean_kwargs({
  309. 'test': 'kwarg',
  310. 'show': 'everything-hue-hue',
  311. })
  312. self.assertEqual(clean_kwargs, {'test': 'kwarg'})
  313. self.assertFalse(filtering.is_active)
  314. self.assertIsNone(filtering.show)
  315. for filter_type in available_filters:
  316. clean_kwargs = filtering.clean_kwargs({
  317. 'test': 'kwarg',
  318. 'show': filter_type,
  319. })
  320. self.assertEqual(clean_kwargs, {
  321. 'test': 'kwarg',
  322. 'show': filter_type,
  323. })
  324. self.assertTrue(filtering.is_active)
  325. self.assertEqual(filtering.show, filter_type)
  326. def test_current(self):
  327. """current returns dict with current filter"""
  328. self.override_acl({
  329. 'can_see_all_threads': True,
  330. 'can_see_reports': True,
  331. 'can_review_moderated_content': True,
  332. })
  333. test_cases = (
  334. ('my-threads', _("My threads")),
  335. ('reported', _("With reported posts")),
  336. ('moderated-threads', _("Moderated threads")),
  337. ('moderated-posts', _("With moderated posts")),
  338. )
  339. for filter_type, name in test_cases:
  340. filtering = ForumFiltering(self.forum, 'misago:forum', {
  341. 'forum_id': self.forum.id,
  342. 'forum_slug': self.forum.slug,
  343. })
  344. filtering.clean_kwargs({'show': filter_type})
  345. self.assertEqual(filtering.current['name'], name)
  346. def test_choices(self):
  347. """choices returns list of dicts with available filters"""
  348. self.override_acl({
  349. 'can_see_all_threads': True,
  350. 'can_see_reports': True,
  351. 'can_review_moderated_content': True,
  352. })
  353. test_cases = (
  354. 'my-threads',
  355. 'reported',
  356. 'moderated-threads',
  357. 'moderated-posts',
  358. )
  359. for filter_type in test_cases:
  360. filtering = ForumFiltering(self.forum, 'misago:forum', {
  361. 'forum_id': self.forum.id,
  362. 'forum_slug': self.forum.slug,
  363. })
  364. filtering.clean_kwargs({'show': filter_type})
  365. choices = [choice['type'] for choice in filtering.choices()]
  366. self.assertNotIn(filter_type, choices)
  367. class ForumThreadsTests(ForumViewHelperTestCase):
  368. def test_empty_list(self):
  369. """list returns empty list of items"""
  370. self.override_acl({
  371. 'can_see_all_threads': True,
  372. 'can_review_moderated_content': False
  373. })
  374. threads = ForumThreads(self.user, self.forum)
  375. threads_list = threads.list()
  376. self.assertEqual(threads_list, [])
  377. def test_list_exception(self):
  378. """
  379. uninitialized list raises exceptions when
  380. page and paginator attributes are accessed
  381. """
  382. self.override_acl({
  383. 'can_see_all_threads': False,
  384. 'can_review_moderated_content': False
  385. })
  386. threads = ForumThreads(self.user, self.forum)
  387. with self.assertRaises(AttributeError):
  388. threads.page
  389. with self.assertRaises(AttributeError):
  390. threads.paginator
  391. def test_list_with_threads(self):
  392. """list returns list of visible threads"""
  393. test_threads = [
  394. testutils.post_thread(
  395. forum=self.forum,
  396. title="Hello, I am thread",
  397. is_moderated=False,
  398. poster=self.user),
  399. testutils.post_thread(
  400. forum=self.forum,
  401. title="Hello, I am moderated thread",
  402. is_moderated=True,
  403. poster=self.user),
  404. testutils.post_thread(
  405. forum=self.forum,
  406. title="Hello, I am other user thread",
  407. is_moderated=False,
  408. poster="Bob"),
  409. testutils.post_thread(
  410. forum=self.forum,
  411. title="Hello, I am other user moderated thread",
  412. is_moderated=True,
  413. poster="Bob"),
  414. ]
  415. self.override_acl({
  416. 'can_see_all_threads': False,
  417. 'can_review_moderated_content': False
  418. })
  419. threads = ForumThreads(self.user, self.forum)
  420. self.assertEqual(threads.list(), [test_threads[1], test_threads[0]])
  421. self.override_acl({
  422. 'can_see_all_threads': True,
  423. 'can_review_moderated_content': False
  424. })
  425. threads = ForumThreads(self.user, self.forum)
  426. self.assertEqual(threads.list(),
  427. [test_threads[2], test_threads[1], test_threads[0]])
  428. self.override_acl({
  429. 'can_see_all_threads': True,
  430. 'can_review_moderated_content': True
  431. })
  432. threads = ForumThreads(self.user, self.forum)
  433. test_threads.reverse()
  434. self.assertEqual(threads.list(), test_threads)
  435. self.assertTrue(threads.page)
  436. self.assertTrue(threads.paginator)
  437. class ForumThreadsViewTests(AuthenticatedUserTestCase):
  438. def setUp(self):
  439. super(ForumThreadsViewTests, self).setUp()
  440. self.forum = Forum.objects.all_forums().filter(role="forum")[:1][0]
  441. self.link = self.forum.get_absolute_url()
  442. self.forum.delete_content()
  443. Label.objects.clear_cache()
  444. def tearDown(self):
  445. super(ForumThreadsViewTests, self).tearDown()
  446. Label.objects.clear_cache()
  447. def override_acl(self, new_acl, forum=None):
  448. forum = forum or self.forum
  449. forums_acl = self.user.acl
  450. if new_acl['can_see']:
  451. forums_acl['visible_forums'].append(forum.pk)
  452. else:
  453. forums_acl['visible_forums'].remove(forum.pk)
  454. forums_acl['forums'][forum.pk] = new_acl
  455. override_acl(self.user, forums_acl)
  456. def test_cant_see(self):
  457. """has no permission to see forum"""
  458. self.override_acl({
  459. 'can_see': 0,
  460. 'can_browse': 0,
  461. })
  462. response = self.client.get(self.link)
  463. self.assertEqual(response.status_code, 404)
  464. def test_cant_browse(self):
  465. """has no permission to browse forum"""
  466. self.override_acl({
  467. 'can_see': 1,
  468. 'can_browse': 0,
  469. })
  470. response = self.client.get(self.link)
  471. self.assertEqual(response.status_code, 403)
  472. def test_can_browse_empty(self):
  473. """has permission to browse forum, sees empty list"""
  474. self.override_acl({
  475. 'can_see': 1,
  476. 'can_browse': 1,
  477. })
  478. response = self.client.get(self.link)
  479. self.assertEqual(response.status_code, 200)
  480. self.assertIn("No threads", response.content)
  481. def test_owned_threads_visibility(self):
  482. """
  483. can_see_all_threads=0 displays only owned threads to authenticated user
  484. """
  485. test_acl = {
  486. 'can_see': 1,
  487. 'can_browse': 1,
  488. 'can_see_all_threads': 0,
  489. 'can_review_moderated_content': 0,
  490. }
  491. other_moderated_title = "Test other user moderated thread"
  492. testutils.post_thread(
  493. forum=self.forum, title=other_moderated_title, is_moderated=True)
  494. other_title = "Test other user thread"
  495. testutils.post_thread(forum=self.forum, title=other_title)
  496. owned_title = "Test authenticated user thread"
  497. testutils.post_thread(
  498. forum=self.forum,
  499. title=owned_title,
  500. poster=self.user)
  501. owned_moderated_title = "Test authenticated user moderated thread"
  502. testutils.post_thread(
  503. forum=self.forum,
  504. title=owned_moderated_title,
  505. poster=self.user,
  506. is_moderated=True)
  507. self.override_acl(test_acl)
  508. response = self.client.get(self.link)
  509. self.assertEqual(response.status_code, 200)
  510. self.assertNotIn(other_title, response.content)
  511. self.assertNotIn(other_moderated_title, response.content)
  512. self.assertIn(owned_title, response.content)
  513. self.assertIn(owned_moderated_title, response.content)
  514. self.assertNotIn('show-my-threads', response.content)
  515. def test_moderated_threads_visibility(self):
  516. """moderated threads are not rendered to non-moderator, except owned"""
  517. test_acl = {
  518. 'can_see': 1,
  519. 'can_browse': 1,
  520. 'can_see_all_threads': 1,
  521. 'can_review_moderated_content': 0,
  522. }
  523. test_title = "Test moderated thread"
  524. thread = testutils.post_thread(
  525. forum=self.forum, title=test_title, is_moderated=True)
  526. self.override_acl(test_acl)
  527. response = self.client.get(self.link)
  528. self.assertEqual(response.status_code, 200)
  529. self.assertNotIn(test_title, response.content)
  530. test_title = "Test owned moderated thread"
  531. thread = testutils.post_thread(
  532. forum=self.forum,
  533. title=test_title,
  534. is_moderated=True,
  535. poster=self.user)
  536. self.override_acl(test_acl)
  537. response = self.client.get(self.link)
  538. self.assertEqual(response.status_code, 200)
  539. self.assertIn(test_title, response.content)
  540. def test_owned_threads_filter(self):
  541. """owned threads filter is available to authenticated user"""
  542. test_acl = {
  543. 'can_see': 1,
  544. 'can_browse': 1,
  545. 'can_see_all_threads': 1,
  546. 'can_review_moderated_content': 0,
  547. }
  548. other_moderated_title = "Test other user moderated thread"
  549. testutils.post_thread(
  550. forum=self.forum, title=other_moderated_title, is_moderated=True)
  551. other_title = "Test other user thread"
  552. testutils.post_thread(forum=self.forum, title=other_title)
  553. owned_title = "Test authenticated user thread"
  554. testutils.post_thread(
  555. forum=self.forum,
  556. title=owned_title,
  557. poster=self.user)
  558. owned_moderated_title = "Test authenticated user moderated thread"
  559. testutils.post_thread(
  560. forum=self.forum,
  561. title=owned_moderated_title,
  562. poster=self.user,
  563. is_moderated=True)
  564. self.override_acl(test_acl)
  565. response = self.client.get(self.link)
  566. self.assertEqual(response.status_code, 200)
  567. self.assertIn(other_title, response.content)
  568. self.assertNotIn(other_moderated_title, response.content)
  569. self.assertIn(owned_title, response.content)
  570. self.assertIn(owned_moderated_title, response.content)
  571. self.assertIn('show-my-threads', response.content)
  572. self.override_acl(test_acl)
  573. response = self.client.get(reverse('misago:forum', kwargs={
  574. 'forum_id': self.forum.id,
  575. 'forum_slug': self.forum.slug,
  576. 'show': 'my-threads',
  577. }))
  578. self.assertEqual(response.status_code, 200)
  579. self.assertNotIn(other_title, response.content)
  580. self.assertNotIn(other_moderated_title, response.content)
  581. self.assertIn(owned_title, response.content)
  582. self.assertIn(owned_moderated_title, response.content)
  583. def test_moderated_threads_filter(self):
  584. """moderated threads filter is available to moderator"""
  585. test_acl = {
  586. 'can_see': 1,
  587. 'can_browse': 1,
  588. 'can_see_all_threads': 1,
  589. 'can_review_moderated_content': 0,
  590. }
  591. not_moderated_title = "Not moderated thread"
  592. testutils.post_thread(forum=self.forum, title=not_moderated_title)
  593. hidden_title = "Test moderated thread"
  594. testutils.post_thread(
  595. forum=self.forum, title=hidden_title, is_moderated=True)
  596. visible_title = "Test owned moderated thread"
  597. testutils.post_thread(
  598. forum=self.forum,
  599. title=visible_title,
  600. is_moderated=True,
  601. poster=self.user)
  602. self.override_acl(test_acl)
  603. response = self.client.get(self.link)
  604. self.assertEqual(response.status_code, 200)
  605. self.assertIn(not_moderated_title, response.content)
  606. self.assertNotIn(hidden_title, response.content)
  607. self.assertIn(visible_title, response.content)
  608. self.assertNotIn('show-moderated-threads', response.content)
  609. self.assertNotIn('show-moderated-posts', response.content)
  610. self.override_acl(test_acl)
  611. response = self.client.get(reverse('misago:forum', kwargs={
  612. 'forum_id': self.forum.id,
  613. 'forum_slug': self.forum.slug,
  614. 'show': 'moderated-threads',
  615. }))
  616. self.assertEqual(response.status_code, 302)
  617. self.override_acl(test_acl)
  618. response = self.client.get(reverse('misago:forum', kwargs={
  619. 'forum_id': self.forum.id,
  620. 'forum_slug': self.forum.slug,
  621. 'show': 'moderated-posts',
  622. }))
  623. self.assertEqual(response.status_code, 302)
  624. test_acl = {
  625. 'can_see': 1,
  626. 'can_browse': 1,
  627. 'can_see_all_threads': 1,
  628. 'can_review_moderated_content': 1,
  629. }
  630. self.override_acl(test_acl)
  631. response = self.client.get(self.link)
  632. self.assertEqual(response.status_code, 200)
  633. self.assertIn(not_moderated_title, response.content)
  634. self.assertIn(hidden_title, response.content)
  635. self.assertIn(visible_title, response.content)
  636. self.assertIn('show-moderated-threads', response.content)
  637. self.assertIn('show-moderated-posts', response.content)
  638. self.override_acl(test_acl)
  639. response = self.client.get(reverse('misago:forum', kwargs={
  640. 'forum_id': self.forum.id,
  641. 'forum_slug': self.forum.slug,
  642. 'show': 'moderated-threads',
  643. }))
  644. self.assertEqual(response.status_code, 200)
  645. self.assertNotIn(not_moderated_title, response.content)
  646. self.assertIn(hidden_title, response.content)
  647. self.assertIn(visible_title, response.content)
  648. self.assertIn('show-moderated-threads', response.content)
  649. self.assertIn('show-moderated-posts', response.content)
  650. def test_anonymous_request(self):
  651. """view renders to anonymous users"""
  652. anon_title = "Hello Anon!"
  653. testutils.post_thread(forum=self.forum, title=anon_title)
  654. response = self.client.get(self.link)
  655. self.assertEqual(response.status_code, 200)
  656. self.assertIn(anon_title, response.content)
  657. def test_change_threads_labels(self):
  658. """moderation allows for changing threads labels"""
  659. threads = [testutils.post_thread(self.forum) for t in xrange(10)]
  660. test_acl = {
  661. 'can_see': 1,
  662. 'can_browse': 1,
  663. 'can_see_all_threads': 1,
  664. 'can_change_threads_labels': 2
  665. }
  666. labels = [
  667. Label(name='Label A', slug='label-a'),
  668. Label(name='Label B', slug='label-b'),
  669. ]
  670. for label in labels:
  671. label.save()
  672. label.forums.add(self.forum)
  673. self.override_acl(test_acl)
  674. response = self.client.get(self.link)
  675. self.assertEqual(response.status_code, 200)
  676. self.assertIn("Remove labels", response.content)
  677. # label threads with invalid label
  678. self.override_acl(test_acl)
  679. response = self.client.post(self.link, data={
  680. 'action': 'label:mehssiah', 'thread': [t.pk for t in threads]
  681. })
  682. self.assertEqual(response.status_code, 200)
  683. self.assertIn("Requested action is invalid.", response.content)
  684. # label threads
  685. self.override_acl(test_acl)
  686. response = self.client.post(self.link, data={
  687. 'action': 'label:%s' % labels[0].slug,
  688. 'thread': [t.pk for t in threads]
  689. })
  690. self.assertEqual(response.status_code, 302)
  691. self.override_acl(test_acl)
  692. response = self.client.get(self.link)
  693. self.assertEqual(response.status_code, 200)
  694. self.assertIn("10 threads were labeled", response.content)
  695. # label labeled threads
  696. self.override_acl(test_acl)
  697. response = self.client.post(self.link, data={
  698. 'action': 'label:%s' % labels[0].slug,
  699. 'thread': [t.pk for t in threads]
  700. })
  701. self.assertEqual(response.status_code, 302)
  702. self.override_acl(test_acl)
  703. response = self.client.get(self.link)
  704. self.assertEqual(response.status_code, 200)
  705. self.assertIn("No threads were labeled.", response.content)
  706. # relabel labeled threads
  707. self.override_acl(test_acl)
  708. response = self.client.post(self.link, data={
  709. 'action': 'label:%s' % labels[1].slug,
  710. 'thread': [t.pk for t in threads]
  711. })
  712. self.assertEqual(response.status_code, 302)
  713. self.override_acl(test_acl)
  714. response = self.client.get(self.link)
  715. self.assertEqual(response.status_code, 200)
  716. self.assertIn("10 threads were labeled", response.content)
  717. # remove labels from threads
  718. self.override_acl(test_acl)
  719. response = self.client.post(self.link, data={
  720. 'action': 'unlabel', 'thread': [t.pk for t in threads]
  721. })
  722. self.assertEqual(response.status_code, 302)
  723. self.override_acl(test_acl)
  724. response = self.client.get(self.link)
  725. self.assertEqual(response.status_code, 200)
  726. self.assertIn("10 threads labels were removed", response.content)
  727. # remove labels from unlabeled threads
  728. self.override_acl(test_acl)
  729. response = self.client.post(self.link, data={
  730. 'action': 'unlabel', 'thread': [t.pk for t in threads]
  731. })
  732. self.assertEqual(response.status_code, 302)
  733. self.override_acl(test_acl)
  734. response = self.client.get(self.link)
  735. self.assertEqual(response.status_code, 200)
  736. self.assertIn("No threads were unlabeled.", response.content)
  737. def test_moderate_threads_weight(self):
  738. """moderation allows for changing threads weight"""
  739. test_acl = {
  740. 'can_see': 1,
  741. 'can_browse': 1,
  742. 'can_see_all_threads': 1,
  743. 'can_change_threads_weight': 2
  744. }
  745. self.override_acl(test_acl)
  746. response = self.client.get(self.link)
  747. self.assertEqual(response.status_code, 200)
  748. self.assertIn("Change to announcements", response.content)
  749. announcement = testutils.post_thread(self.forum, weight=2)
  750. pinned = testutils.post_thread(self.forum, weight=1)
  751. thread = testutils.post_thread(self.forum, weight=0)
  752. # annouce nothing
  753. self.override_acl(test_acl)
  754. response = self.client.post(self.link, data={'action': 'announce'})
  755. self.assertEqual(response.status_code, 200)
  756. self.assertIn("You have to select at least one thread.",
  757. response.content)
  758. # make announcement announcement
  759. self.override_acl(test_acl)
  760. response = self.client.post(self.link, data={
  761. 'action': 'announce', 'thread': [announcement.pk]
  762. })
  763. self.assertEqual(response.status_code, 302)
  764. self.override_acl(test_acl)
  765. response = self.client.get(self.link)
  766. self.assertEqual(response.status_code, 200)
  767. self.assertIn("No threads were changed to announcements.",
  768. response.content)
  769. # make non-announcements announcements
  770. self.override_acl(test_acl)
  771. response = self.client.post(self.link, data={
  772. 'action': 'announce', 'thread': [pinned.pk, thread.pk]
  773. })
  774. self.assertEqual(response.status_code, 302)
  775. self.override_acl(test_acl)
  776. response = self.client.get(self.link)
  777. self.assertEqual(response.status_code, 200)
  778. self.assertIn("2 threads were changed to announcements.",
  779. response.content)
  780. pinned = Thread.objects.get(pk=pinned.pk)
  781. thread = Thread.objects.get(pk=thread.pk)
  782. self.assertEqual(pinned.weight, 2)
  783. self.assertEqual(thread.weight, 2)
  784. # make threads pinned
  785. self.override_acl(test_acl)
  786. response = self.client.post(self.link, data={
  787. 'action': 'pin', 'thread': [pinned.pk, thread.pk]
  788. })
  789. self.assertEqual(response.status_code, 302)
  790. self.override_acl(test_acl)
  791. response = self.client.get(self.link)
  792. self.assertEqual(response.status_code, 200)
  793. self.assertIn("2 threads were pinned.", response.content)
  794. announcement = Thread.objects.get(pk=announcement.pk)
  795. pinned = Thread.objects.get(pk=pinned.pk)
  796. thread = Thread.objects.get(pk=thread.pk)
  797. self.assertEqual(announcement.weight, 2)
  798. self.assertEqual(pinned.weight, 1)
  799. self.assertEqual(thread.weight, 1)
  800. # reset threads pinned
  801. self.override_acl(test_acl)
  802. response = self.client.post(self.link, data={
  803. 'action': 'reset', 'thread': [thread.pk]
  804. })
  805. self.assertEqual(response.status_code, 302)
  806. self.override_acl(test_acl)
  807. response = self.client.get(self.link)
  808. self.assertEqual(response.status_code, 200)
  809. self.assertIn("1 thread weight was reset.", response.content)
  810. announcement = Thread.objects.get(pk=announcement.pk)
  811. pinned = Thread.objects.get(pk=pinned.pk)
  812. thread = Thread.objects.get(pk=thread.pk)
  813. self.assertEqual(announcement.weight, 2)
  814. self.assertEqual(pinned.weight, 1)
  815. self.assertEqual(thread.weight, 0)
  816. def test_approve_moderated_threads(self):
  817. """moderation allows for aproving moderated threads"""
  818. test_acl = {
  819. 'can_see': 1,
  820. 'can_browse': 1,
  821. 'can_see_all_threads': 1,
  822. 'can_review_moderated_content': 1
  823. }
  824. self.override_acl(test_acl)
  825. response = self.client.get(self.link)
  826. self.assertEqual(response.status_code, 200)
  827. self.assertIn("Approve threads", response.content)
  828. thread = testutils.post_thread(self.forum, is_moderated=False)
  829. moderated_thread = testutils.post_thread(self.forum, is_moderated=True)
  830. # approve approved thread
  831. self.override_acl(test_acl)
  832. response = self.client.post(self.link, data={
  833. 'action': 'approve', 'thread': [thread.pk]
  834. })
  835. self.assertEqual(response.status_code, 302)
  836. self.override_acl(test_acl)
  837. response = self.client.get(self.link)
  838. self.assertEqual(response.status_code, 200)
  839. self.assertIn("No threads were approved.", response.content)
  840. # approve moderated thread
  841. self.override_acl(test_acl)
  842. response = self.client.post(self.link, data={
  843. 'action': 'approve', 'thread': [moderated_thread.pk]
  844. })
  845. self.assertEqual(response.status_code, 302)
  846. self.override_acl(test_acl)
  847. response = self.client.get(self.link)
  848. self.assertEqual(response.status_code, 200)
  849. self.assertIn("1 thread was approved.", response.content)
  850. def test_move_threads(self):
  851. """moderation allows for moving threads"""
  852. new_forum = Forum(name="New Forum",
  853. slug="new-forum",
  854. role="forum")
  855. new_forum.insert_at(self.forum, save=True)
  856. test_acl = {
  857. 'can_see': 1,
  858. 'can_browse': 1,
  859. 'can_see_all_threads': 1,
  860. 'can_move_threads': 1
  861. }
  862. self.override_acl(test_acl)
  863. response = self.client.get(self.link)
  864. self.assertEqual(response.status_code, 200)
  865. self.assertIn("Move threads", response.content)
  866. threads = [testutils.post_thread(self.forum) for t in xrange(10)]
  867. # see move threads form
  868. self.override_acl(test_acl)
  869. response = self.client.post(self.link, data={
  870. 'action': 'move', 'thread': [t.pk for t in threads[:5]]
  871. })
  872. self.assertEqual(response.status_code, 200)
  873. for thread in threads[:5]:
  874. self.assertIn(thread.title, response.content)
  875. # submit form with non-existing forum
  876. self.override_acl(test_acl)
  877. response = self.client.post(self.link, data={
  878. 'action': 'move',
  879. 'thread': [t.pk for t in threads[:5]],
  880. 'submit': '',
  881. 'new_forum': new_forum.pk + 1234
  882. })
  883. self.assertEqual(response.status_code, 200)
  884. self.assertIn("Select valid forum.", response.content)
  885. # attempt move to category
  886. self.override_acl(test_acl)
  887. category = Forum.objects.all_forums().filter(role="category")[:1][0]
  888. response = self.client.post(self.link, data={
  889. 'action': 'move',
  890. 'thread': [t.pk for t in threads[:5]],
  891. 'submit': '',
  892. 'new_forum': category.pk
  893. })
  894. self.assertEqual(response.status_code, 200)
  895. self.assertIn("You can't move threads to category.",
  896. response.content)
  897. # attempt move to redirect
  898. self.override_acl(test_acl)
  899. redirect = Forum.objects.all_forums().filter(role="redirect")[:1][0]
  900. response = self.client.post(self.link, data={
  901. 'action': 'move',
  902. 'thread': [t.pk for t in threads[:5]],
  903. 'submit': '',
  904. 'new_forum': redirect.pk
  905. })
  906. self.assertEqual(response.status_code, 200)
  907. self.assertIn("You can't move threads to redirect.",
  908. response.content)
  909. # move to new_forum
  910. self.override_acl(test_acl)
  911. self.override_acl(test_acl, new_forum)
  912. response = self.client.post(self.link, data={
  913. 'action': 'move',
  914. 'thread': [t.pk for t in threads[:5]],
  915. 'submit': '',
  916. 'new_forum': new_forum.pk
  917. })
  918. self.assertEqual(response.status_code, 302)
  919. self.override_acl(test_acl)
  920. response = self.client.get(self.link)
  921. self.assertEqual(response.status_code, 200)
  922. self.assertIn("5 threads were moved to New Forum.", response.content)
  923. for thread in new_forum.thread_set.all():
  924. self.assertIn(thread, threads[:5])
  925. for thread in self.forum.thread_set.all():
  926. self.assertIn(thread, threads[5:])
  927. def test_merge_threads(self):
  928. """moderation allows for merging threads"""
  929. test_acl = {
  930. 'can_see': 1,
  931. 'can_browse': 1,
  932. 'can_see_all_threads': 1,
  933. 'can_merge_threads': 1
  934. }
  935. self.override_acl(test_acl)
  936. response = self.client.get(self.link)
  937. self.assertEqual(response.status_code, 200)
  938. self.assertIn("Merge threads", response.content)
  939. threads = [testutils.post_thread(self.forum) for t in xrange(10)]
  940. # see merge threads form
  941. self.override_acl(test_acl)
  942. response = self.client.post(self.link, data={
  943. 'action': 'merge', 'thread': [t.pk for t in threads[:5]]
  944. })
  945. self.assertEqual(response.status_code, 200)
  946. # submit form with empty title
  947. self.override_acl(test_acl)
  948. response = self.client.post(self.link, data={
  949. 'action': 'merge',
  950. 'thread': [t.pk for t in threads[:5]],
  951. 'submit': ''
  952. })
  953. self.assertEqual(response.status_code, 200)
  954. self.assertIn("You have to enter merged thread title.",
  955. response.content)
  956. # submit form with one thread selected
  957. self.override_acl(test_acl)
  958. response = self.client.post(self.link, data={
  959. 'action': 'merge',
  960. 'thread': [threads[0].pk],
  961. 'submit': ''
  962. })
  963. self.assertEqual(response.status_code, 200)
  964. self.assertIn("You have to select at least two threads to merge.",
  965. response.content)
  966. # submit form with valid title
  967. self.override_acl(test_acl)
  968. response = self.client.post(self.link, data={
  969. 'action': 'merge',
  970. 'thread': [t.pk for t in threads[:5]],
  971. 'merged_thread_title': 'Merged thread',
  972. 'submit': ''
  973. })
  974. self.assertEqual(response.status_code, 302)
  975. # see if merged thread is there
  976. self.override_acl(test_acl)
  977. response = self.client.get(self.link)
  978. self.assertEqual(response.status_code, 200)
  979. self.assertIn("Merged thread", response.content)
  980. # assert that merged threads are gone
  981. for thread in threads[:5]:
  982. self.assertNotIn(thread.get_absolute_url(), response.content)
  983. # assert that non-merged threads were untouched
  984. for thread in threads[5:]:
  985. self.assertIn(thread.get_absolute_url(), response.content)
  986. def test_close_open_threads(self):
  987. """moderation allows for closing and opening threads"""
  988. test_acl = {
  989. 'can_see': 1,
  990. 'can_browse': 1,
  991. 'can_see_all_threads': 1,
  992. 'can_close_threads': 1
  993. }
  994. self.override_acl(test_acl)
  995. response = self.client.get(self.link)
  996. self.assertEqual(response.status_code, 200)
  997. self.assertIn("Close threads", response.content)
  998. self.assertIn("Open threads", response.content)
  999. threads = [testutils.post_thread(self.forum) for t in xrange(10)]
  1000. # close threads
  1001. self.override_acl(test_acl)
  1002. response = self.client.post(self.link, data={
  1003. 'action': 'close', 'thread': [t.pk for t in threads]
  1004. })
  1005. self.assertEqual(response.status_code, 302)
  1006. self.override_acl(test_acl)
  1007. response = self.client.get(self.link)
  1008. self.assertEqual(response.status_code, 200)
  1009. self.assertIn("10 threads were closed.", response.content)
  1010. # close closed threads
  1011. self.override_acl(test_acl)
  1012. response = self.client.post(self.link, data={
  1013. 'action': 'close', 'thread': [t.pk for t in threads]
  1014. })
  1015. self.assertEqual(response.status_code, 302)
  1016. self.override_acl(test_acl)
  1017. response = self.client.get(self.link)
  1018. self.assertEqual(response.status_code, 200)
  1019. self.assertIn("No threads were closed.", response.content)
  1020. # open closed threads
  1021. self.override_acl(test_acl)
  1022. response = self.client.post(self.link, data={
  1023. 'action': 'open', 'thread': [t.pk for t in threads]
  1024. })
  1025. self.assertEqual(response.status_code, 302)
  1026. self.override_acl(test_acl)
  1027. response = self.client.get(self.link)
  1028. self.assertEqual(response.status_code, 200)
  1029. self.assertIn("10 threads were opened.", response.content)
  1030. # open opened threads
  1031. self.override_acl(test_acl)
  1032. response = self.client.post(self.link, data={
  1033. 'action': 'open', 'thread': [t.pk for t in threads]
  1034. })
  1035. self.assertEqual(response.status_code, 302)
  1036. self.override_acl(test_acl)
  1037. response = self.client.get(self.link)
  1038. self.assertEqual(response.status_code, 200)
  1039. self.assertIn("No threads were opened.", response.content)
  1040. def test_hide_unhide_threads(self):
  1041. """moderation allows for hiding and unhiding threads"""
  1042. test_acl = {
  1043. 'can_see': 1,
  1044. 'can_browse': 1,
  1045. 'can_see_all_threads': 1,
  1046. 'can_hide_threads': 1
  1047. }
  1048. self.override_acl(test_acl)
  1049. response = self.client.get(self.link)
  1050. self.assertEqual(response.status_code, 200)
  1051. self.assertIn("Unhide threads", response.content)
  1052. self.assertIn("Hide threads", response.content)
  1053. threads = [testutils.post_thread(self.forum) for t in xrange(10)]
  1054. # hide threads
  1055. self.override_acl(test_acl)
  1056. response = self.client.post(self.link, data={
  1057. 'action': 'hide', 'thread': [t.pk for t in threads]
  1058. })
  1059. self.assertEqual(response.status_code, 302)
  1060. self.override_acl(test_acl)
  1061. response = self.client.get(self.link)
  1062. self.assertEqual(response.status_code, 200)
  1063. self.assertIn("10 threads were hidden.", response.content)
  1064. # hide hidden threads
  1065. self.override_acl(test_acl)
  1066. response = self.client.post(self.link, data={
  1067. 'action': 'hide', 'thread': [t.pk for t in threads]
  1068. })
  1069. self.assertEqual(response.status_code, 302)
  1070. self.override_acl(test_acl)
  1071. response = self.client.get(self.link)
  1072. self.assertEqual(response.status_code, 200)
  1073. self.assertIn("No threads were hidden.", response.content)
  1074. # unhide hidden threads
  1075. self.override_acl(test_acl)
  1076. response = self.client.post(self.link, data={
  1077. 'action': 'unhide', 'thread': [t.pk for t in threads]
  1078. })
  1079. self.assertEqual(response.status_code, 302)
  1080. self.override_acl(test_acl)
  1081. response = self.client.get(self.link)
  1082. self.assertEqual(response.status_code, 200)
  1083. self.assertIn("10 threads were made visible.", response.content)
  1084. # unhide visible threads
  1085. self.override_acl(test_acl)
  1086. response = self.client.post(self.link, data={
  1087. 'action': 'unhide', 'thread': [t.pk for t in threads]
  1088. })
  1089. self.assertEqual(response.status_code, 302)
  1090. self.override_acl(test_acl)
  1091. response = self.client.get(self.link)
  1092. self.assertEqual(response.status_code, 200)
  1093. self.assertIn("No threads were made visible.", response.content)
  1094. def test_delete_threads(self):
  1095. """moderation allows for deleting threads"""
  1096. threads = [testutils.post_thread(self.forum) for t in xrange(10)]
  1097. self.forum.synchronize()
  1098. self.assertEqual(self.forum.threads, 10)
  1099. test_acl = {
  1100. 'can_see': 1,
  1101. 'can_browse': 1,
  1102. 'can_see_all_threads': 1,
  1103. 'can_hide_threads': 2
  1104. }
  1105. self.override_acl(test_acl)
  1106. response = self.client.get(self.link)
  1107. self.assertEqual(response.status_code, 200)
  1108. self.assertIn("Delete threads", response.content)
  1109. self.override_acl(test_acl)
  1110. response = self.client.post(self.link, data={
  1111. 'action': 'delete', 'thread': [t.pk for t in threads]
  1112. })
  1113. forum = Forum.objects.get(pk=self.forum.pk)
  1114. self.assertEqual(forum.threads, 0)