-test_forumthreads_view.py 46 KB

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