Browse Source

wip forum -> category switch

Rafał Pitoń 9 years ago
parent
commit
4d35854110
105 changed files with 1457 additions and 1425 deletions
  1. 6 5
      misago/conf/defaults.py
  2. 1 1
      misago/core/tests/test_common_middleware_redirect.py
  3. 11 9
      misago/faker/management/commands/createfakethreads.py
  4. 108 0
      misago/readtracker/categoriestracker.py
  5. 2 3
      misago/readtracker/dates.py
  6. 0 97
      misago/readtracker/forumstracker.py
  7. 3 3
      misago/readtracker/migrations/0001_initial.py
  8. 3 3
      misago/readtracker/models.py
  9. 5 5
      misago/readtracker/signals.py
  10. 8 6
      misago/readtracker/tests/test_dates.py
  11. 92 89
      misago/readtracker/tests/test_readtracker.py
  12. 25 23
      misago/readtracker/tests/test_views.py
  13. 30 26
      misago/readtracker/threadstracker.py
  14. 1 1
      misago/readtracker/urls.py
  15. 7 7
      misago/readtracker/views.py
  16. 6 7
      misago/threads/admin.py
  17. 3 2
      misago/threads/counts.py
  18. 2 2
      misago/threads/events.py
  19. 5 5
      misago/threads/forms/admin.py
  20. 28 27
      misago/threads/forms/moderation.py
  21. 10 10
      misago/threads/migrations/0001_initial.py
  22. 2 2
      misago/threads/models/event.py
  23. 7 7
      misago/threads/models/label.py
  24. 3 3
      misago/threads/models/post.py
  25. 1 1
      misago/threads/models/report.py
  26. 7 7
      misago/threads/models/thread.py
  27. 6 6
      misago/threads/moderation/threads.py
  28. 6 6
      misago/threads/permissions/privatethreads.py
  29. 153 150
      misago/threads/permissions/threads.py
  30. 1 1
      misago/threads/posting/__init__.py
  31. 3 4
      misago/threads/posting/savechanges.py
  32. 1 1
      misago/threads/posting/threadclose.py
  33. 4 3
      misago/threads/posting/threadlabel.py
  34. 1 1
      misago/threads/posting/threadpin.py
  35. 6 7
      misago/threads/posting/updatestats.py
  36. 1 1
      misago/threads/reports.py
  37. 28 27
      misago/threads/signals.py
  38. 59 58
      misago/threads/tests/-test_editpost_view.py
  39. 156 155
      misago/threads/tests/-test_forumthreads_view.py
  40. 5 5
      misago/threads/tests/-test_goto_views.py
  41. 10 10
      misago/threads/tests/-test_gotolists_views.py
  42. 12 12
      misago/threads/tests/-test_moderatedcontent_view.py
  43. 5 5
      misago/threads/tests/-test_newthreads_view.py
  44. 10 10
      misago/threads/tests/-test_post_views.py
  45. 3 3
      misago/threads/tests/-test_privatethread_view.py
  46. 7 7
      misago/threads/tests/-test_privatethreads_view.py
  47. 38 38
      misago/threads/tests/-test_replythread_view.py
  48. 38 38
      misago/threads/tests/-test_startthread_view.py
  49. 34 32
      misago/threads/tests/-test_thread_view.py
  50. 3 3
      misago/threads/tests/-test_threadparticipants_views.py
  51. 3 3
      misago/threads/tests/-test_unreadthreads_view.py
  52. 12 12
      misago/threads/tests/test_counters.py
  53. 4 4
      misago/threads/tests/test_event_model.py
  54. 4 4
      misago/threads/tests/test_events.py
  55. 8 8
      misago/threads/tests/test_events_view.py
  56. 5 5
      misago/threads/tests/test_goto.py
  57. 12 14
      misago/threads/tests/test_label_model.py
  58. 23 23
      misago/threads/tests/test_labelsadmin_views.py
  59. 5 6
      misago/threads/tests/test_participants.py
  60. 10 10
      misago/threads/tests/test_post_model.py
  61. 3 3
      misago/threads/tests/test_posts_moderation.py
  62. 4 4
      misago/threads/tests/test_synchronizethreads.py
  63. 18 18
      misago/threads/tests/test_thread_model.py
  64. 4 4
      misago/threads/tests/test_threadparticipant_model.py
  65. 13 13
      misago/threads/tests/test_threads_moderation.py
  66. 3 3
      misago/threads/tests/test_threadslist_view.py
  67. 7 7
      misago/threads/testutils.py
  68. 2 2
      misago/threads/threadtypes/__init__.py
  69. 9 9
      misago/threads/threadtypes/forumthread.py
  70. 4 4
      misago/threads/threadtypes/privatethread.py
  71. 1 1
      misago/threads/threadtypes/report.py
  72. 13 13
      misago/threads/urls/threads.py
  73. 1 1
      misago/threads/views/generic/__init__.py
  74. 38 37
      misago/threads/views/generic/base.py
  75. 6 6
      misago/threads/views/generic/events.py
  76. 4 4
      misago/threads/views/generic/forum/__init__.py
  77. 43 43
      misago/threads/views/generic/forum/actions.py
  78. 9 9
      misago/threads/views/generic/forum/filtering.py
  79. 10 10
      misago/threads/views/generic/forum/threads.py
  80. 18 18
      misago/threads/views/generic/forum/view.py
  81. 10 9
      misago/threads/views/generic/goto.py
  82. 5 5
      misago/threads/views/generic/gotopostslist.py
  83. 10 10
      misago/threads/views/generic/post.py
  84. 19 19
      misago/threads/views/generic/posting.py
  85. 24 24
      misago/threads/views/generic/thread/postsactions.py
  86. 29 29
      misago/threads/views/generic/thread/threadactions.py
  87. 13 13
      misago/threads/views/generic/thread/view.py
  88. 4 4
      misago/threads/views/labelsadmin.py
  89. 1 1
      misago/threads/views/moderatedcontent.py
  90. 1 1
      misago/threads/views/newthreads.py
  91. 16 16
      misago/threads/views/privatethreads.py
  92. 1 1
      misago/threads/views/threads.py
  93. 1 1
      misago/threads/views/unreadthreads.py
  94. 1 3
      misago/urls.py
  95. 5 3
      misago/users/activepostersranking.py
  96. 4 4
      misago/users/api/auth.py
  97. 2 2
      misago/users/api/userendpoints/changeemail.py
  98. 2 2
      misago/users/api/userendpoints/changepassword.py
  99. 2 2
      misago/users/api/userendpoints/create.py
  100. 0 1
      misago/users/api/userendpoints/list.py
  101. 10 8
      misago/users/api/users.py
  102. 7 7
      misago/users/tests/test_activepostersranking.py
  103. 5 5
      misago/users/tests/test_useradmin_views.py
  104. 8 8
      misago/users/tests/test_users_api.py
  105. 15 15
      misago/users/views/admin/users.py

+ 6 - 5
misago/conf/defaults.py

@@ -105,7 +105,7 @@ INSTALLED_APPS = (
     'misago.markup',
     'misago.markup',
     'misago.notifications',
     'misago.notifications',
     'misago.legal',
     'misago.legal',
-    'misago.forums',
+    'misago.categories',
     'misago.threads',
     'misago.threads',
     'misago.readtracker',
     'misago.readtracker',
     'misago.faker',
     'misago.faker',
@@ -159,7 +159,7 @@ MISAGO_ACL_EXTENSIONS = (
     'misago.users.permissions.warnings',
     'misago.users.permissions.warnings',
     'misago.users.permissions.moderation',
     'misago.users.permissions.moderation',
     'misago.users.permissions.delete',
     'misago.users.permissions.delete',
-    'misago.forums.permissions',
+    'misago.categories.permissions',
     'misago.threads.permissions.threads',
     'misago.threads.permissions.threads',
     'misago.threads.permissions.privatethreads',
     'misago.threads.permissions.privatethreads',
 )
 )
@@ -182,9 +182,10 @@ MISAGO_POSTING_MIDDLEWARES = (
 
 
 MISAGO_THREAD_TYPES = (
 MISAGO_THREAD_TYPES = (
     # category and redirect types
     # category and redirect types
-    'misago.forums.forumtypes.RootCategory',
+    'misago.categories.forumtypes.RootCategory',
-    'misago.forums.forumtypes.Category',
+    'misago.categories.forumtypes.Category',
-    'misago.forums.forumtypes.Redirect',
+    'misago.categories.forumtypes.Redirect',
+
     # real thread types
     # real thread types
     'misago.threads.threadtypes.forumthread.ForumThread',
     'misago.threads.threadtypes.forumthread.ForumThread',
     'misago.threads.threadtypes.privatethread.PrivateThread',
     'misago.threads.threadtypes.privatethread.PrivateThread',

+ 1 - 1
misago/core/tests/test_common_middleware_redirect.py

@@ -1,7 +1,7 @@
 from misago.users.testutils import AuthenticatedUserTestCase
 from misago.users.testutils import AuthenticatedUserTestCase
 
 
 
 
-class ChangeForumOptionsTests(AuthenticatedUserTestCase):
+class CommonMiddlewareRedirectTests(AuthenticatedUserTestCase):
     def test_slashless_redirect(self):
     def test_slashless_redirect(self):
         """
         """
         Regression test for https://github.com/rafalp/Misago/issues/450
         Regression test for https://github.com/rafalp/Misago/issues/450

+ 11 - 9
misago/faker/management/commands/createfakethreads.py

@@ -10,7 +10,7 @@ from django.template.defaultfilters import linebreaks_filter
 from django.utils import timezone
 from django.utils import timezone
 
 
 from misago.core.management.progressbar import show_progress
 from misago.core.management.progressbar import show_progress
-from misago.forums.models import Forum
+from misago.categories.models import Category
 from misago.threads.checksums import update_post_checksum
 from misago.threads.checksums import update_post_checksum
 from misago.threads.models import Thread, Post
 from misago.threads.models import Thread, Post
 
 
@@ -27,7 +27,9 @@ class Command(BaseCommand):
             self.stderr.write("\nOptional argument should be integer.")
             self.stderr.write("\nOptional argument should be integer.")
             sys.exit(1)
             sys.exit(1)
 
 
-        forums = [f for f in Forum.objects.all_forums().filter(role='forum')]
+        categories = []
+        for category in Category.objects.all_categories().filter(role='forum'):
+            categories.append(category)
 
 
         fake = Factory.create()
         fake = Factory.create()
 
 
@@ -44,7 +46,7 @@ class Command(BaseCommand):
         for i in xrange(fake_threads_to_create):
         for i in xrange(fake_threads_to_create):
             with atomic():
             with atomic():
                 datetime = timezone.now()
                 datetime = timezone.now()
-                forum = random.choice(forums)
+                category = random.choice(categories)
                 user = User.objects.order_by('?')[:1][0]
                 user = User.objects.order_by('?')[:1][0]
 
 
                 thread_is_moderated = random.randint(0, 100) > 90
                 thread_is_moderated = random.randint(0, 100) > 90
@@ -52,7 +54,7 @@ class Command(BaseCommand):
                 thread_is_closed = random.randint(0, 100) > 90
                 thread_is_closed = random.randint(0, 100) > 90
 
 
                 thread = Thread(
                 thread = Thread(
-                    forum=forum,
+                    category=category,
                     started_on=datetime,
                     started_on=datetime,
                     starter_name='-',
                     starter_name='-',
                     starter_slug='-',
                     starter_slug='-',
@@ -68,7 +70,7 @@ class Command(BaseCommand):
 
 
                 fake_message = "\n\n".join(fake.paragraphs())
                 fake_message = "\n\n".join(fake.paragraphs())
                 post = Post.objects.create(
                 post = Post.objects.create(
-                    forum=forum,
+                    category=category,
                     thread=thread,
                     thread=thread,
                     poster=user,
                     poster=user,
                     poster_name=user.username,
                     poster_name=user.username,
@@ -108,7 +110,7 @@ class Command(BaseCommand):
                         is_hidden = False
                         is_hidden = False
 
 
                     post = Post.objects.create(
                     post = Post.objects.create(
-                        forum=forum,
+                        category=category,
                         thread=thread,
                         thread=thread,
                         poster=user,
                         poster=user,
                         poster_name=user.username,
                         poster_name=user.username,
@@ -139,8 +141,8 @@ class Command(BaseCommand):
             thread.is_pinned = True
             thread.is_pinned = True
             thread.save()
             thread.save()
 
 
-        for forum in forums:
+        for category in categories:
-            forum.synchronize()
+            category.synchronize()
-            forum.save()
+            category.save()
 
 
         self.stdout.write(message % created_threads)
         self.stdout.write(message % created_threads)

+ 108 - 0
misago/readtracker/categoriestracker.py

@@ -0,0 +1,108 @@
+from django.db.models import F
+from django.utils import timezone
+
+from misago.threads.permissions import exclude_invisible_threads
+
+from misago.readtracker import signals
+from misago.readtracker.dates import is_date_tracked
+from misago.readtracker.models import CategoryRead
+
+
+__all__ = ['make_read_aware', 'sync_record']
+
+
+def make_read_aware(user, categories):
+    if not hasattr(categories, '__iter__'):
+        categories = [categories]
+
+    if user.is_anonymous():
+        make_read(categories)
+        return None
+
+    categories_dict = {}
+    for category in categories:
+        category.last_read_on = user.reads_cutoff
+        category.is_read = not is_date_tracked(category.last_post_on, user)
+        if not category.is_read:
+            categories_dict[category.pk] = category
+
+    if categories_dict:
+        categories_records = user.categoryread_set.filter(
+            category__in=categories_dict.keys())
+
+        for record in categories_records:
+            category = categories_dict[record.category_id]
+            category.last_read_on = record.last_read_on
+            category.is_read = category.last_read_on >= category.last_post_on
+
+
+def make_read(categories):
+    now = timezone.now()
+    for category in categories:
+        category.last_read_on = now
+        category.is_read = True
+
+
+def start_record(user, category):
+    user.categoryread_set.create(
+        category=category,
+        last_read_on=user.reads_cutoff,
+    )
+
+
+def sync_record(user, category):
+    cutoff_date = user.reads_cutoff
+
+    try:
+        category_record = user.categoryread_set.get(category=category)
+        if category_record.last_read_on > cutoff_date:
+            cutoff_date = category_record.last_read_on
+    except CategoryRead.DoesNotExist:
+        category_record = None
+
+    recorded_threads = category.thread_set.filter(last_post_on__gt=cutoff_date)
+    recorded_threads = exclude_invisible_threads(
+        recorded_threads, user, category)
+
+    all_threads_count = recorded_threads.count()
+
+    read_threads = user.threadread_set.filter(
+        category=category, last_read_on__gt=cutoff_date)
+    read_threads_count = read_threads.filter(
+        thread__last_post_on__lte=F("last_read_on")).count()
+
+    category_is_read = read_threads_count == all_threads_count
+
+    if category_is_read:
+        signals.category_read.send(sender=user, category=category)
+
+    if category_record:
+        if category_is_read:
+            category_record.last_read_on = category_record.last_read_on
+        else:
+            category_record.last_read_on = cutoff_date
+        category_record.save(update_fields=['last_read_on'])
+    else:
+        if category_is_read:
+            last_read_on = timezone.now()
+        else:
+            last_read_on = cutoff_date
+
+        category_record = user.categoryread_set.create(
+            category=category,
+            last_read_on=last_read_on)
+
+
+def read_category(user, category):
+    try:
+        category_record = user.categoryread_set.get(category=category)
+        category_record.last_read_on = timezone.now()
+        category_record.save(update_fields=['last_read_on'])
+    except CategoryRead.DoesNotExist:
+        user.categoryread_set.create(
+            category=category,
+            last_read_on=timezone.now(),
+        )
+
+    signals.category_read.send(sender=user, category=category)
+

+ 2 - 3
misago/readtracker/dates.py

@@ -1,12 +1,11 @@
 from datetime import timedelta
 from datetime import timedelta
-
 from django.conf import settings
 from django.conf import settings
 from django.utils import timezone
 from django.utils import timezone
 
 
 
 
-def is_date_tracked(date, user, forum_read_cutoff=None):
+def is_date_tracked(date, user, category_read_cutoff=None):
     if date:
     if date:
-        if forum_read_cutoff and forum_read_cutoff > date:
+        if category_read_cutoff and category_read_cutoff > date:
             return False
             return False
         else:
         else:
             return date > user.reads_cutoff
             return date > user.reads_cutoff

+ 0 - 97
misago/readtracker/forumstracker.py

@@ -1,97 +0,0 @@
-from django.db.models import F
-from django.utils import timezone
-
-from misago.threads.permissions import exclude_invisible_threads
-
-from misago.readtracker import signals
-from misago.readtracker.dates import is_date_tracked
-from misago.readtracker.models import ForumRead
-
-
-__all__ = ['make_read_aware', 'sync_record']
-
-
-def make_read_aware(user, forums):
-    if not hasattr(forums, '__iter__'):
-        forums = [forums]
-
-    if user.is_anonymous():
-        make_read(forums)
-        return None
-
-    forums_dict = {}
-    for forum in forums:
-        forum.last_read_on = user.reads_cutoff
-        forum.is_read = not is_date_tracked(forum.last_post_on, user)
-        if not forum.is_read:
-            forums_dict[forum.pk] = forum
-
-    if forums_dict:
-        for record in user.forumread_set.filter(forum__in=forums_dict.keys()):
-            forum = forums_dict[record.forum_id]
-            forum.last_read_on = record.last_read_on
-            forum.is_read = forum.last_read_on >= forum.last_post_on
-
-
-def make_read(forums):
-    now = timezone.now()
-    for forum in forums:
-        forum.last_read_on = now
-        forum.is_read = True
-
-
-def start_record(user, forum):
-    user.forumread_set.create(forum=forum, last_read_on=user.reads_cutoff)
-
-
-def sync_record(user, forum):
-    cutoff_date = user.reads_cutoff
-
-    try:
-        forum_record = user.forumread_set.get(forum=forum)
-        if forum_record.last_read_on > cutoff_date:
-            cutoff_date = forum_record.last_read_on
-    except ForumRead.DoesNotExist:
-        forum_record = None
-
-    recorded_threads = forum.thread_set.filter(last_post_on__gt=cutoff_date)
-    recorded_threads = exclude_invisible_threads(recorded_threads, user, forum)
-
-    all_threads_count = recorded_threads.count()
-
-    read_threads = user.threadread_set.filter(
-        forum=forum, last_read_on__gt=cutoff_date)
-    read_threads_count = read_threads.filter(
-        thread__last_post_on__lte=F("last_read_on")).count()
-
-    forum_is_read = read_threads_count == all_threads_count
-
-    if forum_is_read:
-        signals.forum_read.send(sender=user, forum=forum)
-
-    if forum_record:
-        if forum_is_read:
-            forum_record.last_read_on = forum_record.last_read_on
-        else:
-            forum_record.last_read_on = cutoff_date
-        forum_record.save(update_fields=['last_read_on'])
-    else:
-        if forum_is_read:
-            last_read_on = timezone.now()
-        else:
-            last_read_on = cutoff_date
-
-        forum_record = user.forumread_set.create(
-            forum=forum,
-            last_read_on=last_read_on)
-
-
-def read_forum(user, forum):
-    try:
-        forum_record = user.forumread_set.get(forum=forum)
-        forum_record.last_read_on = timezone.now()
-        forum_record.save(update_fields=['last_read_on'])
-    except ForumRead.DoesNotExist:
-        user.forumread_set.create(forum=forum, last_read_on=timezone.now())
-    signals.forum_read.send(sender=user, forum=forum)
-

+ 3 - 3
misago/readtracker/migrations/0001_initial.py

@@ -14,11 +14,11 @@ class Migration(migrations.Migration):
 
 
     operations = [
     operations = [
         migrations.CreateModel(
         migrations.CreateModel(
-            name='ForumRead',
+            name='CategoryRead',
             fields=[
             fields=[
                 ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
                 ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
                 ('last_read_on', models.DateTimeField()),
                 ('last_read_on', models.DateTimeField()),
-                ('forum', models.ForeignKey(to='misago_forums.Forum')),
+                ('category', models.ForeignKey(to='misago_categories.Category')),
                 ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
                 ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
             ],
             ],
             options={
             options={
@@ -31,7 +31,7 @@ class Migration(migrations.Migration):
                 ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
                 ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
                 ('read_replies', models.PositiveIntegerField(default=0)),
                 ('read_replies', models.PositiveIntegerField(default=0)),
                 ('last_read_on', models.DateTimeField()),
                 ('last_read_on', models.DateTimeField()),
-                ('forum', models.ForeignKey(to='misago_forums.Forum')),
+                ('category', models.ForeignKey(to='misago_categories.Category')),
                 ('thread', models.ForeignKey(to='misago_threads.Thread')),
                 ('thread', models.ForeignKey(to='misago_threads.Thread')),
                 ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
                 ('user', models.ForeignKey(to=settings.AUTH_USER_MODEL)),
             ],
             ],

+ 3 - 3
misago/readtracker/models.py

@@ -5,15 +5,15 @@ from django.db import models
 from django.utils import timezone
 from django.utils import timezone
 
 
 
 
-class ForumRead(models.Model):
+class CategoryRead(models.Model):
     user = models.ForeignKey(settings.AUTH_USER_MODEL)
     user = models.ForeignKey(settings.AUTH_USER_MODEL)
-    forum = models.ForeignKey('misago_forums.Forum')
+    category = models.ForeignKey('misago_categories.Category')
     last_read_on = models.DateTimeField()
     last_read_on = models.DateTimeField()
 
 
 
 
 class ThreadRead(models.Model):
 class ThreadRead(models.Model):
     user = models.ForeignKey(settings.AUTH_USER_MODEL)
     user = models.ForeignKey(settings.AUTH_USER_MODEL)
-    forum = models.ForeignKey('misago_forums.Forum')
+    category = models.ForeignKey('misago_categories.Category')
     thread = models.ForeignKey('misago_threads.Thread')
     thread = models.ForeignKey('misago_threads.Thread')
     read_replies =  models.PositiveIntegerField(default=0)
     read_replies =  models.PositiveIntegerField(default=0)
     last_read_on = models.DateTimeField()
     last_read_on = models.DateTimeField()

+ 5 - 5
misago/readtracker/signals.py

@@ -1,11 +1,11 @@
 from django.dispatch import receiver, Signal
 from django.dispatch import receiver, Signal
 
 
-from misago.forums.signals import move_forum_content
+from misago.categorues.signals import move_category_content
 from misago.threads.signals import move_thread, remove_thread_participant
 from misago.threads.signals import move_thread, remove_thread_participant
 
 
 
 
 all_read = Signal()
 all_read = Signal()
-forum_read = Signal(providing_args=["forum"])
+category_read = Signal(providing_args=["category"])
 thread_tracked = Signal(providing_args=["thread"])
 thread_tracked = Signal(providing_args=["thread"])
 thread_read = Signal(providing_args=["thread"])
 thread_read = Signal(providing_args=["thread"])
 
 
@@ -13,9 +13,9 @@ thread_read = Signal(providing_args=["thread"])
 """
 """
 Signal handlers
 Signal handlers
 """
 """
-@receiver(move_forum_content)
+@receiver(move_category_content)
-def delete_forum_tracker(sender, **kwargs):
+def delete_category_tracker(sender, **kwargs):
-    sender.forumread_set.all().delete()
+    sender.categoryread_set.all().delete()
     sender.threadread_set.all().delete()
     sender.threadread_set.all().delete()
 
 
 
 

+ 8 - 6
misago/readtracker/tests/test_dates.py

@@ -22,13 +22,15 @@ class ReadTrackerDatesTests(TestCase):
         future_date = timezone.now() + timedelta(minutes=10)
         future_date = timezone.now() + timedelta(minutes=10)
         self.assertTrue(is_date_tracked(future_date, MockUser()))
         self.assertTrue(is_date_tracked(future_date, MockUser()))
 
 
-    def test_is_date_tracked_with_forum_cutoff(self):
+    def test_is_date_tracked_with_category_cutoff(self):
-        """is_date_tracked validates dates using forum cutoff"""
+        """is_date_tracked validates dates using category cutoff"""
         self.assertFalse(is_date_tracked(None, MockUser()))
         self.assertFalse(is_date_tracked(None, MockUser()))
         past_date = timezone.now() + timedelta(minutes=10)
         past_date = timezone.now() + timedelta(minutes=10)
 
 
-        forum_cutoff = timezone.now() + timedelta(minutes=20)
+        category_cutoff = timezone.now() + timedelta(minutes=20)
-        self.assertFalse(is_date_tracked(past_date, MockUser(), forum_cutoff))
+        self.assertFalse(
+            is_date_tracked(past_date, MockUser(), category_cutoff))
 
 
-        forum_cutoff = timezone.now() - timedelta(minutes=20)
+        category_cutoff = timezone.now() - timedelta(minutes=20)
-        self.assertTrue(is_date_tracked(past_date, MockUser(), forum_cutoff))
+        self.assertTrue(
+            is_date_tracked(past_date, MockUser(), category_cutoff))

+ 92 - 89
misago/readtracker/tests/test_readtracker.py

@@ -5,149 +5,152 @@ from django.test import TestCase
 from django.utils import timezone
 from django.utils import timezone
 
 
 from misago.acl import add_acl
 from misago.acl import add_acl
-from misago.forums.models import Forum
+from misago.categories.models import Category
 from misago.threads import testutils
 from misago.threads import testutils
 from misago.users.models import AnonymousUser
 from misago.users.models import AnonymousUser
 
 
-from misago.readtracker import forumstracker, threadstracker
+from misago.readtracker import categoriestracker, threadstracker
 
 
 
 
 class ReadTrackerTests(TestCase):
 class ReadTrackerTests(TestCase):
     def setUp(self):
     def setUp(self):
-        self.forums = [f for f in Forum.objects.filter(role="forum")[:1]]
+        self.categories = [f for f in Category.objects.filter(role='forum')[:1]]
-        self.forum = self.forums[0]
+        self.category = self.categories[0]
 
 
         User = get_user_model()
         User = get_user_model()
         self.user = User.objects.create_user("Bob", "bob@test.com", "Pass.123")
         self.user = User.objects.create_user("Bob", "bob@test.com", "Pass.123")
         self.anon = AnonymousUser()
         self.anon = AnonymousUser()
 
 
     def post_thread(self, datetime):
     def post_thread(self, datetime):
-        return testutils.post_thread(forum=self.forum, started_on=datetime)
+        return testutils.post_thread(
+            category=self.category,
+            started_on=datetime
+        )
 
 
 
 
-class ForumsTrackerTests(ReadTrackerTests):
+class CategorysTrackerTests(ReadTrackerTests):
-    def test_anon_empty_forum_read(self):
+    def test_anon_empty_category_read(self):
         """anon users content is always read"""
         """anon users content is always read"""
-        forumstracker.make_read_aware(self.anon, self.forums)
+        categoriestracker.make_read_aware(self.anon, self.categories)
-        self.assertIsNone(self.forum.last_post_on)
+        self.assertIsNone(self.category.last_post_on)
-        self.assertTrue(self.forum.is_read)
+        self.assertTrue(self.category.is_read)
 
 
-    def test_anon_forum_with_recent_reply_read(self):
+    def test_anon_category_with_recent_reply_read(self):
         """anon users content is always read"""
         """anon users content is always read"""
-        forumstracker.make_read_aware(self.anon, self.forums)
+        categoriestracker.make_read_aware(self.anon, self.categories)
-        self.forum.last_post_on = timezone.now()
+        self.category.last_post_on = timezone.now()
-        self.assertTrue(self.forum.is_read)
+        self.assertTrue(self.category.is_read)
 
 
-    def test_empty_forum_is_read(self):
+    def test_empty_category_is_read(self):
-        """empty forum is read for signed in user"""
+        """empty category is read for signed in user"""
-        forumstracker.make_read_aware(self.user, self.forums)
+        categoriestracker.make_read_aware(self.user, self.categories)
-        self.assertTrue(self.forum.is_read)
+        self.assertTrue(self.category.is_read)
 
 
-    def test_make_read_aware_sets_read_flag_for_empty_forum(self):
+    def test_make_read_aware_sets_read_flag_for_empty_category(self):
-        """make_read_aware sets read flag on empty forum"""
+        """make_read_aware sets read flag on empty category"""
-        forumstracker.make_read_aware(self.anon, self.forums)
+        categoriestracker.make_read_aware(self.anon, self.categories)
-        self.assertTrue(self.forum.is_read)
+        self.assertTrue(self.category.is_read)
 
 
-        forumstracker.make_read_aware(self.user, self.forums)
+        categoriestracker.make_read_aware(self.user, self.categories)
-        self.assertTrue(self.forum.is_read)
+        self.assertTrue(self.category.is_read)
 
 
-    def test_make_read_aware_sets_read_flag_for_forum_with_old_thread(self):
+    def test_make_read_aware_sets_read_flag_for_category_with_old_thread(self):
-        """make_read_aware sets read flag on forum with old thread"""
+        """make_read_aware sets read flag on category with old thread"""
-        self.forum.last_post_on = self.user.reads_cutoff - timedelta(days=1)
+        self.category.last_post_on = self.user.reads_cutoff - timedelta(days=1)
 
 
-        forumstracker.make_read_aware(self.user, self.forums)
+        categoriestracker.make_read_aware(self.user, self.categories)
-        self.assertTrue(self.forum.is_read)
+        self.assertTrue(self.category.is_read)
 
 
-    def test_make_read_aware_sets_unread_flag_for_forum_with_new_thread(self):
+    def test_make_read_aware_sets_unread_flag_for_category_with_new_thread(self):
-        """make_read_aware sets unread flag on forum with new thread"""
+        """make_read_aware sets unread flag on category with new thread"""
-        self.forum.last_post_on = self.user.reads_cutoff + timedelta(days=1)
+        self.category.last_post_on = self.user.reads_cutoff + timedelta(days=1)
 
 
-        forumstracker.make_read_aware(self.user, self.forums)
+        categoriestracker.make_read_aware(self.user, self.categories)
-        self.assertFalse(self.forum.is_read)
+        self.assertFalse(self.category.is_read)
 
 
-    def test_sync_record_for_empty_forum(self):
+    def test_sync_record_for_empty_category(self):
-        """sync_record sets read flag on empty forum"""
+        """sync_record sets read flag on empty category"""
-        add_acl(self.user, self.forums)
+        add_acl(self.user, self.categories)
-        forumstracker.sync_record(self.user, self.forum)
+        categoriestracker.sync_record(self.user, self.category)
-        self.user.forumread_set.get(forum=self.forum)
+        self.user.categoryread_set.get(category=self.category)
 
 
-        forumstracker.make_read_aware(self.user, self.forums)
+        categoriestracker.make_read_aware(self.user, self.categories)
-        self.assertTrue(self.forum.is_read)
+        self.assertTrue(self.category.is_read)
 
 
-    def test_sync_record_for_forum_with_old_thread_and_reply(self):
+    def test_sync_record_for_category_with_old_thread_and_reply(self):
         """
         """
-        sync_record sets read flag on forum with old thread,
+        sync_record sets read flag on category with old thread,
         then changes flag to unread when new reply is posted
         then changes flag to unread when new reply is posted
         """
         """
         self.post_thread(self.user.reads_cutoff - timedelta(days=1))
         self.post_thread(self.user.reads_cutoff - timedelta(days=1))
 
 
-        add_acl(self.user, self.forums)
+        add_acl(self.user, self.categories)
-        forumstracker.sync_record(self.user, self.forum)
+        categoriestracker.sync_record(self.user, self.category)
-        self.user.forumread_set.get(forum=self.forum)
+        self.user.categoryread_set.get(category=self.category)
 
 
-        forumstracker.make_read_aware(self.user, self.forums)
+        categoriestracker.make_read_aware(self.user, self.categories)
-        self.assertTrue(self.forum.is_read)
+        self.assertTrue(self.category.is_read)
 
 
         thread = self.post_thread(self.user.reads_cutoff + timedelta(days=1))
         thread = self.post_thread(self.user.reads_cutoff + timedelta(days=1))
-        forumstracker.sync_record(self.user, self.forum)
+        categoriestracker.sync_record(self.user, self.category)
-        forumstracker.make_read_aware(self.user, self.forums)
+        categoriestracker.make_read_aware(self.user, self.categories)
-        self.assertFalse(self.forum.is_read)
+        self.assertFalse(self.category.is_read)
 
 
-    def test_sync_record_for_forum_with_new_thread(self):
+    def test_sync_record_for_category_with_new_thread(self):
         """
         """
-        sync_record sets read flag on forum with old thread,
+        sync_record sets read flag on category with old thread,
         then keeps flag to unread when new reply is posted
         then keeps flag to unread when new reply is posted
         """
         """
         self.post_thread(self.user.reads_cutoff + timedelta(days=1))
         self.post_thread(self.user.reads_cutoff + timedelta(days=1))
 
 
-        add_acl(self.user, self.forums)
+        add_acl(self.user, self.categories)
-        forumstracker.sync_record(self.user, self.forum)
+        categoriestracker.sync_record(self.user, self.category)
-        self.user.forumread_set.get(forum=self.forum)
+        self.user.categoryread_set.get(category=self.category)
 
 
-        forumstracker.make_read_aware(self.user, self.forums)
+        categoriestracker.make_read_aware(self.user, self.categories)
-        self.assertFalse(self.forum.is_read)
+        self.assertFalse(self.category.is_read)
 
 
         self.post_thread(self.user.reads_cutoff + timedelta(days=1))
         self.post_thread(self.user.reads_cutoff + timedelta(days=1))
-        forumstracker.sync_record(self.user, self.forum)
+        categoriestracker.sync_record(self.user, self.category)
-        forumstracker.make_read_aware(self.user, self.forums)
+        categoriestracker.make_read_aware(self.user, self.categories)
-        self.assertFalse(self.forum.is_read)
+        self.assertFalse(self.category.is_read)
 
 
-    def test_sync_record_for_forum_with_deleted_threads(self):
+    def test_sync_record_for_category_with_deleted_threads(self):
-        """unread forum reverts to read after its emptied"""
+        """unread category reverts to read after its emptied"""
         self.post_thread(self.user.reads_cutoff + timedelta(days=1))
         self.post_thread(self.user.reads_cutoff + timedelta(days=1))
         self.post_thread(self.user.reads_cutoff + timedelta(days=1))
         self.post_thread(self.user.reads_cutoff + timedelta(days=1))
         self.post_thread(self.user.reads_cutoff + timedelta(days=1))
         self.post_thread(self.user.reads_cutoff + timedelta(days=1))
 
 
-        add_acl(self.user, self.forums)
+        add_acl(self.user, self.categories)
-        forumstracker.sync_record(self.user, self.forum)
+        categoriestracker.sync_record(self.user, self.category)
-        forumstracker.make_read_aware(self.user, self.forums)
+        categoriestracker.make_read_aware(self.user, self.categories)
-        self.assertFalse(self.forum.is_read)
+        self.assertFalse(self.category.is_read)
 
 
-        self.forum.thread_set.all().delete()
+        self.category.thread_set.all().delete()
-        self.forum.synchronize()
+        self.category.synchronize()
-        self.forum.save()
+        self.category.save()
 
 
-        forumstracker.make_read_aware(self.user, self.forums)
+        categoriestracker.make_read_aware(self.user, self.categories)
-        self.assertTrue(self.forum.is_read)
+        self.assertTrue(self.category.is_read)
 
 
-    def test_sync_record_for_forum_with_many_threads(self):
+    def test_sync_record_for_category_with_many_threads(self):
-        """sync_record sets unread flag on forum with many threads"""
+        """sync_record sets unread flag on category with many threads"""
         self.post_thread(self.user.reads_cutoff + timedelta(days=1))
         self.post_thread(self.user.reads_cutoff + timedelta(days=1))
         self.post_thread(self.user.reads_cutoff - timedelta(days=1))
         self.post_thread(self.user.reads_cutoff - timedelta(days=1))
         self.post_thread(self.user.reads_cutoff + timedelta(days=1))
         self.post_thread(self.user.reads_cutoff + timedelta(days=1))
         self.post_thread(self.user.reads_cutoff - timedelta(days=1))
         self.post_thread(self.user.reads_cutoff - timedelta(days=1))
 
 
-        add_acl(self.user, self.forums)
+        add_acl(self.user, self.categories)
-        forumstracker.sync_record(self.user, self.forum)
+        categoriestracker.sync_record(self.user, self.category)
-        self.user.forumread_set.get(forum=self.forum)
+        self.user.categoryread_set.get(category=self.category)
 
 
-        forumstracker.make_read_aware(self.user, self.forums)
+        categoriestracker.make_read_aware(self.user, self.categories)
-        self.assertFalse(self.forum.is_read)
+        self.assertFalse(self.category.is_read)
 
 
         self.post_thread(self.user.reads_cutoff + timedelta(days=1))
         self.post_thread(self.user.reads_cutoff + timedelta(days=1))
-        forumstracker.sync_record(self.user, self.forum)
+        categoriestracker.sync_record(self.user, self.category)
-        forumstracker.make_read_aware(self.user, self.forums)
+        categoriestracker.make_read_aware(self.user, self.categories)
-        self.assertFalse(self.forum.is_read)
+        self.assertFalse(self.category.is_read)
 
 
 
 
 class ThreadsTrackerTests(ReadTrackerTests):
 class ThreadsTrackerTests(ReadTrackerTests):
@@ -189,26 +192,26 @@ class ThreadsTrackerTests(ReadTrackerTests):
         """thread read flag is set for user, then its set as unread by reply"""
         """thread read flag is set for user, then its set as unread by reply"""
         self.reply_thread(self.thread)
         self.reply_thread(self.thread)
 
 
-        add_acl(self.user, self.forums)
+        add_acl(self.user, self.categories)
         threadstracker.make_read_aware(self.user, self.thread)
         threadstracker.make_read_aware(self.user, self.thread)
         self.assertFalse(self.thread.is_read)
         self.assertFalse(self.thread.is_read)
 
 
         threadstracker.read_thread(self.user, self.thread, self.post)
         threadstracker.read_thread(self.user, self.thread, self.post)
         threadstracker.make_read_aware(self.user, self.thread)
         threadstracker.make_read_aware(self.user, self.thread)
         self.assertTrue(self.thread.is_read)
         self.assertTrue(self.thread.is_read)
-        forumstracker.make_read_aware(self.user, self.forums)
+        categoriestracker.make_read_aware(self.user, self.categories)
-        self.assertTrue(self.forum.is_read)
+        self.assertTrue(self.category.is_read)
 
 
         self.thread.last_post_on = timezone.now()
         self.thread.last_post_on = timezone.now()
         self.thread.save()
         self.thread.save()
-        self.forum.synchronize()
+        self.category.synchronize()
-        self.forum.save()
+        self.category.save()
 
 
         self.reply_thread()
         self.reply_thread()
         threadstracker.make_read_aware(self.user, self.thread)
         threadstracker.make_read_aware(self.user, self.thread)
         self.assertFalse(self.thread.is_read)
         self.assertFalse(self.thread.is_read)
-        forumstracker.make_read_aware(self.user, self.forums)
+        categoriestracker.make_read_aware(self.user, self.categories)
-        self.assertFalse(self.forum.is_read)
+        self.assertFalse(self.category.is_read)
 
 
         posts = [post for post in self.thread.post_set.order_by('id')]
         posts = [post for post in self.thread.post_set.order_by('id')]
         threadstracker.make_posts_read_aware(self.user, self.thread, posts)
         threadstracker.make_posts_read_aware(self.user, self.thread, posts)

+ 25 - 23
misago/readtracker/tests/test_views.py

@@ -2,49 +2,51 @@ from django.contrib.auth import get_user_model
 from django.core.urlresolvers import reverse
 from django.core.urlresolvers import reverse
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
 
 
-from misago.forums.models import Forum
+from misago.categories.models import Category
 from misago.users.testutils import AuthenticatedUserTestCase
 from misago.users.testutils import AuthenticatedUserTestCase
 from misago.threads import testutils
 from misago.threads import testutils
 
 
-from misago.readtracker.forumstracker import make_read_aware
+from misago.readtracker.categoriestracker import make_read_aware
 
 
 
 
 class AuthenticatedTests(AuthenticatedUserTestCase):
 class AuthenticatedTests(AuthenticatedUserTestCase):
     def test_read_all_threads(self):
     def test_read_all_threads(self):
         """read_all view updates reads cutoff on user model"""
         """read_all view updates reads cutoff on user model"""
-        forum = Forum.objects.all_forums().filter(role="forum")[:1][0]
+        category = Category.objects.all_categories().filter(role='forum')[:1][0]
-        threads = [testutils.post_thread(forum) for t in xrange(10)]
+        threads = [testutils.post_thread(category) for t in xrange(10)]
 
 
-        forum = Forum.objects.get(id=forum.id)
+        category = Category.objects.get(id=category.id)
-        make_read_aware(self.user, [forum])
+        make_read_aware(self.user, [category])
-        self.assertFalse(forum.is_read)
+        self.assertFalse(category.is_read)
 
 
         response = self.client.post(reverse('misago:read_all'))
         response = self.client.post(reverse('misago:read_all'))
         self.assertEqual(response.status_code, 302)
         self.assertEqual(response.status_code, 302)
 
 
-        forum = Forum.objects.get(id=forum.id)
+        category = Category.objects.get(id=category.id)
         user = get_user_model().objects.get(id=self.user.id)
         user = get_user_model().objects.get(id=self.user.id)
 
 
-        make_read_aware(user, [forum])
+        make_read_aware(user, [category])
-        self.assertTrue(forum.is_read)
+        self.assertTrue(category.is_read)
 
 
-    def test_read_forum(self):
+    def test_read_category(self):
-        """read_forum view updates reads cutoff on forum tracker"""
+        """read_category view updates reads cutoff on category tracker"""
-        forum = Forum.objects.all_forums().filter(role="forum")[:1][0]
+        category = Category.objects.all_categories().filter(role='forum')[:1][0]
-        threads = [testutils.post_thread(forum) for t in xrange(10)]
+        threads = [testutils.post_thread(category) for t in xrange(10)]
 
 
-        forum = Forum.objects.get(id=forum.id)
+        category = Category.objects.get(id=category.id)
-        make_read_aware(self.user, [forum])
+        make_read_aware(self.user, [category])
-        self.assertFalse(forum.is_read)
+        self.assertFalse(category.is_read)
+
+        response = self.client.post(everse('misago:read_category', kwargs={
+            'category_id': category.id
+        }))
 
 
-        response = self.client.post(
-            reverse('misago:read_forum', kwargs={'forum_id': forum.id}))
         self.assertEqual(response.status_code, 302)
         self.assertEqual(response.status_code, 302)
         self.assertTrue(
         self.assertTrue(
-            response['location'].endswith(forum.get_absolute_url()))
+            response['location'].endswith(category.get_absolute_url()))
 
 
-        forum = Forum.objects.get(id=forum.id)
+        category = Category.objects.get(id=category.id)
         user = get_user_model().objects.get(id=self.user.id)
         user = get_user_model().objects.get(id=self.user.id)
 
 
-        make_read_aware(user, [forum])
+        make_read_aware(user, [category])
-        self.assertTrue(forum.is_read)
+        self.assertTrue(category.is_read)

+ 30 - 26
misago/readtracker/threadstracker.py

@@ -3,9 +3,9 @@ from django.utils import timezone
 
 
 from misago.notifications import read_user_notifications
 from misago.notifications import read_user_notifications
 
 
-from misago.readtracker import forumstracker, signals
+from misago.readtracker import categoriestracker, signals
 from misago.readtracker.dates import is_date_tracked
 from misago.readtracker.dates import is_date_tracked
-from misago.readtracker.models import ForumRead, ThreadRead
+from misago.readtracker.models import CategoyRead, ThreadRead
 
 
 
 
 __all__ = ['make_read_aware', 'read_thread']
 __all__ = ['make_read_aware', 'read_thread']
@@ -18,7 +18,7 @@ def make_read_aware(user, target):
         make_thread_read_aware(user, target)
         make_thread_read_aware(user, target)
 
 
 
 
-def make_threads_read_aware(user, threads, forum=None):
+def make_threads_read_aware(user, threads, category=None):
     if not threads:
     if not threads:
         return None
         return None
 
 
@@ -26,10 +26,10 @@ def make_threads_read_aware(user, threads, forum=None):
         make_read(threads)
         make_read(threads)
         return None
         return None
 
 
-    if forum:
+    if category:
-        make_forum_threads_read_aware(user, forum, threads)
+        make_category_threads_read_aware(user, category, threads)
     else:
     else:
-        make_forums_threads_read_aware(user, threads)
+        make_categories_threads_read_aware(user, threads)
 
 
 
 
 def make_read(threads):
 def make_read(threads):
@@ -39,14 +39,14 @@ def make_read(threads):
         thread.is_new = False
         thread.is_new = False
 
 
 
 
-def make_forum_threads_read_aware(user, forum, threads):
+def make_category_threads_read_aware(user, category, threads):
-    if forum.is_read:
+    if category.is_read:
         make_read(threads)
         make_read(threads)
     else:
     else:
         threads_dict = {}
         threads_dict = {}
         for thread in threads:
         for thread in threads:
             thread.is_read = not is_date_tracked(
             thread.is_read = not is_date_tracked(
-                thread.last_post_on, user, forum.last_read_on)
+                thread.last_post_on, user, category.last_read_on)
             thread.is_new = True
             thread.is_new = True
             if thread.is_read:
             if thread.is_read:
                 thread.unread_replies = 0
                 thread.unread_replies = 0
@@ -58,14 +58,16 @@ def make_forum_threads_read_aware(user, forum, threads):
             make_threads_dict_read_aware(user, threads_dict)
             make_threads_dict_read_aware(user, threads_dict)
 
 
 
 
-def make_forums_threads_read_aware(user, threads):
+def make_categories_threads_read_aware(user, threads):
-    forums_cutoffs = fetch_forums_cutoffs_for_threads(user, threads)
+    categories_cutoffs = fetch_categories_cutoffs_for_threads(user, threads)
 
 
     threads_dict = {}
     threads_dict = {}
     for thread in threads:
     for thread in threads:
+        category_cutoff = categories_cutoffs.get(thread.category_id)
         thread.is_read = not is_date_tracked(
         thread.is_read = not is_date_tracked(
-            thread.last_post_on, user, forums_cutoffs.get(thread.forum_id))
+            thread.last_post_on, user, category_cutoff)
         thread.is_new = True
         thread.is_new = True
+
         if thread.is_read:
         if thread.is_read:
             thread.unread_replies = 0
             thread.unread_replies = 0
         else:
         else:
@@ -76,16 +78,16 @@ def make_forums_threads_read_aware(user, threads):
         make_threads_dict_read_aware(user, threads_dict)
         make_threads_dict_read_aware(user, threads_dict)
 
 
 
 
-def fetch_forums_cutoffs_for_threads(user, threads):
+def fetch_categories_cutoffs_for_threads(user, threads):
-    forums = []
+    categories = []
     for thread in threads:
     for thread in threads:
-        if thread.forum_id not in forums:
+        if thread.category_id not in categories:
-            forums.append(thread.forum_id)
+            categories.append(thread.category_id)
 
 
-    forums_dict = {}
+    categories_dict = {}
-    for record in user.forumread_set.filter(forum__in=forums):
+    for record in user.categoriesread_set.filter(category__in=categories):
-        forums_dict[record.forum_id] = record.last_read_on
+        categories_dict[record.category_id] = record.last_read_on
-    return forums_dict
+    return categories_dict
 
 
 
 
 def make_threads_dict_read_aware(user, threads_dict):
 def make_threads_dict_read_aware(user, threads_dict):
@@ -117,8 +119,10 @@ def make_thread_read_aware(user, thread):
         thread.is_new = True
         thread.is_new = True
 
 
         try:
         try:
-            forum_record = user.forumread_set.get(forum_id=thread.forum_id)
+            category_record = user.categoriesread_set.get(
-            if thread.last_post_on > forum_record.last_read_on:
+                category_id=thread.category_id)
+
+            if thread.last_post_on > category_record.last_read_on:
                 try:
                 try:
                     thread_record = user.threadread_set.get(thread=thread)
                     thread_record = user.threadread_set.get(thread=thread)
                     thread.last_read_on = thread_record.last_read_on
                     thread.last_read_on = thread_record.last_read_on
@@ -131,8 +135,8 @@ def make_thread_read_aware(user, thread):
             else:
             else:
                 thread.is_read = True
                 thread.is_read = True
                 thread.is_new = False
                 thread.is_new = False
-        except ForumRead.DoesNotExist:
+        except CategoyRead.DoesNotExist:
-            forumstracker.start_record(user, thread.forum)
+            categoriestracker.start_record(user, thread.category)
 
 
 
 
 def make_posts_read_aware(user, thread, posts):
 def make_posts_read_aware(user, thread, posts):
@@ -170,7 +174,7 @@ def sync_record(user, thread, last_read_reply):
         thread.read_record.save(update_fields=['read_replies', 'last_read_on'])
         thread.read_record.save(update_fields=['read_replies', 'last_read_on'])
     else:
     else:
         user.threadread_set.create(
         user.threadread_set.create(
-            forum=thread.forum,
+            category=thread.category,
             thread=thread,
             thread=thread,
             read_replies=read_replies,
             read_replies=read_replies,
             last_read_on=last_read_reply.posted_on)
             last_read_on=last_read_reply.posted_on)
@@ -179,7 +183,7 @@ def sync_record(user, thread, last_read_reply):
 
 
     if last_read_reply.posted_on == thread.last_post_on:
     if last_read_reply.posted_on == thread.last_post_on:
         signals.thread_read.send(sender=user, thread=thread)
         signals.thread_read.send(sender=user, thread=thread)
-        forumstracker.sync_record(user, thread.forum)
+        categoriestracker.sync_record(user, thread.category)
 
 
     read_user_notifications(user, notification_triggers, False)
     read_user_notifications(user, notification_triggers, False)
 
 

+ 1 - 1
misago/readtracker/urls.py

@@ -3,5 +3,5 @@ from django.conf.urls import include, patterns, url
 
 
 urlpatterns = patterns('misago.readtracker.views',
 urlpatterns = patterns('misago.readtracker.views',
     url(r'^read-all/$', 'read_all', name='read_all'),
     url(r'^read-all/$', 'read_all', name='read_all'),
-    url(r'^read-forum/(?P<forum_id>\d+)/$', 'read_forum', name='read_forum'),
+    url(r'^read-category/(?P<category_id>\d+)/$', 'read_category', name='read_category'),
 )
 )

+ 7 - 7
misago/readtracker/views.py

@@ -6,11 +6,11 @@ from django.utils.translation import ugettext as _
 from django.views.decorators.cache import never_cache
 from django.views.decorators.cache import never_cache
 from django.views.decorators.csrf import csrf_protect
 from django.views.decorators.csrf import csrf_protect
 
 
+from misago.categories.models import Category
 from misago.core.decorators import require_POST
 from misago.core.decorators import require_POST
-from misago.forums.models import Forum
 from misago.users.decorators import deny_guests
 from misago.users.decorators import deny_guests
 
 
-from misago.readtracker import forumstracker
+from misago.readtracker import categoriestracker
 from misago.readtracker.signals import all_read
 from misago.readtracker.signals import all_read
 
 
 
 
@@ -32,14 +32,14 @@ def read_all(request):
 
 
     all_read.send(sender=request.user)
     all_read.send(sender=request.user)
 
 
-    messages.info(request, _("All forums and threads were marked as read."))
+    messages.info(request, _("All categories and threads were marked as read."))
     return redirect('misago:index')
     return redirect('misago:index')
 
 
 
 
 @read_view
 @read_view
-def read_forum(request, forum_id):
+def read_category(request, category_id):
-    forum = get_object_or_404(Forum.objects, id=forum_id)
+    category = get_object_or_404(Category.objects, id=category_id)
-    forumstracker.read_forum(request.user, forum)
+    categoriestracker.read_category(request.user, category)
 
 
     messages.info(request, _("Threads were marked as read."))
     messages.info(request, _("Threads were marked as read."))
-    return redirect(forum.get_absolute_url())
+    return redirect(category.get_absolute_url())

+ 6 - 7
misago/threads/admin.py

@@ -1,6 +1,5 @@
 from django.conf.urls import url
 from django.conf.urls import url
 from django.utils.translation import ugettext_lazy as _
 from django.utils.translation import ugettext_lazy as _
-
 from misago.threads.views.labelsadmin import (LabelsList, NewLabel,
 from misago.threads.views.labelsadmin import (LabelsList, NewLabel,
                                               EditLabel, DeleteLabel)
                                               EditLabel, DeleteLabel)
 
 
@@ -8,8 +7,8 @@ from misago.threads.views.labelsadmin import (LabelsList, NewLabel,
 class MisagoAdminExtension(object):
 class MisagoAdminExtension(object):
     def register_urlpatterns(self, urlpatterns):
     def register_urlpatterns(self, urlpatterns):
         # Threads Labels
         # Threads Labels
-        urlpatterns.namespace(r'^labels/', 'labels', 'forums')
+        urlpatterns.namespace(r'^labels/', 'labels', 'categories')
-        urlpatterns.patterns('forums:labels',
+        urlpatterns.patterns('categories:labels',
             url(r'^$', LabelsList.as_view(), name='index'),
             url(r'^$', LabelsList.as_view(), name='index'),
             url(r'^new/$', NewLabel.as_view(), name='new'),
             url(r'^new/$', NewLabel.as_view(), name='new'),
             url(r'^edit/(?P<label_id>\d+)/$', EditLabel.as_view(), name='edit'),
             url(r'^edit/(?P<label_id>\d+)/$', EditLabel.as_view(), name='edit'),
@@ -19,7 +18,7 @@ class MisagoAdminExtension(object):
     def register_navigation_nodes(self, site):
     def register_navigation_nodes(self, site):
         site.add_node(name=_("Thread labels"),
         site.add_node(name=_("Thread labels"),
                       icon='fa fa-tags',
                       icon='fa fa-tags',
-                      parent='misago:admin:forums',
+                      parent='misago:admin:categories',
-                      after='misago:admin:forums:nodes:index',
+                      after='misago:admin:categories:nodes:index',
-                      namespace='misago:admin:forums:labels',
+                      namespace='misago:admin:categories:labels',
-                      link='misago:admin:forums:labels:index')
+                      link='misago:admin:categories:labels:index')

+ 3 - 2
misago/threads/counts.py

@@ -4,7 +4,7 @@ from django.conf import settings
 from django.db.models import F
 from django.db.models import F
 from django.dispatch import receiver
 from django.dispatch import receiver
 
 
-from misago.forums.models import Forum
+from misago.categories.models import Category
 
 
 from misago.threads.views.moderatedcontent import ModeratedContent
 from misago.threads.views.moderatedcontent import ModeratedContent
 from misago.threads.views.newthreads import NewThreads
 from misago.threads.views.newthreads import NewThreads
@@ -98,7 +98,8 @@ def sync_user_unread_private_threads_count(user):
 
 
     all_threads_count = threads_qs.count()
     all_threads_count = threads_qs.count()
 
 
-    read_qs = user.threadread_set.filter(forum=Forum.objects.private_threads())
+    read_qs = user.threadread_set.filter(
+        category=Category.objects.private_threads())
     read_qs = read_qs.filter(last_read_on__gte=F('thread__last_post_on'))
     read_qs = read_qs.filter(last_read_on__gte=F('thread__last_post_on'))
     read_threads_count = read_qs.count()
     read_threads_count = read_qs.count()
 
 

+ 2 - 2
misago/threads/events.py

@@ -41,7 +41,7 @@ def format_message(message, links):
 
 
 def record_event(user, thread, icon, message, links=None):
 def record_event(user, thread, icon, message, links=None):
     event = Event.objects.create(
     event = Event.objects.create(
-        forum=thread.forum,
+        category=thread.category,
         thread=thread,
         thread=thread,
         author=user,
         author=user,
         author_name=user.username,
         author_name=user.username,
@@ -71,7 +71,7 @@ def real_add_events_to_posts(user, thread, posts, delimeter=None):
         events_queryset = events_queryset.filter(occured_on__lt=delimeter)
         events_queryset = events_queryset.filter(occured_on__lt=delimeter)
     events_queryset = events_queryset.order_by('id')
     events_queryset = events_queryset.order_by('id')
 
 
-    acl = user.acl['forums'].get(thread.forum_id, {})
+    acl = user.acl['categories'].get(thread.category_id, {})
     if not acl.get('can_hide_events'):
     if not acl.get('can_hide_events'):
         events_queryset = events_queryset.filter(is_hidden=False)
         events_queryset = events_queryset.filter(is_hidden=False)
 
 

+ 5 - 5
misago/threads/forms/admin.py

@@ -2,7 +2,7 @@ from django.utils.translation import ugettext_lazy as _
 
 
 from misago.core import forms
 from misago.core import forms
 from misago.core.validators import validate_sluggable
 from misago.core.validators import validate_sluggable
-from misago.forums.forms import AdminForumMultipleChoiceField
+from misago.categories.forms import AdminCategoryMultipleChoiceField
 
 
 from misago.threads.models import Label
 from misago.threads.models import Label
 
 
@@ -13,14 +13,14 @@ class LabelForm(forms.ModelForm):
     css_class = forms.CharField(
     css_class = forms.CharField(
         label=_("CSS class"), required=False,
         label=_("CSS class"), required=False,
         help_text=_("Optional CSS clas used to style this label."))
         help_text=_("Optional CSS clas used to style this label."))
-    forums = AdminForumMultipleChoiceField(
+    categories = AdminCategoryMultipleChoiceField(
-        label=_('Forums'), required=False, include_root=False,
+        label=_('Categories'), required=False, include_root=False,
         widget=forms.CheckboxSelectMultiple(),
         widget=forms.CheckboxSelectMultiple(),
-        help_text=_('Select forums this label will be available in.'))
+        help_text=_('Select categories this label will be available in.'))
 
 
     class Meta:
     class Meta:
         model = Label
         model = Label
-        fields = ['name', 'css_class', 'forums']
+        fields = ['name', 'css_class', 'categories']
 
 
     def clean_name(self):
     def clean_name(self):
         data = self.cleaned_data['name']
         data = self.cleaned_data['name']

+ 28 - 27
misago/threads/forms/moderation.py

@@ -5,9 +5,10 @@ from django.http import Http404
 from django.utils.translation import ugettext_lazy as _
 from django.utils.translation import ugettext_lazy as _
 
 
 from misago.acl import add_acl
 from misago.acl import add_acl
+from misago.categories.forms import CategoryChoiceField
+from misago.categories.permissions import (allow_see_category,
+                                           allow_browse_category)
 from misago.core import forms
 from misago.core import forms
-from misago.forums.forms import ForumChoiceField
-from misago.forums.permissions import allow_see_forum, allow_browse_forum
 
 
 from misago.threads.models import Thread
 from misago.threads.models import Thread
 from misago.threads.permissions import allow_see_thread
 from misago.threads.permissions import allow_see_thread
@@ -31,39 +32,39 @@ class MergeThreadsForm(forms.Form):
 
 
 
 
 class MoveThreadsForm(forms.Form):
 class MoveThreadsForm(forms.Form):
-    new_forum = ForumChoiceField(label=_("Move threads to forum"),
+    new_category = CategoryChoiceField(label=_("Move threads to category"),
-                                 empty_label=None)
+                                       empty_label=None)
 
 
     def __init__(self, *args, **kwargs):
     def __init__(self, *args, **kwargs):
-        self.forum = kwargs.pop('forum')
+        self.category = kwargs.pop('category')
         acl = kwargs.pop('acl')
         acl = kwargs.pop('acl')
 
 
         super(MoveThreadsForm, self).__init__(*args, **kwargs)
         super(MoveThreadsForm, self).__init__(*args, **kwargs)
 
 
-        self.fields['new_forum'].set_acl(acl)
+        self.fields['new_category'].set_acl(acl)
 
 
     def clean(self):
     def clean(self):
         data = super(MoveThreadsForm, self).clean()
         data = super(MoveThreadsForm, self).clean()
 
 
-        new_forum = data.get('new_forum')
+        new_category = data.get('new_category')
-        if new_forum:
+        if new_category:
-            if new_forum.is_category:
+            if new_category.is_category:
                 message = _("You can't move threads to category.")
                 message = _("You can't move threads to category.")
                 raise forms.ValidationError(message)
                 raise forms.ValidationError(message)
-            if new_forum.is_redirect:
+            if new_category.is_redirect:
                 message = _("You can't move threads to redirect.")
                 message = _("You can't move threads to redirect.")
                 raise forms.ValidationError(message)
                 raise forms.ValidationError(message)
-            if new_forum.pk == self.forum.pk:
+            if new_category.pk == self.category.pk:
-                message = _("New forum is same as current one.")
+                message = _("New category is same as current one.")
                 raise forms.ValidationError(message)
                 raise forms.ValidationError(message)
         else:
         else:
-            raise forms.ValidationError(_("You have to select forum."))
+            raise forms.ValidationError(_("You have to select category."))
         return data
         return data
 
 
 
 
 class MoveThreadForm(MoveThreadsForm):
 class MoveThreadForm(MoveThreadsForm):
-    new_forum = ForumChoiceField(label=_("Move thread to forum"),
+    new_category = CategoryChoiceField(label=_("Move thread to category"),
-                                 empty_label=None)
+                                       empty_label=None)
 
 
 
 
 class MovePostsForm(forms.Form):
 class MovePostsForm(forms.Form):
@@ -90,14 +91,14 @@ class MovePostsForm(forms.Form):
             if not 'thread_id' in resolution.kwargs:
             if not 'thread_id' in resolution.kwargs:
                 raise Http404()
                 raise Http404()
 
 
-            queryset = Thread.objects.select_related('forum')
+            queryset = Thread.objects.select_related('category')
             self.new_thread = queryset.get(id=resolution.kwargs['thread_id'])
             self.new_thread = queryset.get(id=resolution.kwargs['thread_id'])
 
 
-            add_acl(self.user, self.new_thread.forum)
+            add_acl(self.user, self.new_thread.category)
             add_acl(self.user, self.new_thread)
             add_acl(self.user, self.new_thread)
 
 
-            allow_see_forum(self.user, self.new_thread.forum)
+            allow_see_category(self.user, self.new_thread.category)
-            allow_browse_forum(self.user, self.new_thread.forum)
+            allow_browse_category(self.user, self.new_thread.category)
             allow_see_thread(self.user, self.new_thread)
             allow_see_thread(self.user, self.new_thread)
 
 
         except (Http404, Thread.DoesNotExist):
         except (Http404, Thread.DoesNotExist):
@@ -108,7 +109,7 @@ class MovePostsForm(forms.Form):
             message = _("New thread is same as current one.")
             message = _("New thread is same as current one.")
             raise forms.ValidationError(message)
             raise forms.ValidationError(message)
 
 
-        if self.new_thread.forum.special_role:
+        if self.new_thread.category.special_role:
             message = _("You can't move posts to special threads.")
             message = _("You can't move posts to special threads.")
             raise forms.ValidationError(message)
             raise forms.ValidationError(message)
 
 
@@ -116,7 +117,7 @@ class MovePostsForm(forms.Form):
 
 
 
 
 class SplitThreadForm(forms.Form):
 class SplitThreadForm(forms.Form):
-    forum = ForumChoiceField(label=_("New thread forum"),
+    category = CategoryChoiceField(label=_("New thread category"),
                                  empty_label=None)
                                  empty_label=None)
 
 
     thread_title = forms.CharField(label=_("New thread title"),
     thread_title = forms.CharField(label=_("New thread title"),
@@ -127,21 +128,21 @@ class SplitThreadForm(forms.Form):
 
 
         super(SplitThreadForm, self).__init__(*args, **kwargs)
         super(SplitThreadForm, self).__init__(*args, **kwargs)
 
 
-        self.fields['forum'].set_acl(acl)
+        self.fields['category'].set_acl(acl)
 
 
     def clean(self):
     def clean(self):
         data = super(SplitThreadForm, self).clean()
         data = super(SplitThreadForm, self).clean()
 
 
-        forum = data.get('forum')
+        category = data.get('category')
-        if forum:
+        if category:
-            if forum.is_category:
+            if category.is_category:
                 message = _("You can't start threads in category.")
                 message = _("You can't start threads in category.")
                 raise forms.ValidationError(message)
                 raise forms.ValidationError(message)
-            if forum.is_redirect:
+            if category.is_redirect:
                 message = _("You can't start threads in redirect.")
                 message = _("You can't start threads in redirect.")
                 raise forms.ValidationError(message)
                 raise forms.ValidationError(message)
         else:
         else:
-            raise forms.ValidationError(_("You have to select forum."))
+            raise forms.ValidationError(_("You have to select category."))
 
 
         thread_title = data.get('thread_title')
         thread_title = data.get('thread_title')
         if thread_title:
         if thread_title:

+ 10 - 10
misago/threads/migrations/0001_initial.py

@@ -12,7 +12,7 @@ from misago.core.pgutils import CreatePartialIndex, CreatePartialCompositeIndex
 class Migration(migrations.Migration):
 class Migration(migrations.Migration):
 
 
     dependencies = [
     dependencies = [
-        ('misago_forums', '0001_initial'),
+        ('misago_categories', '0001_initial'),
         migrations.swappable_dependency(settings.AUTH_USER_MODEL),
         migrations.swappable_dependency(settings.AUTH_USER_MODEL),
     ]
     ]
 
 
@@ -24,7 +24,7 @@ class Migration(migrations.Migration):
                 ('name', models.CharField(max_length=255)),
                 ('name', models.CharField(max_length=255)),
                 ('slug', models.SlugField(max_length=255)),
                 ('slug', models.SlugField(max_length=255)),
                 ('css_class', models.CharField(max_length=255, null=True, blank=True)),
                 ('css_class', models.CharField(max_length=255, null=True, blank=True)),
-                ('forums', models.ManyToManyField(to='misago_forums.Forum')),
+                ('categories', models.ManyToManyField(to='misago_categories.Category')),
             ],
             ],
             options={
             options={
             },
             },
@@ -55,7 +55,7 @@ class Migration(migrations.Migration):
                 ('is_moderated', models.BooleanField(default=False, db_index=True)),
                 ('is_moderated', models.BooleanField(default=False, db_index=True)),
                 ('is_hidden', models.BooleanField(default=False)),
                 ('is_hidden', models.BooleanField(default=False)),
                 ('is_protected', models.BooleanField(default=False)),
                 ('is_protected', models.BooleanField(default=False)),
-                ('forum', models.ForeignKey(to='misago_forums.Forum')),
+                ('category', models.ForeignKey(to='misago_categories.Category')),
                 ('last_editor', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.SET_NULL, blank=True, to=settings.AUTH_USER_MODEL, null=True)),
                 ('last_editor', models.ForeignKey(related_name='+', on_delete=django.db.models.deletion.SET_NULL, blank=True, to=settings.AUTH_USER_MODEL, null=True)),
                 ('mentions', models.ManyToManyField(related_name='mention_set', to=settings.AUTH_USER_MODEL)),
                 ('mentions', models.ManyToManyField(related_name='mention_set', to=settings.AUTH_USER_MODEL)),
                 ('poster', models.ForeignKey(on_delete=django.db.models.deletion.SET_NULL, blank=True, to=settings.AUTH_USER_MODEL, null=True)),
                 ('poster', models.ForeignKey(on_delete=django.db.models.deletion.SET_NULL, blank=True, to=settings.AUTH_USER_MODEL, null=True)),
@@ -132,7 +132,7 @@ class Migration(migrations.Migration):
                 ('checksum', models.CharField(max_length=64, default='-')),
                 ('checksum', models.CharField(max_length=64, default='-')),
                 ('is_hidden', models.BooleanField(default=False)),
                 ('is_hidden', models.BooleanField(default=False)),
                 ('author', models.ForeignKey(on_delete=django.db.models.deletion.SET_NULL, blank=True, to=settings.AUTH_USER_MODEL, null=True)),
                 ('author', models.ForeignKey(on_delete=django.db.models.deletion.SET_NULL, blank=True, to=settings.AUTH_USER_MODEL, null=True)),
-                ('forum', models.ForeignKey(to='misago_forums.Forum')),
+                ('category', models.ForeignKey(to='misago_categories.Category')),
                 ('thread', models.ForeignKey(to='misago_threads.Thread')),
                 ('thread', models.ForeignKey(to='misago_threads.Thread')),
             ],
             ],
             options={
             options={
@@ -154,7 +154,7 @@ class Migration(migrations.Migration):
                 ('closed_by_slug', models.CharField(max_length=255)),
                 ('closed_by_slug', models.CharField(max_length=255)),
                 ('closed_by', models.ForeignKey(related_name='closedreport_set', on_delete=django.db.models.deletion.SET_NULL, blank=True, to=settings.AUTH_USER_MODEL, null=True)),
                 ('closed_by', models.ForeignKey(related_name='closedreport_set', on_delete=django.db.models.deletion.SET_NULL, blank=True, to=settings.AUTH_USER_MODEL, null=True)),
                 ('closed_on', models.DateTimeField(default=django.utils.timezone.now)),
                 ('closed_on', models.DateTimeField(default=django.utils.timezone.now)),
-                ('forum', models.ForeignKey(to='misago_forums.Forum')),
+                ('category', models.ForeignKey(to='misago_categories.Category')),
                 ('post', models.ForeignKey(to='misago_threads.Post')),
                 ('post', models.ForeignKey(to='misago_threads.Post')),
                 ('reported_by', models.ForeignKey(on_delete=django.db.models.deletion.SET_NULL, blank=True, to=settings.AUTH_USER_MODEL, null=True)),
                 ('reported_by', models.ForeignKey(on_delete=django.db.models.deletion.SET_NULL, blank=True, to=settings.AUTH_USER_MODEL, null=True)),
                 ('thread', models.ForeignKey(to='misago_threads.Thread')),
                 ('thread', models.ForeignKey(to='misago_threads.Thread')),
@@ -192,8 +192,8 @@ class Migration(migrations.Migration):
         ),
         ),
         migrations.AddField(
         migrations.AddField(
             model_name='thread',
             model_name='thread',
-            name='forum',
+            name='category',
-            field=models.ForeignKey(to='misago_forums.Forum'),
+            field=models.ForeignKey(to='misago_categories.Category'),
             preserve_default=True,
             preserve_default=True,
         ),
         ),
         migrations.AddField(
         migrations.AddField(
@@ -223,9 +223,9 @@ class Migration(migrations.Migration):
         migrations.AlterIndexTogether(
         migrations.AlterIndexTogether(
             name='thread',
             name='thread',
             index_together=set([
             index_together=set([
-                ('forum', 'id'),
+                ('category', 'id'),
-                ('forum', 'last_post_on'),
+                ('category', 'last_post_on'),
-                ('forum', 'replies'),
+                ('category', 'replies'),
             ]),
             ]),
         ),
         ),
         CreatePartialCompositeIndex(
         CreatePartialCompositeIndex(

+ 2 - 2
misago/threads/models/event.py

@@ -8,7 +8,7 @@ from misago.threads.checksums import is_event_valid
 
 
 
 
 class Event(models.Model):
 class Event(models.Model):
-    forum = models.ForeignKey('misago_forums.Forum')
+    category = models.ForeignKey('misago_categories.Category')
     thread = models.ForeignKey('Thread')
     thread = models.ForeignKey('Thread')
     author = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True,
     author = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True,
                                on_delete=models.SET_NULL)
                                on_delete=models.SET_NULL)
@@ -31,7 +31,7 @@ class Event(models.Model):
 
 
     @property
     @property
     def thread_type(self):
     def thread_type(self):
-        return self.forum.thread_type
+        return self.category.thread_type
 
 
     def get_edit_url(self):
     def get_edit_url(self):
         return self.thread_type.get_event_edit_url(self)
         return self.thread_type.get_event_edit_url(self)

+ 7 - 7
misago/threads/models/label.py

@@ -8,10 +8,10 @@ CACHE_NAME = 'misago_threads_labels'
 
 
 
 
 class LabelManager(models.Manager):
 class LabelManager(models.Manager):
-    def get_forum_labels(self, forum):
+    def get_category_labels(self, category):
         labels = []
         labels = []
         for label in self.get_cached_labels():
         for label in self.get_cached_labels():
-            if forum.pk in label.forums_ids:
+            if category.pk in label.categories_ids:
                 labels.append(label)
                 labels.append(label)
         return labels
         return labels
 
 
@@ -22,9 +22,9 @@ class LabelManager(models.Manager):
         labels = cache.get(CACHE_NAME, 'nada')
         labels = cache.get(CACHE_NAME, 'nada')
         if labels == 'nada':
         if labels == 'nada':
             labels = []
             labels = []
-            labels_qs = self.all().prefetch_related('forums')
+            labels_qs = self.all().prefetch_related('categories')
             for label in labels_qs.order_by('name'):
             for label in labels_qs.order_by('name'):
-                label.forums_ids = [f.pk for f in label.forums.all()]
+                label.categories_ids = [f.pk for f in label.categories.all()]
                 labels.append(label)
                 labels.append(label)
             cache.set(CACHE_NAME, labels)
             cache.set(CACHE_NAME, labels)
         return labels
         return labels
@@ -34,7 +34,7 @@ class LabelManager(models.Manager):
 
 
 
 
 class Label(models.Model):
 class Label(models.Model):
-    forums = models.ManyToManyField('misago_forums.Forum')
+    categories = models.ManyToManyField('misago_categories.Category')
     name = models.CharField(max_length=255)
     name = models.CharField(max_length=255)
     slug = models.SlugField(max_length=255)
     slug = models.SlugField(max_length=255)
     css_class = models.CharField(max_length=255, null=True, blank=True)
     css_class = models.CharField(max_length=255, null=True, blank=True)
@@ -56,8 +56,8 @@ class Label(models.Model):
 
 
     def strip_inavailable_labels(self):
     def strip_inavailable_labels(self):
         qs = self.thread_set
         qs = self.thread_set
-        if self.forums:
+        if self.categories:
-            qs = qs.exclude(forum__in=self.forums.all())
+            qs = qs.exclude(category__in=self.categories.all())
         qs.update(label=None)
         qs.update(label=None)
 
 
     def set_name(self, name):
     def set_name(self, name):

+ 3 - 3
misago/threads/models/post.py

@@ -10,7 +10,7 @@ from misago.threads.checksums import update_post_checksum, is_post_valid
 
 
 
 
 class Post(models.Model):
 class Post(models.Model):
-    forum = models.ForeignKey('misago_forums.Forum')
+    category = models.ForeignKey('misago_categories.Category')
     thread = models.ForeignKey('misago_threads.Thread')
     thread = models.ForeignKey('misago_threads.Thread')
     poster = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True,
     poster = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True,
                                on_delete=models.SET_NULL)
                                on_delete=models.SET_NULL)
@@ -82,13 +82,13 @@ class Post(models.Model):
     def move(self, new_thread):
     def move(self, new_thread):
         from misago.threads.signals import move_post
         from misago.threads.signals import move_post
 
 
-        self.forum = new_thread.forum
+        self.category = new_thread.category
         self.thread = new_thread
         self.thread = new_thread
         move_post.send(sender=self)
         move_post.send(sender=self)
 
 
     @property
     @property
     def thread_type(self):
     def thread_type(self):
-        return self.forum.thread_type
+        return self.category.thread_type
 
 
     def get_absolute_url(self):
     def get_absolute_url(self):
         return self.thread_type.get_post_absolute_url(self)
         return self.thread_type.get_post_absolute_url(self)

+ 1 - 1
misago/threads/models/report.py

@@ -5,7 +5,7 @@ from misago.conf import settings
 
 
 
 
 class Report(models.Model):
 class Report(models.Model):
-    forum = models.ForeignKey('misago_forums.Forum')
+    category = models.ForeignKey('misago_categories.Category')
     thread = models.ForeignKey('misago_threads.Thread')
     thread = models.ForeignKey('misago_threads.Thread')
     post = models.ForeignKey('misago_threads.Post')
     post = models.ForeignKey('misago_threads.Post')
     reported_by = models.ForeignKey(settings.AUTH_USER_MODEL,
     reported_by = models.ForeignKey(settings.AUTH_USER_MODEL,

+ 7 - 7
misago/threads/models/thread.py

@@ -5,7 +5,7 @@ from misago.core.utils import slugify
 
 
 
 
 class Thread(models.Model):
 class Thread(models.Model):
-    forum = models.ForeignKey('misago_forums.Forum')
+    category = models.ForeignKey('misago_categories.Category')
     label = models.ForeignKey('misago_threads.Label',
     label = models.ForeignKey('misago_threads.Label',
                               null=True, blank=True,
                               null=True, blank=True,
                               on_delete=models.SET_NULL)
                               on_delete=models.SET_NULL)
@@ -49,9 +49,9 @@ class Thread(models.Model):
 
 
     class Meta:
     class Meta:
         index_together = [
         index_together = [
-            ['forum', 'id'],
+            ['category', 'id'],
-            ['forum', 'last_post_on'],
+            ['category', 'last_post_on'],
-            ['forum', 'replies'],
+            ['category', 'replies'],
         ]
         ]
 
 
     def __unicode__(self):
     def __unicode__(self):
@@ -73,10 +73,10 @@ class Thread(models.Model):
         from misago.threads.signals import merge_thread
         from misago.threads.signals import merge_thread
         merge_thread.send(sender=self, other_thread=other_thread)
         merge_thread.send(sender=self, other_thread=other_thread)
 
 
-    def move(self, new_forum):
+    def move(self, new_category):
         from misago.threads.signals import move_thread
         from misago.threads.signals import move_thread
 
 
-        self.forum = new_forum
+        self.category = new_category
         move_thread.send(sender=self)
         move_thread.send(sender=self)
 
 
     def synchronize(self):
     def synchronize(self):
@@ -116,7 +116,7 @@ class Thread(models.Model):
 
 
     @property
     @property
     def thread_type(self):
     def thread_type(self):
-        return self.forum.thread_type
+        return self.category.thread_type
 
 
     def get_absolute_url(self):
     def get_absolute_url(self):
         return self.thread_type.get_thread_absolute_url(self)
         return self.thread_type.get_thread_absolute_url(self)

+ 6 - 6
misago/threads/moderation/threads.py

@@ -68,16 +68,16 @@ def unpin_thread(user, thread):
 
 
 
 
 @atomic
 @atomic
-def move_thread(user, thread, new_forum):
+def move_thread(user, thread, new_category):
-    if thread.forum_id != new_forum.pk:
+    if thread.category_id != new_category.pk:
-        message = _("%(user)s moved thread from %(forum)s.")
+        message = _("%(user)s moved thread from %(category)s.")
         record_event(user, thread, "arrow-right", message, {
         record_event(user, thread, "arrow-right", message, {
             'user': user,
             'user': user,
-            'forum': thread.forum
+            'category': thread.category
         })
         })
 
 
-        thread.move(new_forum)
+        thread.move(new_category)
-        thread.save(update_fields=['has_events', 'forum'])
+        thread.save(update_fields=['has_events', 'category'])
         return True
         return True
     else:
     else:
         return False
         return False

+ 6 - 6
misago/threads/permissions/privatethreads.py

@@ -7,8 +7,8 @@ from django.utils.translation import ugettext_lazy as _
 from misago.acl import add_acl, algebra
 from misago.acl import add_acl, algebra
 from misago.acl.decorators import return_boolean
 from misago.acl.decorators import return_boolean
 from misago.acl.models import Role
 from misago.acl.models import Role
+from misago.categories.models import Category
 from misago.core import forms
 from misago.core import forms
-from misago.forums.models import Forum
 
 
 
 
 __all__ = [
 __all__ = [
@@ -87,12 +87,12 @@ def build_acl(acl, roles, key_name):
     if not new_acl['can_use_private_threads']:
     if not new_acl['can_use_private_threads']:
         return new_acl
         return new_acl
 
 
-    private_forum = Forum.objects.private_threads()
+    private_category = Category.objects.private_threads()
 
 
     if new_acl['can_moderate_private_threads']:
     if new_acl['can_moderate_private_threads']:
-        new_acl['can_review_moderated_content'].append(private_forum.pk)
+        new_acl['can_review_moderated_content'].append(private_category.pk)
 
 
-    forum_acl = {
+    category_acl = {
         'can_see': 1,
         'can_see': 1,
         'can_browse': 1,
         'can_browse': 1,
         'can_see_all_threads': 1,
         'can_see_all_threads': 1,
@@ -117,7 +117,7 @@ def build_acl(acl, roles, key_name):
     }
     }
 
 
     if new_acl['can_moderate_private_threads']:
     if new_acl['can_moderate_private_threads']:
-        forum_acl.update({
+        category_acl.update({
             'can_edit_threads': 2,
             'can_edit_threads': 2,
             'can_edit_posts': 2,
             'can_edit_posts': 2,
             'can_hide_threads': 2,
             'can_hide_threads': 2,
@@ -130,7 +130,7 @@ def build_acl(acl, roles, key_name):
             'can_hide_events': 2,
             'can_hide_events': 2,
         })
         })
 
 
-    new_acl['forums'][private_forum.pk] = forum_acl
+    new_acl['categories'][private_category.pk] = category_acl
 
 
     return new_acl
     return new_acl
 
 

+ 153 - 150
misago/threads/permissions/threads.py

@@ -6,9 +6,9 @@ from django.utils.translation import ungettext, ugettext_lazy as _
 
 
 from misago.acl import add_acl, algebra
 from misago.acl import add_acl, algebra
 from misago.acl.decorators import return_boolean
 from misago.acl.decorators import return_boolean
+from misago.categories.models import Category, RoleCategoryACL, CategoryRole
+from misago.categories.permissions import get_categories_roles
 from misago.core import forms
 from misago.core import forms
-from misago.forums.models import Forum, RoleForumACL, ForumRole
-from misago.forums.permissions import get_forums_roles
 
 
 from misago.threads.models import Thread, Post, Event
 from misago.threads.models import Thread, Post, Event
 
 
@@ -148,7 +148,7 @@ class PermissionsForm(forms.Form):
 
 
 
 
 def change_permissions_form(role):
 def change_permissions_form(role):
-    if isinstance(role, ForumRole):
+    if isinstance(role, CategoryRole):
         return PermissionsForm
         return PermissionsForm
     else:
     else:
         return None
         return None
@@ -160,22 +160,22 @@ ACL Builder
 def build_acl(acl, roles, key_name):
 def build_acl(acl, roles, key_name):
     acl['can_review_moderated_content'] = []
     acl['can_review_moderated_content'] = []
     acl['can_see_reports'] = []
     acl['can_see_reports'] = []
-    forums_roles = get_forums_roles(roles)
+    categories_roles = get_categories_roles(roles)
-
+
-    for forum in Forum.objects.all_forums():
+    for category in Category.objects.all_categories():
-        forum_acl = acl['forums'].get(forum.pk, {'can_browse': 0})
+        category_acl = acl['categories'].get(category.pk, {'can_browse': 0})
-        if forum_acl['can_browse']:
+        if category_acl['can_browse']:
-            acl['forums'][forum.pk] = build_forum_acl(
+            acl['categories'][category.pk] = build_category_acl(
-                forum_acl, forum, forums_roles, key_name)
+                category_acl, category, categories_roles, key_name)
-            if acl['forums'][forum.pk]['can_review_moderated_content']:
+            if acl['categories'][category.pk]['can_review_moderated_content']:
-                acl['can_review_moderated_content'].append(forum.pk)
+                acl['can_review_moderated_content'].append(category.pk)
-            if acl['forums'][forum.pk]['can_see_reports']:
+            if acl['categories'][category.pk]['can_see_reports']:
-                acl['can_see_reports'].append(forum.pk)
+                acl['can_see_reports'].append(category.pk)
     return acl
     return acl
 
 
 
 
-def build_forum_acl(acl, forum, forums_roles, key_name):
+def build_category_acl(acl, category, categories_roles, key_name):
-    forum_roles = forums_roles.get(forum.pk, [])
+    category_roles = categories_roles.get(category.pk, [])
 
 
     final_acl = {
     final_acl = {
         'can_see_all_threads': 0,
         'can_see_all_threads': 0,
@@ -205,7 +205,7 @@ def build_forum_acl(acl, forum, forums_roles, key_name):
     }
     }
     final_acl.update(acl)
     final_acl.update(acl)
 
 
-    algebra.sum_acls(final_acl, roles=forum_roles, key=key_name,
+    algebra.sum_acls(final_acl, roles=category_roles, key=key_name,
         can_see_all_threads=algebra.greater,
         can_see_all_threads=algebra.greater,
         can_start_threads=algebra.greater,
         can_start_threads=algebra.greater,
         can_reply_threads=algebra.greater,
         can_reply_threads=algebra.greater,
@@ -238,10 +238,10 @@ def build_forum_acl(acl, forum, forums_roles, key_name):
 """
 """
 ACL's for targets
 ACL's for targets
 """
 """
-def add_acl_to_forum(user, forum):
+def add_acl_to_category(user, category):
-    forum_acl = user.acl['forums'].get(forum.pk, {})
+    category_acl = user.acl['categories'].get(category.pk, {})
 
 
-    forum.acl.update({
+    category.acl.update({
         'can_see_all_threads': 0,
         'can_see_all_threads': 0,
         'can_start_threads': 0,
         'can_start_threads': 0,
         'can_reply_threads': 0,
         'can_reply_threads': 0,
@@ -268,11 +268,11 @@ def add_acl_to_forum(user, forum):
         'can_hide_events': 0,
         'can_hide_events': 0,
     })
     })
 
 
-    algebra.sum_acls(forum.acl, acls=[forum_acl],
+    algebra.sum_acls(category.acl, acls=[category_acl],
         can_see_all_threads=algebra.greater)
         can_see_all_threads=algebra.greater)
 
 
     if user.is_authenticated():
     if user.is_authenticated():
-        algebra.sum_acls(forum.acl, acls=[forum_acl],
+        algebra.sum_acls(category.acl, acls=[category_acl],
             can_start_threads=algebra.greater,
             can_start_threads=algebra.greater,
             can_reply_threads=algebra.greater,
             can_reply_threads=algebra.greater,
             can_edit_threads=algebra.greater,
             can_edit_threads=algebra.greater,
@@ -298,55 +298,55 @@ def add_acl_to_forum(user, forum):
             can_hide_events=algebra.greater,
             can_hide_events=algebra.greater,
         )
         )
 
 
-    forum.acl['can_see_own_threads'] = not forum.acl['can_see_all_threads']
+    category.acl['can_see_own_threads'] = not category.acl['can_see_all_threads']
 
 
 
 
 def add_acl_to_thread(user, thread):
 def add_acl_to_thread(user, thread):
-    forum_acl = user.acl['forums'].get(thread.forum_id, {})
+    category_acl = user.acl['categories'].get(thread.category_id, {})
 
 
     thread.acl.update({
     thread.acl.update({
         'can_reply': can_reply_thread(user, thread),
         'can_reply': can_reply_thread(user, thread),
         'can_edit': can_edit_thread(user, thread),
         'can_edit': can_edit_thread(user, thread),
-        'can_hide': forum_acl.get('can_hide_threads'),
+        'can_hide': category_acl.get('can_hide_threads'),
-        'can_change_label': forum_acl.get('can_change_threads_labels') == 2,
+        'can_change_label': category_acl.get('can_change_threads_labels') == 2,
-        'can_pin': forum_acl.get('can_pin_threads'),
+        'can_pin': category_acl.get('can_pin_threads'),
-        'can_close': forum_acl.get('can_close_threads'),
+        'can_close': category_acl.get('can_close_threads'),
-        'can_move': forum_acl.get('can_move_threads'),
+        'can_move': category_acl.get('can_move_threads'),
-        'can_review': forum_acl.get('can_review_moderated_content'),
+        'can_review': category_acl.get('can_review_moderated_content'),
-        'can_report': forum_acl.get('can_report_content'),
+        'can_report': category_acl.get('can_report_content'),
-        'can_see_reports': forum_acl.get('can_see_reports')
+        'can_see_reports': category_acl.get('can_see_reports')
     })
     })
 
 
     if can_change_owned_thread(user, thread):
     if can_change_owned_thread(user, thread):
-        if not forum_acl.get('can_close_threads'):
+        if not category_acl.get('can_close_threads'):
-            thread_is_protected = thread.is_closed or thread.forum.is_closed
+            thread_is_protected = thread.is_closed or thread.category.is_closed
         else:
         else:
             thread_is_protected = False
             thread_is_protected = False
 
 
         if not thread_is_protected:
         if not thread_is_protected:
             if not thread.acl['can_change_label']:
             if not thread.acl['can_change_label']:
-                can_change_label = forum_acl.get('can_change_threads_labels')
+                can_change_label = category_acl.get('can_change_threads_labels')
                 thread.acl['can_change_label'] = can_change_label == 1
                 thread.acl['can_change_label'] = can_change_label == 1
             if not thread.acl['can_hide']:
             if not thread.acl['can_hide']:
                 if not thread.replies:
                 if not thread.replies:
-                    can_hide_thread = forum_acl.get('can_hide_own_threads')
+                    can_hide_thread = category_acl.get('can_hide_own_threads')
                     thread.acl['can_hide'] = can_hide_thread
                     thread.acl['can_hide'] = can_hide_thread
 
 
 
 
 def add_acl_to_post(user, post):
 def add_acl_to_post(user, post):
-    forum_acl = user.acl['forums'].get(post.forum_id, {})
+    category_acl = user.acl['categories'].get(post.category_id, {})
 
 
     post.acl.update({
     post.acl.update({
         'can_reply': can_reply_thread(user, post.thread),
         'can_reply': can_reply_thread(user, post.thread),
         'can_edit': can_edit_post(user, post),
         'can_edit': can_edit_post(user, post),
-        'can_see_hidden': forum_acl.get('can_hide_posts'),
+        'can_see_hidden': category_acl.get('can_hide_posts'),
         'can_unhide': can_unhide_post(user, post),
         'can_unhide': can_unhide_post(user, post),
         'can_hide': can_hide_post(user, post),
         'can_hide': can_hide_post(user, post),
         'can_delete': can_delete_post(user, post),
         'can_delete': can_delete_post(user, post),
-        'can_protect': forum_acl.get('can_protect_posts'),
+        'can_protect': category_acl.get('can_protect_posts'),
-        'can_report': forum_acl.get('can_report_content'),
+        'can_report': category_acl.get('can_report_content'),
-        'can_see_reports': forum_acl.get('can_see_reports'),
+        'can_see_reports': category_acl.get('can_see_reports'),
-        'can_approve': forum_acl.get('can_review_moderated_content'),
+        'can_approve': category_acl.get('can_review_moderated_content'),
     })
     })
 
 
     if not post.is_moderated:
     if not post.is_moderated:
@@ -360,15 +360,15 @@ def add_acl_to_post(user, post):
 
 
 
 
 def add_acl_to_event(user, event):
 def add_acl_to_event(user, event):
-    forum_acl = user.acl['forums'].get(event.forum_id, {})
+    category_acl = user.acl['categories'].get(event.category_id, {})
-    can_hide_events = forum_acl.get('can_hide_events', 0)
+    can_hide_events = category_acl.get('can_hide_events', 0)
 
 
     event.acl['can_hide'] = can_hide_events > 0
     event.acl['can_hide'] = can_hide_events > 0
     event.acl['can_delete'] = can_hide_events == 2
     event.acl['can_delete'] = can_hide_events == 2
 
 
 
 
 def register_with(registry):
 def register_with(registry):
-    registry.acl_annotator(Forum, add_acl_to_forum)
+    registry.acl_annotator(Category, add_acl_to_category)
     registry.acl_annotator(Thread, add_acl_to_thread)
     registry.acl_annotator(Thread, add_acl_to_thread)
     registry.acl_annotator(Post, add_acl_to_post)
     registry.acl_annotator(Post, add_acl_to_post)
     registry.acl_annotator(Event, add_acl_to_event)
     registry.acl_annotator(Event, add_acl_to_event)
@@ -378,17 +378,17 @@ def register_with(registry):
 ACL tests
 ACL tests
 """
 """
 def allow_see_thread(user, target):
 def allow_see_thread(user, target):
-    forum_acl = user.acl['forums'].get(target.forum_id, {})
+    category_acl = user.acl['categories'].get(target.category_id, {})
-    if not forum_acl.get('can_browse'):
+    if not category_acl.get('can_browse'):
         raise Http404()
         raise Http404()
 
 
     if user.is_anonymous() or user.pk != target.starter_id:
     if user.is_anonymous() or user.pk != target.starter_id:
-        if not forum_acl.get('can_see_all_threads'):
+        if not category_acl.get('can_see_all_threads'):
             raise Http404()
             raise Http404()
         if target.is_moderated:
         if target.is_moderated:
-            if not forum_acl.get('can_review_moderated_content'):
+            if not category_acl.get('can_review_moderated_content'):
                 raise Http404()
                 raise Http404()
-        if target.is_hidden and not forum_acl.get('can_hide_threads'):
+        if target.is_hidden and not category_acl.get('can_hide_threads'):
             raise Http404()
             raise Http404()
 can_see_thread = return_boolean(allow_see_thread)
 can_see_thread = return_boolean(allow_see_thread)
 
 
@@ -399,11 +399,11 @@ def allow_start_thread(user, target):
 
 
     if target.is_closed and not target.acl['can_close_threads']:
     if target.is_closed and not target.acl['can_close_threads']:
         raise PermissionDenied(
         raise PermissionDenied(
-            _("This forum is closed. You can't start new threads in it."))
+            _("This category is closed. You can't start new threads in it."))
 
 
-    if not user.acl['forums'].get(target.id, {'can_start_threads': False}):
+    if not user.acl['categories'].get(target.id, {'can_start_threads': False}):
         raise PermissionDenied(_("You don't have permission to start "
         raise PermissionDenied(_("You don't have permission to start "
-                                 "new threads in this forum."))
+                                 "new threads in this category."))
 can_start_thread = return_boolean(allow_start_thread)
 can_start_thread = return_boolean(allow_start_thread)
 
 
 
 
@@ -411,18 +411,19 @@ def allow_reply_thread(user, target):
     if user.is_anonymous():
     if user.is_anonymous():
         raise PermissionDenied(_("You have to sign in to reply threads."))
         raise PermissionDenied(_("You have to sign in to reply threads."))
 
 
-    forum_acl = target.forum.acl
+    category_acl = target.category.acl
 
 
-    if not forum_acl['can_close_threads']:
+    if not category_acl['can_close_threads']:
-        if target.forum.is_closed:
+        if target.category.is_closed:
             raise PermissionDenied(
             raise PermissionDenied(
-                _("This forum is closed. You can't reply to threads in it."))
+                _("This category is closed. You can't reply to threads in it."))
         if target.is_closed:
         if target.is_closed:
             raise PermissionDenied(
             raise PermissionDenied(
-                _("You can't reply to closed threads in this forum."))
+                _("You can't reply to closed threads in this category."))
 
 
-    if not forum_acl['can_reply_threads']:
+    if not category_acl['can_reply_threads']:
-        raise PermissionDenied(_("You can't reply to threads in this forum."))
+        raise PermissionDenied(
+            _("You can't reply to threads in this category."))
 can_reply_thread = return_boolean(allow_reply_thread)
 can_reply_thread = return_boolean(allow_reply_thread)
 
 
 
 
@@ -430,39 +431,39 @@ def allow_edit_thread(user, target):
     if user.is_anonymous():
     if user.is_anonymous():
         raise PermissionDenied(_("You have to sign in to edit threads."))
         raise PermissionDenied(_("You have to sign in to edit threads."))
 
 
-    forum_acl = target.forum.acl
+    category_acl = target.category.acl
 
 
-    if not forum_acl['can_edit_threads']:
+    if not category_acl['can_edit_threads']:
-        raise PermissionDenied(_("You can't edit threads in this forum."))
+        raise PermissionDenied(_("You can't edit threads in this category."))
 
 
-    if forum_acl['can_edit_threads'] == 1:
+    if category_acl['can_edit_threads'] == 1:
         if target.starter_id != user.pk:
         if target.starter_id != user.pk:
             raise PermissionDenied(
             raise PermissionDenied(
-                _("You can't edit other users threads in this forum."))
+                _("You can't edit other users threads in this category."))
 
 
-        if not forum_acl['can_close_threads']:
+        if not category_acl['can_close_threads']:
-            if target.forum.is_closed:
+            if target.category.is_closed:
                 raise PermissionDenied(
                 raise PermissionDenied(
-                    _("This forum is closed. You can't edit threads in it."))
+                    _("This category is closed. You can't edit threads in it."))
             if target.is_closed:
             if target.is_closed:
                 raise PermissionDenied(
                 raise PermissionDenied(
-                    _("You can't edit closed threads in this forum."))
+                    _("You can't edit closed threads in this category."))
 
 
         if not has_time_to_edit_thread(user, target):
         if not has_time_to_edit_thread(user, target):
             message = ungettext("You can't edit threads that are "
             message = ungettext("You can't edit threads that are "
                                 "older than %(minutes)s minute.",
                                 "older than %(minutes)s minute.",
                                 "You can't edit threads that are "
                                 "You can't edit threads that are "
                                 "older than %(minutes)s minutes.",
                                 "older than %(minutes)s minutes.",
-                                forum_acl['thread_edit_time'])
+                                category_acl['thread_edit_time'])
             raise PermissionDenied(
             raise PermissionDenied(
-                message % {'minutes': forum_acl['thread_edit_time']})
+                message % {'minutes': category_acl['thread_edit_time']})
 can_edit_thread = return_boolean(allow_edit_thread)
 can_edit_thread = return_boolean(allow_edit_thread)
 
 
 
 
 def allow_see_post(user, target):
 def allow_see_post(user, target):
     if target.is_moderated:
     if target.is_moderated:
-        forum_acl = user.acl['forums'].get(target.forum_id, {})
+        category_acl = user.acl['categories'].get(target.category_id, {})
-        if not forum_acl.get('can_review_moderated_content'):
+        if not category_acl.get('can_review_moderated_content'):
             if user.is_anonymous() or user.pk != target.poster_id:
             if user.is_anonymous() or user.pk != target.poster_id:
                 raise Http404()
                 raise Http404()
 can_see_post = return_boolean(allow_see_post)
 can_see_post = return_boolean(allow_see_post)
@@ -472,28 +473,28 @@ def allow_edit_post(user, target):
     if user.is_anonymous():
     if user.is_anonymous():
         raise PermissionDenied(_("You have to sign in to edit posts."))
         raise PermissionDenied(_("You have to sign in to edit posts."))
 
 
-    forum_acl = target.forum.acl
+    category_acl = target.category.acl
 
 
-    if not forum_acl['can_edit_posts']:
+    if not category_acl['can_edit_posts']:
-        raise PermissionDenied(_("You can't edit posts in this forum."))
+        raise PermissionDenied(_("You can't edit posts in this category."))
 
 
     if target.is_hidden and not can_unhide_post(user, target):
     if target.is_hidden and not can_unhide_post(user, target):
         raise PermissionDenied(_("This post is hidden, you can't edit it."))
         raise PermissionDenied(_("This post is hidden, you can't edit it."))
 
 
-    if forum_acl['can_edit_posts'] == 1:
+    if category_acl['can_edit_posts'] == 1:
         if target.poster_id != user.pk:
         if target.poster_id != user.pk:
             raise PermissionDenied(
             raise PermissionDenied(
-                _("You can't edit other users posts in this forum."))
+                _("You can't edit other users posts in this category."))
 
 
-        if not forum_acl['can_close_threads']:
+        if not category_acl['can_close_threads']:
-            if target.forum.is_closed:
+            if target.category.is_closed:
                 raise PermissionDenied(
                 raise PermissionDenied(
-                    _("This forum is closed. You can't edit posts in it."))
+                    _("This category is closed. You can't edit posts in it."))
             if target.thread.is_closed:
             if target.thread.is_closed:
                 raise PermissionDenied(
                 raise PermissionDenied(
                     _("This thread is closed. You can't edit posts in it."))
                     _("This thread is closed. You can't edit posts in it."))
 
 
-        if target.is_protected and not forum_acl['can_protect_posts']:
+        if target.is_protected and not category_acl['can_protect_posts']:
             raise PermissionDenied(
             raise PermissionDenied(
                 _("This post is protected. You can't edit it."))
                 _("This post is protected. You can't edit it."))
 
 
@@ -502,9 +503,9 @@ def allow_edit_post(user, target):
                                 "older than %(minutes)s minute.",
                                 "older than %(minutes)s minute.",
                                 "You can't edit posts that are "
                                 "You can't edit posts that are "
                                 "older than %(minutes)s minutes.",
                                 "older than %(minutes)s minutes.",
-                                forum_acl['post_edit_time'])
+                                category_acl['post_edit_time'])
             raise PermissionDenied(
             raise PermissionDenied(
-                message % {'minutes': forum_acl['post_edit_time']})
+                message % {'minutes': category_acl['post_edit_time']})
 can_edit_post = return_boolean(allow_edit_post)
 can_edit_post = return_boolean(allow_edit_post)
 
 
 
 
@@ -512,25 +513,26 @@ def allow_unhide_post(user, target):
     if user.is_anonymous():
     if user.is_anonymous():
         raise PermissionDenied(_("You have to sign in to reveal posts."))
         raise PermissionDenied(_("You have to sign in to reveal posts."))
 
 
-    forum_acl = target.forum.acl
+    category_acl = target.category.acl
 
 
-    if not forum_acl['can_hide_posts']:
+    if not category_acl['can_hide_posts']:
-        if not forum_acl['can_hide_own_posts']:
+        if not category_acl['can_hide_own_posts']:
-            raise PermissionDenied(_("You can't reveal posts in this forum."))
+            raise PermissionDenied(
+                _("You can't reveal posts in this category."))
 
 
         if user.id != target.poster_id:
         if user.id != target.poster_id:
             raise PermissionDenied(
             raise PermissionDenied(
-                _("You can't reveal other users posts in this forum."))
+                _("You can't reveal other users posts in this category."))
 
 
-        if not forum_acl['can_close_threads']:
+        if not category_acl['can_close_threads']:
-            if target.forum.is_closed:
+            if target.category.is_closed:
-                raise PermissionDenied(_("This forum is closed. You can't "
+                raise PermissionDenied(_("This category is closed. You can't "
                                          "reveal posts in it."))
                                          "reveal posts in it."))
             if target.thread.is_closed:
             if target.thread.is_closed:
                 raise PermissionDenied(_("This thread is closed. You can't "
                 raise PermissionDenied(_("This thread is closed. You can't "
                                          "reveal posts in it."))
                                          "reveal posts in it."))
 
 
-        if target.is_protected and not forum_acl['can_protect_posts']:
+        if target.is_protected and not category_acl['can_protect_posts']:
             raise PermissionDenied(
             raise PermissionDenied(
                 _("This post is protected. You can't reveal it."))
                 _("This post is protected. You can't reveal it."))
 
 
@@ -539,9 +541,9 @@ def allow_unhide_post(user, target):
                                 "older than %(minutes)s minute.",
                                 "older than %(minutes)s minute.",
                                 "You can't reveal posts that are "
                                 "You can't reveal posts that are "
                                 "older than %(minutes)s minutes.",
                                 "older than %(minutes)s minutes.",
-                                forum_acl['post_edit_time'])
+                                category_acl['post_edit_time'])
             raise PermissionDenied(
             raise PermissionDenied(
-                message % {'minutes': forum_acl['post_edit_time']})
+                message % {'minutes': category_acl['post_edit_time']})
 
 
     if target.id == target.thread.first_post_id:
     if target.id == target.thread.first_post_id:
         raise PermissionDenied(_("You can't reveal thread's first post."))
         raise PermissionDenied(_("You can't reveal thread's first post."))
@@ -554,25 +556,25 @@ def allow_hide_post(user, target):
     if user.is_anonymous():
     if user.is_anonymous():
         raise PermissionDenied(_("You have to sign in to hide posts."))
         raise PermissionDenied(_("You have to sign in to hide posts."))
 
 
-    forum_acl = target.forum.acl
+    category_acl = target.category.acl
 
 
-    if not forum_acl['can_hide_posts']:
+    if not category_acl['can_hide_posts']:
-        if not forum_acl['can_hide_own_posts']:
+        if not category_acl['can_hide_own_posts']:
-            raise PermissionDenied(_("You can't hide posts in this forum."))
+            raise PermissionDenied(_("You can't hide posts in this category."))
 
 
         if user.id != target.poster_id:
         if user.id != target.poster_id:
             raise PermissionDenied(
             raise PermissionDenied(
-                _("You can't hide other users posts in this forum."))
+                _("You can't hide other users posts in this category."))
 
 
-        if not forum_acl['can_close_threads']:
+        if not category_acl['can_close_threads']:
-            if target.forum.is_closed:
+            if target.category.is_closed:
-                raise PermissionDenied(_("This forum is closed. You can't "
+                raise PermissionDenied(_("This category is closed. You can't "
                                          "hide posts in it."))
                                          "hide posts in it."))
             if target.thread.is_closed:
             if target.thread.is_closed:
                 raise PermissionDenied(_("This thread is closed. You can't "
                 raise PermissionDenied(_("This thread is closed. You can't "
                                          "hide posts in it."))
                                          "hide posts in it."))
 
 
-        if target.is_protected and not forum_acl['can_protect_posts']:
+        if target.is_protected and not category_acl['can_protect_posts']:
             raise PermissionDenied(
             raise PermissionDenied(
                 _("This post is protected. You can't hide it."))
                 _("This post is protected. You can't hide it."))
 
 
@@ -581,9 +583,9 @@ def allow_hide_post(user, target):
                                 "older than %(minutes)s minute.",
                                 "older than %(minutes)s minute.",
                                 "You can't hide posts that are "
                                 "You can't hide posts that are "
                                 "older than %(minutes)s minutes.",
                                 "older than %(minutes)s minutes.",
-                                forum_acl['post_edit_time'])
+                                category_acl['post_edit_time'])
             raise PermissionDenied(
             raise PermissionDenied(
-                message % {'minutes': forum_acl['post_edit_time']})
+                message % {'minutes': category_acl['post_edit_time']})
 
 
     if target.id == target.thread.first_post_id:
     if target.id == target.thread.first_post_id:
         raise PermissionDenied(_("You can't hide thread's first post."))
         raise PermissionDenied(_("You can't hide thread's first post."))
@@ -596,25 +598,26 @@ def allow_delete_post(user, target):
     if user.is_anonymous():
     if user.is_anonymous():
         raise PermissionDenied(_("You have to sign in to delete posts."))
         raise PermissionDenied(_("You have to sign in to delete posts."))
 
 
-    forum_acl = target.forum.acl
+    category_acl = target.category.acl
 
 
-    if forum_acl['can_hide_posts'] != 2:
+    if category_acl['can_hide_posts'] != 2:
-        if not forum_acl['can_hide_own_posts'] != 2:
+        if not category_acl['can_hide_own_posts'] != 2:
-            raise PermissionDenied(_("You can't delete posts in this forum."))
+            raise PermissionDenied(
+                _("You can't delete posts in this category."))
 
 
         if user.id != target.poster_id:
         if user.id != target.poster_id:
             raise PermissionDenied(
             raise PermissionDenied(
-                _("You can't delete other users posts in this forum."))
+                _("You can't delete other users posts in this category."))
 
 
-        if not forum_acl['can_close_threads']:
+        if not category_acl['can_close_threads']:
-            if target.forum.is_closed:
+            if target.category.is_closed:
-                raise PermissionDenied(_("This forum is closed. You can't "
+                raise PermissionDenied(_("This category is closed. You can't "
                                          "delete posts from it."))
                                          "delete posts from it."))
             if target.thread.is_closed:
             if target.thread.is_closed:
                 raise PermissionDenied(_("This thread is closed. You can't "
                 raise PermissionDenied(_("This thread is closed. You can't "
                                          "delete posts from it."))
                                          "delete posts from it."))
 
 
-        if target.is_protected and not forum_acl['can_protect_posts']:
+        if target.is_protected and not category_acl['can_protect_posts']:
             raise PermissionDenied(
             raise PermissionDenied(
                 _("This post is protected. You can't delete it."))
                 _("This post is protected. You can't delete it."))
 
 
@@ -623,9 +626,9 @@ def allow_delete_post(user, target):
                                 "older than %(minutes)s minute.",
                                 "older than %(minutes)s minute.",
                                 "You can't delete posts that are "
                                 "You can't delete posts that are "
                                 "older than %(minutes)s minutes.",
                                 "older than %(minutes)s minutes.",
-                                forum_acl['post_edit_time'])
+                                category_acl['post_edit_time'])
             raise PermissionDenied(
             raise PermissionDenied(
-                message % {'minutes': forum_acl['post_edit_time']})
+                message % {'minutes': category_acl['post_edit_time']})
 
 
     if target.id == target.thread.first_post_id:
     if target.id == target.thread.first_post_id:
         raise PermissionDenied(_("You can't delete thread's first post."))
         raise PermissionDenied(_("You can't delete thread's first post."))
@@ -636,12 +639,12 @@ can_delete_post = return_boolean(allow_delete_post)
 Permission check helpers
 Permission check helpers
 """
 """
 def can_change_owned_thread(user, target):
 def can_change_owned_thread(user, target):
-    forum_acl = user.acl['forums'].get(target.forum_id, {})
+    category_acl = user.acl['categories'].get(target.category_id, {})
 
 
     if user.is_anonymous() or user.pk != target.starter_id:
     if user.is_anonymous() or user.pk != target.starter_id:
         return False
         return False
 
 
-    if target.forum.is_closed or target.is_closed:
+    if target.category.is_closed or target.is_closed:
         return False
         return False
 
 
     if target.first_post.is_protected:
     if target.first_post.is_protected:
@@ -651,23 +654,23 @@ def can_change_owned_thread(user, target):
 
 
 
 
 def has_time_to_edit_thread(user, target):
 def has_time_to_edit_thread(user, target):
-    forum_acl = user.acl['forums'].get(target.forum_id, {})
+    category_acl = user.acl['categories'].get(target.category_id, {})
-    if forum_acl.get('thread_edit_time'):
+    if category_acl.get('thread_edit_time'):
         diff = timezone.now() - target.started_on
         diff = timezone.now() - target.started_on
         diff_minutes = int(diff.total_seconds() / 60)
         diff_minutes = int(diff.total_seconds() / 60)
 
 
-        return diff_minutes < forum_acl.get('thread_edit_time')
+        return diff_minutes < category_acl.get('thread_edit_time')
     else:
     else:
         return True
         return True
 
 
 
 
 def has_time_to_edit_post(user, target):
 def has_time_to_edit_post(user, target):
-    forum_acl = user.acl['forums'].get(target.forum_id, {})
+    category_acl = user.acl['categories'].get(target.category_id, {})
-    if forum_acl.get('post_edit_time'):
+    if category_acl.get('post_edit_time'):
         diff = timezone.now() - target.posted_on
         diff = timezone.now() - target.posted_on
         diff_minutes = int(diff.total_seconds() / 60)
         diff_minutes = int(diff.total_seconds() / 60)
 
 
-        return diff_minutes < forum_acl.get('post_edit_time')
+        return diff_minutes < category_acl.get('post_edit_time')
     else:
     else:
         return True
         return True
 
 
@@ -675,19 +678,19 @@ def has_time_to_edit_post(user, target):
 """
 """
 Queryset helpers
 Queryset helpers
 """
 """
-def exclude_invisible_threads(queryset, user, forum=None):
+def exclude_invisible_threads(queryset, user, category=None):
-    if forum:
+    if category:
-        return exclude_invisible_forum_threads(queryset, user, forum)
+        return exclude_invisible_category_threads(queryset, user, category)
     else:
     else:
         return exclude_all_invisible_threads(queryset, user)
         return exclude_all_invisible_threads(queryset, user)
 
 
 
 
-def exclude_invisible_forum_threads(queryset, user, forum):
+def exclude_invisible_category_threads(queryset, user, category):
     if user.is_authenticated():
     if user.is_authenticated():
         condition_author = Q(starter_id=user.id)
         condition_author = Q(starter_id=user.id)
 
 
-        can_mod = forum.acl['can_review_moderated_content']
+        can_mod = category.acl['can_review_moderated_content']
-        can_hide = forum.acl['can_hide_threads']
+        can_hide = category.acl['can_hide_threads']
 
 
         if not can_mod and not can_hide:
         if not can_mod and not can_hide:
             condition = Q(is_moderated=False) & Q(is_hidden=False)
             condition = Q(is_moderated=False) & Q(is_hidden=False)
@@ -699,28 +702,28 @@ def exclude_invisible_forum_threads(queryset, user, forum):
             condition = Q(is_hidden=False)
             condition = Q(is_hidden=False)
             queryset = queryset.filter(condition_author | condition)
             queryset = queryset.filter(condition_author | condition)
     else:
     else:
-        if not forum.acl['can_review_moderated_content']:
+        if not category.acl['can_review_moderated_content']:
             queryset = queryset.filter(is_moderated=False)
             queryset = queryset.filter(is_moderated=False)
-        if not forum.acl['can_hide_threads']:
+        if not category.acl['can_hide_threads']:
             queryset = queryset.filter(is_hidden=False)
             queryset = queryset.filter(is_hidden=False)
 
 
     return queryset
     return queryset
 
 
 
 
 def exclude_all_invisible_threads(queryset, user):
 def exclude_all_invisible_threads(queryset, user):
-    forums_in = []
+    categories_in = []
     conditions = None
     conditions = None
 
 
-    for forum in Forum.objects.all_forums():
+    for category in Category.objects.all_categories():
-        add_acl(user, forum)
+        add_acl(user, category)
 
 
-        condition_forum = Q(forum=forum)
+        condition_category = Q(category=category)
         condition_author = Q(starter_id=user.id)
         condition_author = Q(starter_id=user.id)
 
 
         # can see all threads?
         # can see all threads?
-        if forum.acl['can_see_all_threads']:
+        if category.acl['can_see_all_threads']:
-            can_mod = forum.acl['can_review_moderated_content']
+            can_mod = category.acl['can_review_moderated_content']
-            can_hide = forum.acl['can_hide_threads']
+            can_hide = category.acl['can_hide_threads']
 
 
             if not can_mod or not can_hide:
             if not can_mod or not can_hide:
                 if not can_mod and not can_hide:
                 if not can_mod and not can_hide:
@@ -730,32 +733,32 @@ def exclude_all_invisible_threads(queryset, user):
                 elif not can_hide:
                 elif not can_hide:
                     condition = Q(is_hidden=False)
                     condition = Q(is_hidden=False)
                 visibility_condition = condition_author | condition
                 visibility_condition = condition_author | condition
-                visibility_condition = condition_forum & visibility_condition
+                visibility_condition = condition_category & visibility_condition
             else:
             else:
                 # user can see everything so don't bother with rest of routine
                 # user can see everything so don't bother with rest of routine
-                forums_in.append(forum.pk)
+                categories_in.append(category.pk)
                 continue
                 continue
         else:
         else:
-            # show all threads in forum made by user
+            # show all threads in category made by user
-            visibility_condition = condition_forum & condition_author
+            visibility_condition = condition_category & condition_author
 
 
         if conditions:
         if conditions:
             conditions = conditions | visibility_condition
             conditions = conditions | visibility_condition
         else:
         else:
             conditions = visibility_condition
             conditions = visibility_condition
 
 
-    if conditions and forums_in:
+    if conditions and categories_in:
-        return queryset.filter(Q(forum_id__in=forums_in) | conditions)
+        return queryset.filter(Q(category_id__in=categories_in) | conditions)
     elif conditions:
     elif conditions:
         return queryset.filter(conditions)
         return queryset.filter(conditions)
-    elif forums_in:
+    elif categories_in:
-        return queryset.filter(forum_id__in=forums_in)
+        return queryset.filter(category_id__in=categories_in)
     else:
     else:
         return Thread.objects.none()
         return Thread.objects.none()
 
 
 
 
-def exclude_invisible_posts(queryset, user, forum):
+def exclude_invisible_posts(queryset, user, category):
-    if not forum.acl['can_review_moderated_content']:
+    if not category.acl['can_review_moderated_content']:
         if user.is_authenticated():
         if user.is_authenticated():
             condition_author = Q(poster_id=user.id)
             condition_author = Q(poster_id=user.id)
             condition = Q(is_moderated=False)
             condition = Q(is_moderated=False)

+ 1 - 1
misago/threads/posting/__init__.py

@@ -28,7 +28,7 @@ class EditorFormset(object):
         self._forms_list = []
         self._forms_list = []
         self._forms_dict = {}
         self._forms_dict = {}
 
 
-        is_private = kwargs['forum'].special_role == "private_threads"
+        is_private = kwargs['category'].special_role == 'private_threads'
         kwargs['is_private'] = is_private
         kwargs['is_private'] = is_private
 
 
         self.kwargs = kwargs
         self.kwargs = kwargs

+ 3 - 4
misago/threads/posting/savechanges.py

@@ -1,5 +1,4 @@
 from collections import OrderedDict
 from collections import OrderedDict
-
 from misago.threads.posting import PostingMiddleware
 from misago.threads.posting import PostingMiddleware
 
 
 
 
@@ -10,18 +9,18 @@ class SaveChangesMiddleware(PostingMiddleware):
 
 
     def reset_state(self):
     def reset_state(self):
         self.user.update_all = False
         self.user.update_all = False
-        self.forum.update_all = False
+        self.category.update_all = False
         self.thread.update_all = False
         self.thread.update_all = False
         self.post.update_all = False
         self.post.update_all = False
 
 
         self.user.update_fields = []
         self.user.update_fields = []
-        self.forum.update_fields = []
+        self.category.update_fields = []
         self.thread.update_fields = []
         self.thread.update_fields = []
         self.post.update_fields = []
         self.post.update_fields = []
 
 
     def save_models(self):
     def save_models(self):
         self.save_model(self.user)
         self.save_model(self.user)
-        self.save_model(self.forum)
+        self.save_model(self.category)
         self.save_model(self.thread)
         self.save_model(self.thread)
         self.save_model(self.post)
         self.save_model(self.post)
         self.reset_state()
         self.reset_state()

+ 1 - 1
misago/threads/posting/threadclose.py

@@ -5,7 +5,7 @@ from misago.threads.posting import PostingMiddleware, START
 
 
 class ThreadCloseFormMiddleware(PostingMiddleware):
 class ThreadCloseFormMiddleware(PostingMiddleware):
     def use_this_middleware(self):
     def use_this_middleware(self):
-        if self.forum.acl['can_close_threads']:
+        if self.category.acl['can_close_threads']:
             self.is_closed = self.thread.is_closed
             self.is_closed = self.thread.is_closed
             return True
             return True
         else:
         else:

+ 4 - 3
misago/threads/posting/threadlabel.py

@@ -7,7 +7,8 @@ from misago.threads.posting import PostingMiddleware, START, EDIT
 
 
 class ThreadLabelFormMiddleware(PostingMiddleware):
 class ThreadLabelFormMiddleware(PostingMiddleware):
     def use_this_middleware(self):
     def use_this_middleware(self):
-        if self.forum.labels and self.forum.acl['can_change_threads_labels']:
+        if (self.category.labels and
+                self.category.acl['can_change_threads_labels']):
             self.label_id = self.thread.label_id
             self.label_id = self.thread.label_id
 
 
             if self.mode == START:
             if self.mode == START:
@@ -21,11 +22,11 @@ class ThreadLabelFormMiddleware(PostingMiddleware):
     def make_form(self):
     def make_form(self):
         if self.request.method == 'POST':
         if self.request.method == 'POST':
             return ThreadLabelForm(self.request.POST, prefix=self.prefix,
             return ThreadLabelForm(self.request.POST, prefix=self.prefix,
-                                    labels=self.forum.labels)
+                                    labels=self.category.labels)
         else:
         else:
             initial = {'label_id': self.label_id}
             initial = {'label_id': self.label_id}
             return ThreadLabelForm(prefix=self.prefix,
             return ThreadLabelForm(prefix=self.prefix,
-                                    labels=self.forum.labels,
+                                    labels=self.category.labels,
                                     initial=initial)
                                     initial=initial)
 
 
     def pre_save(self, form):
     def pre_save(self, form):

+ 1 - 1
misago/threads/posting/threadpin.py

@@ -5,7 +5,7 @@ from misago.threads.posting import PostingMiddleware, START
 
 
 class ThreadPinFormMiddleware(PostingMiddleware):
 class ThreadPinFormMiddleware(PostingMiddleware):
     def use_this_middleware(self):
     def use_this_middleware(self):
-        if self.forum.acl['can_pin_threads']:
+        if self.category.acl['can_pin_threads']:
             self.is_pinned = self.thread.is_pinned
             self.is_pinned = self.thread.is_pinned
             return True
             return True
         else:
         else:

+ 6 - 7
misago/threads/posting/updatestats.py

@@ -1,22 +1,21 @@
 from django.db.models import F
 from django.db.models import F
-
 from misago.threads.posting import PostingMiddleware, START, REPLY, EDIT
 from misago.threads.posting import PostingMiddleware, START, REPLY, EDIT
 
 
 
 
 class UpdateStatsMiddleware(PostingMiddleware):
 class UpdateStatsMiddleware(PostingMiddleware):
     def save(self, form):
     def save(self, form):
         self.update_thread()
         self.update_thread()
-        self.update_forum()
+        self.update_category()
         self.update_user()
         self.update_user()
 
 
-    def update_forum(self):
+    def update_category(self):
         if self.mode == START:
         if self.mode == START:
-            self.forum.threads = F('threads') + 1
+            self.category.threads = F('threads') + 1
 
 
         if self.mode != EDIT:
         if self.mode != EDIT:
-            self.forum.set_last_thread(self.thread)
+            self.category.set_last_thread(self.thread)
-            self.forum.posts = F('posts') + 1
+            self.category.posts = F('posts') + 1
-            self.forum.update_all = True
+            self.category.update_all = True
 
 
     def update_thread(self):
     def update_thread(self):
         if self.mode == START:
         if self.mode == START:

+ 1 - 1
misago/threads/reports.py

@@ -39,7 +39,7 @@ def report_post(request, post, message):
         message = message['parsed_text']
         message = message['parsed_text']
 
 
     report = Report.objects.create(
     report = Report.objects.create(
-        forum=post.forum,
+        category=post.category,
         thread=post.thread,
         thread=post.thread,
         post=post,
         post=post,
         reported_by=request.user,
         reported_by=request.user,

+ 28 - 27
misago/threads/signals.py

@@ -1,8 +1,8 @@
 from django.db import transaction
 from django.db import transaction
 from django.dispatch import receiver, Signal
 from django.dispatch import receiver, Signal
 
 
+from misago.categories.models import Category
 from misago.core.pgutils import batch_update, batch_delete
 from misago.core.pgutils import batch_update, batch_delete
-from misago.forums.models import Forum
 
 
 from misago.threads.models import Thread, Post, Event, Label
 from misago.threads.models import Thread, Post, Event, Label
 
 
@@ -22,58 +22,59 @@ Signal handlers
 @receiver(merge_thread)
 @receiver(merge_thread)
 def merge_threads_posts(sender, **kwargs):
 def merge_threads_posts(sender, **kwargs):
     other_thread = kwargs['other_thread']
     other_thread = kwargs['other_thread']
-    other_thread.post_set.update(forum=sender.forum, thread=sender)
+    other_thread.post_set.update(category=sender.category, thread=sender)
 
 
 
 
 @receiver(move_thread)
 @receiver(move_thread)
 def move_thread_content(sender, **kwargs):
 def move_thread_content(sender, **kwargs):
-    sender.post_set.update(forum=sender.forum)
+    sender.post_set.update(category=sender.category)
-    sender.event_set.update(forum=sender.forum)
+    sender.event_set.update(category=sender.category)
 
 
     # remove unavailable labels
     # remove unavailable labels
     if sender.label_id:
     if sender.label_id:
-        new_forum_labels = Label.objects.get_forum_labels(sender.forum)
+        new_category_labels = Label.objects.get_category_labels(sender.category)
-        if sender.label_id not in [l.pk for l in new_forum_labels]:
+        if sender.label_id not in [l.pk for l in new_category_labels]:
             sender.label = None
             sender.label = None
 
 
 
 
-from misago.forums.signals import delete_forum_content, move_forum_content
+from misago.categories.signals import (delete_category_content,
-@receiver(delete_forum_content)
+                                       move_category_content)
-def delete_forum_threads(sender, **kwargs):
+@receiver(delete_category_content)
+def delete_category_threads(sender, **kwargs):
     sender.event_set.all().delete()
     sender.event_set.all().delete()
     sender.thread_set.all().delete()
     sender.thread_set.all().delete()
     sender.post_set.all().delete()
     sender.post_set.all().delete()
 
 
 
 
-@receiver(move_forum_content)
+@receiver(move_category_content)
-def move_forum_threads(sender, **kwargs):
+def move_category_threads(sender, **kwargs):
-    new_forum = kwargs['new_forum']
+    new_category = kwargs['new_category']
-    Thread.objects.filter(forum=sender).update(forum=new_forum)
+    Thread.objects.filter(category=sender).update(category=new_category)
-    Post.objects.filter(forum=sender).update(forum=new_forum)
+    Post.objects.filter(category=sender).update(category=new_category)
-    Event.objects.filter(forum=sender).update(forum=new_forum)
+    Event.objects.filter(category=sender).update(category=new_category)
 
 
     # move labels
     # move labels
-    old_forum_labels = Label.objects.get_forum_labels(sender)
+    old_category_labels = Label.objects.get_category_labels(sender)
-    new_forum_labels = Label.objects.get_forum_labels(new_forum)
+    new_category_labels = Label.objects.get_category_labels(new_category)
 
 
-    for label in old_forum_labels:
+    for label in old_category_labels:
-        if label not in new_forum_labels:
+        if label not in new_category_labels:
-            label.forums.add(new_forum_labels)
+            label.categories.add(new_category_labels)
 
 
 
 
 from misago.users.signals import delete_user_content, username_changed
 from misago.users.signals import delete_user_content, username_changed
 @receiver(delete_user_content)
 @receiver(delete_user_content)
 def delete_user_threads(sender, **kwargs):
 def delete_user_threads(sender, **kwargs):
-    recount_forums = set()
+    recount_categories = set()
     recount_threads = set()
     recount_threads = set()
 
 
     for thread in batch_delete(sender.thread_set.all(), 50):
     for thread in batch_delete(sender.thread_set.all(), 50):
-        recount_forums.add(thread.forum_id)
+        recount_categories.add(thread.category_id)
         with transaction.atomic():
         with transaction.atomic():
             thread.delete()
             thread.delete()
 
 
     for post in batch_delete(sender.post_set.all(), 50):
     for post in batch_delete(sender.post_set.all(), 50):
-        recount_forums.add(post.forum_id)
+        recount_categories.add(post.category_id)
         recount_threads.add(post.thread_id)
         recount_threads.add(post.thread_id)
         with transaction.atomic():
         with transaction.atomic():
             post.delete()
             post.delete()
@@ -84,10 +85,10 @@ def delete_user_threads(sender, **kwargs):
             thread.synchronize()
             thread.synchronize()
             thread.save()
             thread.save()
 
 
-    if recount_forums:
+    if recount_categories:
-        for forum in Forum.objects.filter(id__in=recount_forums):
+        for category in Category.objects.filter(id__in=recount_categories):
-            forum.synchronize()
+            category.synchronize()
-            forum.save()
+            category.save()
 
 
 
 
 @receiver(username_changed)
 @receiver(username_changed)

+ 59 - 58
misago/threads/tests/-test_editpost_view.py

@@ -4,7 +4,7 @@ from django.conf import settings
 from django.core.urlresolvers import reverse
 from django.core.urlresolvers import reverse
 
 
 from misago.acl.testutils import override_acl
 from misago.acl.testutils import override_acl
-from misago.forums.models import Forum
+from misago.categories.models import Category
 from misago.users.testutils import AuthenticatedUserTestCase
 from misago.users.testutils import AuthenticatedUserTestCase
 
 
 from misago.threads.models import Label, Thread, Post
 from misago.threads.models import Label, Thread, Post
@@ -17,10 +17,11 @@ class EditPostTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
         super(EditPostTests, self).setUp()
         super(EditPostTests, self).setUp()
 
 
-        self.forum = Forum.objects.all_forums().filter(role="forum")[:1][0]
+        self.category = Category.objects.all_categories().filter(
-        self.thread = post_thread(self.forum, poster=self.user)
+            role='category')[:1][0]
+        self.thread = post_thread(self.category, poster=self.user)
         self.link = reverse('misago:edit_post', kwargs={
         self.link = reverse('misago:edit_post', kwargs={
-            'forum_id': self.forum.id,
+            'category_id': self.category.id,
             'thread_id': self.thread.id,
             'thread_id': self.thread.id,
             'post_id': self.thread.first_post_id,
             'post_id': self.thread.first_post_id,
         })
         })
@@ -30,69 +31,69 @@ class EditPostTests(AuthenticatedUserTestCase):
     def tearDown(self):
     def tearDown(self):
         Label.objects.clear_cache()
         Label.objects.clear_cache()
 
 
-    def override_forum_acl(self, extra_acl=None):
+    def override_category_acl(self, extra_acl=None):
-        forums_acl = self.user.acl
+        categories_acl = self.user.acl
-        forums_acl['visible_forums'].append(self.forum.pk)
+        categories_acl['visible_categories'].append(self.category.pk)
-        forums_acl['forums'][self.forum.pk] = {
+        categories_acl['categories'][self.category.pk] = {
             'can_see': 1,
             'can_see': 1,
             'can_browse': 1,
             'can_browse': 1,
             'can_see_all_threads': 1,
             'can_see_all_threads': 1,
         }
         }
         if extra_acl:
         if extra_acl:
-            forums_acl['forums'][self.forum.pk].update(extra_acl)
+            categories_acl['categories'][self.category.pk].update(extra_acl)
-        override_acl(self.user, forums_acl)
+        override_acl(self.user, categories_acl)
 
 
     def test_cant_see(self):
     def test_cant_see(self):
-        """has no permission to see forum"""
+        """has no permission to see category"""
-        forums_acl = self.user.acl
+        categories_acl = self.user.acl
-        forums_acl['visible_forums'].remove(self.forum.pk)
+        categories_acl['visible_categories'].remove(self.category.pk)
-        forums_acl['forums'][self.forum.pk] = {
+        categories_acl['categories'][self.category.pk] = {
             'can_see': 0,
             'can_see': 0,
             'can_browse': 0,
             'can_browse': 0,
             'can_see_all_threads': 1,
             'can_see_all_threads': 1,
             'can_reply_threads': 1,
             'can_reply_threads': 1,
         }
         }
-        override_acl(self.user, forums_acl)
+        override_acl(self.user, categories_acl)
 
 
         response = self.client.get(self.link, **self.ajax_header)
         response = self.client.get(self.link, **self.ajax_header)
         self.assertEqual(response.status_code, 404)
         self.assertEqual(response.status_code, 404)
 
 
     def test_cant_browse(self):
     def test_cant_browse(self):
-        """has no permission to browse forum"""
+        """has no permission to browse category"""
-        forums_acl = self.user.acl
+        categories_acl = self.user.acl
-        forums_acl['visible_forums'].append(self.forum.pk)
+        categories_acl['visible_categories'].append(self.category.pk)
-        forums_acl['forums'][self.forum.pk] = {
+        categories_acl['categories'][self.category.pk] = {
             'can_see': 1,
             'can_see': 1,
             'can_browse': 0,
             'can_browse': 0,
             'can_see_all_threads': 1,
             'can_see_all_threads': 1,
             'can_reply_threads': 1,
             'can_reply_threads': 1,
         }
         }
-        override_acl(self.user, forums_acl)
+        override_acl(self.user, categories_acl)
 
 
         response = self.client.get(self.link, **self.ajax_header)
         response = self.client.get(self.link, **self.ajax_header)
         self.assertEqual(response.status_code, 404)
         self.assertEqual(response.status_code, 404)
 
 
-    def test_cant_edit_own_post_in_locked_forum(self):
+    def test_cant_edit_own_post_in_locked_category(self):
-        """can't edit own post in closed forum"""
+        """can't edit own post in closed category"""
-        self.forum.is_closed = True
+        self.category.is_closed = True
-        self.forum.save()
+        self.category.save()
 
 
-        self.override_forum_acl({'can_edit_threads': 1})
+        self.override_category_acl({'can_edit_threads': 1})
 
 
         response = self.client.get(self.link, **self.ajax_header)
         response = self.client.get(self.link, **self.ajax_header)
         self.assertEqual(response.status_code, 403)
         self.assertEqual(response.status_code, 403)
 
 
-    def test_cant_edit_other_user_post_in_locked_forum(self):
+    def test_cant_edit_other_user_post_in_locked_category(self):
-        """can't edit other user post in closed forum"""
+        """can't edit other user post in closed category"""
-        self.forum.is_closed = True
+        self.category.is_closed = True
-        self.forum.save()
+        self.category.save()
 
 
         self.thread.first_post.poster = None
         self.thread.first_post.poster = None
         self.thread.first_post.save()
         self.thread.first_post.save()
         self.thread.synchronize()
         self.thread.synchronize()
         self.thread.save()
         self.thread.save()
 
 
-        self.override_forum_acl({'can_edit_threads': 2})
+        self.override_category_acl({'can_edit_threads': 2})
 
 
         response = self.client.get(self.link, **self.ajax_header)
         response = self.client.get(self.link, **self.ajax_header)
         self.assertEqual(response.status_code, 403)
         self.assertEqual(response.status_code, 403)
@@ -102,14 +103,14 @@ class EditPostTests(AuthenticatedUserTestCase):
         self.thread.is_closed = True
         self.thread.is_closed = True
         self.thread.save()
         self.thread.save()
 
 
-        self.override_forum_acl({'can_edit_threads': 1})
+        self.override_category_acl({'can_edit_threads': 1})
 
 
         response = self.client.get(self.link, **self.ajax_header)
         response = self.client.get(self.link, **self.ajax_header)
         self.assertEqual(response.status_code, 403)
         self.assertEqual(response.status_code, 403)
 
 
     def test_cant_edit_other_user_post_in_locked_thread(self):
     def test_cant_edit_other_user_post_in_locked_thread(self):
         """can't edit other user post in closed thread"""
         """can't edit other user post in closed thread"""
-        self.override_forum_acl({'can_edit_threads': 2})
+        self.override_category_acl({'can_edit_threads': 2})
 
 
         self.thread.first_post.poster = None
         self.thread.first_post.poster = None
         self.thread.first_post.save()
         self.thread.first_post.save()
@@ -122,14 +123,14 @@ class EditPostTests(AuthenticatedUserTestCase):
 
 
     def test_cant_edit_own_post(self):
     def test_cant_edit_own_post(self):
         """can't edit own post"""
         """can't edit own post"""
-        self.override_forum_acl({'can_edit_posts': 0})
+        self.override_category_acl({'can_edit_posts': 0})
 
 
         response = self.client.get(self.link, **self.ajax_header)
         response = self.client.get(self.link, **self.ajax_header)
         self.assertEqual(response.status_code, 403)
         self.assertEqual(response.status_code, 403)
 
 
     def test_cant_edit_other_user_post(self):
     def test_cant_edit_other_user_post(self):
         """can't edit other user post"""
         """can't edit other user post"""
-        self.override_forum_acl({'can_edit_posts': 1})
+        self.override_category_acl({'can_edit_posts': 1})
 
 
         self.thread.first_post.poster = None
         self.thread.first_post.poster = None
         self.thread.first_post.save()
         self.thread.first_post.save()
@@ -141,7 +142,7 @@ class EditPostTests(AuthenticatedUserTestCase):
 
 
     def test_cant_edit_protected_post(self):
     def test_cant_edit_protected_post(self):
         """can't edit post that was protected by moderator"""
         """can't edit post that was protected by moderator"""
-        self.override_forum_acl({'can_edit_posts': 1, 'can_protect_posts': 0})
+        self.override_category_acl({'can_edit_posts': 1, 'can_protect_posts': 0})
 
 
         self.thread.first_post.is_protected = True
         self.thread.first_post.is_protected = True
         self.thread.first_post.save()
         self.thread.first_post.save()
@@ -151,13 +152,13 @@ class EditPostTests(AuthenticatedUserTestCase):
 
 
     def test_can_edit_own_post(self):
     def test_can_edit_own_post(self):
         """can edit own post"""
         """can edit own post"""
-        self.override_forum_acl({'can_edit_posts': 1, 'can_edit_threads': 0})
+        self.override_category_acl({'can_edit_posts': 1, 'can_edit_threads': 0})
 
 
         response = self.client.get(self.link, **self.ajax_header)
         response = self.client.get(self.link, **self.ajax_header)
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
         self.assertNotIn('thread-title', response.content)
         self.assertNotIn('thread-title', response.content)
 
 
-        self.override_forum_acl({'can_edit_posts': 1, 'can_edit_threads': 0})
+        self.override_category_acl({'can_edit_posts': 1, 'can_edit_threads': 0})
         response = self.client.post(self.link, data={
         response = self.client.post(self.link, data={
             'post': 'Edited reply!',
             'post': 'Edited reply!',
             'submit': True,
             'submit': True,
@@ -174,7 +175,7 @@ class EditPostTests(AuthenticatedUserTestCase):
 
 
     def test_empty_edit_form(self):
     def test_empty_edit_form(self):
         """empty edit form has no crashes"""
         """empty edit form has no crashes"""
-        self.override_forum_acl({'can_edit_posts': 2, 'can_edit_threads': 2})
+        self.override_category_acl({'can_edit_posts': 2, 'can_edit_threads': 2})
 
 
         response = self.client.post(self.link, data={
         response = self.client.post(self.link, data={
             'submit': True,
             'submit': True,
@@ -184,7 +185,7 @@ class EditPostTests(AuthenticatedUserTestCase):
 
 
     def test_can_edit_other_user_post(self):
     def test_can_edit_other_user_post(self):
         """can edit other user post"""
         """can edit other user post"""
-        self.override_forum_acl({'can_edit_posts': 2, 'can_edit_threads': 0})
+        self.override_category_acl({'can_edit_posts': 2, 'can_edit_threads': 0})
 
 
         self.thread.first_post.poster = None
         self.thread.first_post.poster = None
         self.thread.first_post.save()
         self.thread.first_post.save()
@@ -195,7 +196,7 @@ class EditPostTests(AuthenticatedUserTestCase):
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
         self.assertNotIn('thread-title', response.content)
         self.assertNotIn('thread-title', response.content)
 
 
-        self.override_forum_acl({'can_edit_posts': 2, 'can_edit_threads': 0})
+        self.override_category_acl({'can_edit_posts': 2, 'can_edit_threads': 0})
         response = self.client.post(self.link, data={
         response = self.client.post(self.link, data={
             'post': 'Edited reply!',
             'post': 'Edited reply!',
             'submit': True,
             'submit': True,
@@ -212,13 +213,13 @@ class EditPostTests(AuthenticatedUserTestCase):
 
 
     def test_can_edit_own_thread(self):
     def test_can_edit_own_thread(self):
         """can edit own thread"""
         """can edit own thread"""
-        self.override_forum_acl({'can_edit_posts': 1, 'can_edit_threads': 1})
+        self.override_category_acl({'can_edit_posts': 1, 'can_edit_threads': 1})
 
 
         response = self.client.get(self.link, **self.ajax_header)
         response = self.client.get(self.link, **self.ajax_header)
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
         self.assertIn('thread-title', response.content)
         self.assertIn('thread-title', response.content)
 
 
-        self.override_forum_acl({'can_edit_posts': 1, 'can_edit_threads': 1})
+        self.override_category_acl({'can_edit_posts': 1, 'can_edit_threads': 1})
         response = self.client.post(self.link, data={
         response = self.client.post(self.link, data={
             'title': 'Edited title!',
             'title': 'Edited title!',
             'post': self.thread.first_post.original,
             'post': self.thread.first_post.original,
@@ -239,7 +240,7 @@ class EditPostTests(AuthenticatedUserTestCase):
 
 
     def test_can_edit_other_user_thread(self):
     def test_can_edit_other_user_thread(self):
         """can edit other user thread"""
         """can edit other user thread"""
-        self.override_forum_acl({'can_edit_posts': 2, 'can_edit_threads': 2})
+        self.override_category_acl({'can_edit_posts': 2, 'can_edit_threads': 2})
 
 
         self.thread.first_post.poster = None
         self.thread.first_post.poster = None
         self.thread.first_post.save()
         self.thread.first_post.save()
@@ -250,7 +251,7 @@ class EditPostTests(AuthenticatedUserTestCase):
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
         self.assertIn('thread-title', response.content)
         self.assertIn('thread-title', response.content)
 
 
-        self.override_forum_acl({'can_edit_posts': 2, 'can_edit_threads': 2})
+        self.override_category_acl({'can_edit_posts': 2, 'can_edit_threads': 2})
         response = self.client.post(self.link, data={
         response = self.client.post(self.link, data={
             'title': 'Edited title!',
             'title': 'Edited title!',
             'post': self.thread.first_post.original,
             'post': self.thread.first_post.original,
@@ -271,13 +272,13 @@ class EditPostTests(AuthenticatedUserTestCase):
 
 
     def test_no_change_edit(self):
     def test_no_change_edit(self):
         """user edited post but submited no changes"""
         """user edited post but submited no changes"""
-        self.override_forum_acl({'can_edit_posts': 1, 'can_edit_threads': 1})
+        self.override_category_acl({'can_edit_posts': 1, 'can_edit_threads': 1})
 
 
         response = self.client.get(self.link, **self.ajax_header)
         response = self.client.get(self.link, **self.ajax_header)
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
         self.assertIn('thread-title', response.content)
         self.assertIn('thread-title', response.content)
 
 
-        self.override_forum_acl({'can_edit_posts': 1, 'can_edit_threads': 1})
+        self.override_category_acl({'can_edit_posts': 1, 'can_edit_threads': 1})
         response = self.client.post(self.link, data={
         response = self.client.post(self.link, data={
             'title': self.thread.title,
             'title': self.thread.title,
             'post': self.thread.first_post.original,
             'post': self.thread.first_post.original,
@@ -297,13 +298,13 @@ class EditPostTests(AuthenticatedUserTestCase):
         """user edited post to close and open thread"""
         """user edited post to close and open thread"""
         prefix = 'misago.threads.posting.threadclose.ThreadCloseFormMiddleware'
         prefix = 'misago.threads.posting.threadclose.ThreadCloseFormMiddleware'
         field_name = '%s-is_closed' % prefix
         field_name = '%s-is_closed' % prefix
-        self.override_forum_acl({'can_edit_posts': 1, 'can_close_threads': 1})
+        self.override_category_acl({'can_edit_posts': 1, 'can_close_threads': 1})
 
 
         response = self.client.get(self.link, **self.ajax_header)
         response = self.client.get(self.link, **self.ajax_header)
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
         self.assertIn(field_name, response.content)
         self.assertIn(field_name, response.content)
 
 
-        self.override_forum_acl({'can_edit_posts': 1, 'can_close_threads': 1})
+        self.override_category_acl({'can_edit_posts': 1, 'can_close_threads': 1})
         response = self.client.post(self.link, data={
         response = self.client.post(self.link, data={
             'post': self.thread.first_post.original,
             'post': self.thread.first_post.original,
             field_name: 1,
             field_name: 1,
@@ -321,7 +322,7 @@ class EditPostTests(AuthenticatedUserTestCase):
         self.user.last_posted_on = None
         self.user.last_posted_on = None
         self.user.save()
         self.user.save()
 
 
-        self.override_forum_acl({'can_edit_posts': 1, 'can_close_threads': 1})
+        self.override_category_acl({'can_edit_posts': 1, 'can_close_threads': 1})
         response = self.client.post(self.link, data={
         response = self.client.post(self.link, data={
             'post': self.thread.first_post.original,
             'post': self.thread.first_post.original,
             field_name: 0,
             field_name: 0,
@@ -340,13 +341,13 @@ class EditPostTests(AuthenticatedUserTestCase):
         """user edited post to pin and unpin thread"""
         """user edited post to pin and unpin thread"""
         prefix = 'misago.threads.posting.threadpin.ThreadPinFormMiddleware'
         prefix = 'misago.threads.posting.threadpin.ThreadPinFormMiddleware'
         field_name = '%s-is_pinned' % prefix
         field_name = '%s-is_pinned' % prefix
-        self.override_forum_acl({'can_edit_posts': 1, 'can_pin_threads': 1})
+        self.override_category_acl({'can_edit_posts': 1, 'can_pin_threads': 1})
 
 
         response = self.client.get(self.link, **self.ajax_header)
         response = self.client.get(self.link, **self.ajax_header)
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
         self.assertIn(field_name, response.content)
         self.assertIn(field_name, response.content)
 
 
-        self.override_forum_acl({'can_edit_posts': 1, 'can_pin_threads': 1})
+        self.override_category_acl({'can_edit_posts': 1, 'can_pin_threads': 1})
         response = self.client.post(self.link, data={
         response = self.client.post(self.link, data={
             'post': self.thread.first_post.original,
             'post': self.thread.first_post.original,
             field_name: 1,
             field_name: 1,
@@ -364,7 +365,7 @@ class EditPostTests(AuthenticatedUserTestCase):
         self.user.last_posted_on = None
         self.user.last_posted_on = None
         self.user.save()
         self.user.save()
 
 
-        self.override_forum_acl({'can_edit_posts': 1, 'can_pin_threads': 1})
+        self.override_category_acl({'can_edit_posts': 1, 'can_pin_threads': 1})
         response = self.client.post(self.link, data={
         response = self.client.post(self.link, data={
             'post': self.thread.first_post.original,
             'post': self.thread.first_post.original,
             field_name: 0,
             field_name: 0,
@@ -385,19 +386,19 @@ class EditPostTests(AuthenticatedUserTestCase):
         field_name = '%s-label' % prefix
         field_name = '%s-label' % prefix
 
 
         label = Label.objects.create(name="Label", slug="label")
         label = Label.objects.create(name="Label", slug="label")
-        label.forums.add(self.forum)
+        label.categories.add(self.category)
 
 
         acls = {
         acls = {
             'can_edit_posts': 1,
             'can_edit_posts': 1,
             'can_edit_threads': 1,
             'can_edit_threads': 1,
             'can_change_threads_labels': 1
             'can_change_threads_labels': 1
         }
         }
-        self.override_forum_acl(acls)
+        self.override_category_acl(acls)
         response = self.client.get(self.link, **self.ajax_header)
         response = self.client.get(self.link, **self.ajax_header)
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
         self.assertIn(field_name, response.content)
         self.assertIn(field_name, response.content)
 
 
-        self.override_forum_acl(acls)
+        self.override_category_acl(acls)
         response = self.client.post(self.link, data={
         response = self.client.post(self.link, data={
             field_name: label.pk,
             field_name: label.pk,
             'title': self.thread.title,
             'title': self.thread.title,
@@ -413,7 +414,7 @@ class EditPostTests(AuthenticatedUserTestCase):
         self.user.last_posted_on = None
         self.user.last_posted_on = None
         self.user.save()
         self.user.save()
 
 
-        self.override_forum_acl(acls)
+        self.override_category_acl(acls)
         response = self.client.post(self.link, data={
         response = self.client.post(self.link, data={
             field_name: 0,
             field_name: 0,
             'title': self.thread.title,
             'title': self.thread.title,
@@ -434,7 +435,7 @@ class EditPostTests(AuthenticatedUserTestCase):
             'can_change_threads_labels': 1
             'can_change_threads_labels': 1
         }
         }
 
 
-        self.override_forum_acl(acls)
+        self.override_category_acl(acls)
         response = self.client.post(self.link, data={
         response = self.client.post(self.link, data={
             'title': '',
             'title': '',
             'post': '',
             'post': '',
@@ -442,7 +443,7 @@ class EditPostTests(AuthenticatedUserTestCase):
         **self.ajax_header)
         **self.ajax_header)
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
 
 
-        self.override_forum_acl(acls)
+        self.override_category_acl(acls)
         response = self.client.post(self.link, data={
         response = self.client.post(self.link, data={
             'title': '',
             'title': '',
             'post': '',
             'post': '',

+ 156 - 155
misago/threads/tests/-test_forumthreads_view.py

@@ -4,33 +4,34 @@ from django.utils.translation import ugettext as _
 
 
 from misago.acl import add_acl
 from misago.acl import add_acl
 from misago.acl.testutils import override_acl
 from misago.acl.testutils import override_acl
-from misago.forums.models import Forum
+from misago.categories.models import Category
 from misago.users.testutils import AuthenticatedUserTestCase
 from misago.users.testutils import AuthenticatedUserTestCase
 
 
 from misago.threads import testutils
 from misago.threads import testutils
 from misago.threads.models import Thread, Label
 from misago.threads.models import Thread, Label
 from misago.threads.moderation import ModerationError
 from misago.threads.moderation import ModerationError
-from misago.threads.views.generic.forum import (ForumActions, ForumFiltering,
+from misago.threads.views.generic.category import (CategoryActions,
-                                                ForumThreads)
+                                                   CategoryFiltering,
+                                                   CategoryThreads)
 
 
 
 
-class ForumViewHelperTestCase(AuthenticatedUserTestCase):
+class CategoryViewHelperTestCase(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(ForumViewHelperTestCase, self).setUp()
+        super(CategoryViewHelperTestCase, self).setUp()
 
 
-        self.forum = Forum.objects.all_forums().filter(role="forum")[:1][0]
+        self.category = Category.objects.all_categories().filter(role='category')[:1][0]
-        self.forum.labels = []
+        self.category.labels = []
 
 
     def override_acl(self, new_acl):
     def override_acl(self, new_acl):
         new_acl.update({'can_browse': True})
         new_acl.update({'can_browse': True})
 
 
-        forums_acl = self.user.acl
+        categories_acl = self.user.acl
-        forums_acl['visible_forums'].append(self.forum.pk)
+        categories_acl['visible_categories'].append(self.category.pk)
-        forums_acl['forums'][self.forum.pk] = new_acl
+        categories_acl['categories'][self.category.pk] = new_acl
-        override_acl(self.user, forums_acl)
+        override_acl(self.user, categories_acl)
 
 
-        self.forum.acl = {}
+        self.category.acl = {}
-        add_acl(self.user, self.forum)
+        add_acl(self.user, self.category)
 
 
 
 
 class MockRequest(object):
 class MockRequest(object):
@@ -39,10 +40,10 @@ class MockRequest(object):
         self.user = user
         self.user = user
         self.user_ip = '127.0.0.1'
         self.user_ip = '127.0.0.1'
         self.session = {}
         self.session = {}
-        self.path = '/forum/fake-forum-1/'
+        self.path = '/category/fake-category-1/'
 
 
 
 
-class ActionsTests(ForumViewHelperTestCase):
+class ActionsTests(CategoryViewHelperTestCase):
     def setUp(self):
     def setUp(self):
         super(ActionsTests, self).setUp()
         super(ActionsTests, self).setUp()
         Label.objects.clear_cache()
         Label.objects.clear_cache()
@@ -52,32 +53,32 @@ class ActionsTests(ForumViewHelperTestCase):
         Label.objects.clear_cache()
         Label.objects.clear_cache()
 
 
     def test_label_actions(self):
     def test_label_actions(self):
-        """ForumActions initializes list with label actions"""
+        """CategoryActions initializes list with label actions"""
         self.override_acl({
         self.override_acl({
             'can_change_threads_labels': 0,
             'can_change_threads_labels': 0,
         })
         })
 
 
-        actions = ForumActions(user=self.user, forum=self.forum)
+        actions = CategoryActions(user=self.user, category=self.category)
         self.assertEqual(actions.available_actions, [])
         self.assertEqual(actions.available_actions, [])
 
 
         self.override_acl({
         self.override_acl({
             'can_change_threads_labels': 1,
             'can_change_threads_labels': 1,
         })
         })
 
 
-        actions = ForumActions(user=self.user, forum=self.forum)
+        actions = CategoryActions(user=self.user, category=self.category)
         self.assertEqual(actions.available_actions, [])
         self.assertEqual(actions.available_actions, [])
 
 
         self.override_acl({
         self.override_acl({
             'can_change_threads_labels': 2,
             'can_change_threads_labels': 2,
         })
         })
 
 
-        actions = ForumActions(user=self.user, forum=self.forum)
+        actions = CategoryActions(user=self.user, category=self.category)
         self.assertEqual(actions.available_actions, [])
         self.assertEqual(actions.available_actions, [])
 
 
         label = Label.objects.create(name="Mock Label", slug="mock-label")
         label = Label.objects.create(name="Mock Label", slug="mock-label")
-        self.forum.labels = [label]
+        self.category.labels = [label]
 
 
-        actions = ForumActions(user=self.user, forum=self.forum)
+        actions = CategoryActions(user=self.user, category=self.category)
         self.assertEqual(actions.available_actions, [
         self.assertEqual(actions.available_actions, [
             {
             {
                 'action': 'label:%s' % label.slug,
                 'action': 'label:%s' % label.slug,
@@ -92,19 +93,19 @@ class ActionsTests(ForumViewHelperTestCase):
         ])
         ])
 
 
     def test_pin_unpin_actions(self):
     def test_pin_unpin_actions(self):
-        """ForumActions initializes list with pin and unpin actions"""
+        """CategoryActions initializes list with pin and unpin actions"""
         self.override_acl({
         self.override_acl({
             'can_pin_threads': 0,
             'can_pin_threads': 0,
         })
         })
 
 
-        actions = ForumActions(user=self.user, forum=self.forum)
+        actions = CategoryActions(user=self.user, category=self.category)
         self.assertEqual(actions.available_actions, [])
         self.assertEqual(actions.available_actions, [])
 
 
         self.override_acl({
         self.override_acl({
             'can_pin_threads': 1,
             'can_pin_threads': 1,
         })
         })
 
 
-        actions = ForumActions(user=self.user, forum=self.forum)
+        actions = CategoryActions(user=self.user, category=self.category)
         self.assertEqual(actions.available_actions, [
         self.assertEqual(actions.available_actions, [
             {
             {
                 'action': 'pin',
                 'action': 'pin',
@@ -119,19 +120,19 @@ class ActionsTests(ForumViewHelperTestCase):
         ])
         ])
 
 
     def test_approve_action(self):
     def test_approve_action(self):
-        """ForumActions initializes list with approve threads action"""
+        """CategoryActions initializes list with approve threads action"""
         self.override_acl({
         self.override_acl({
             'can_review_moderated_content': 0,
             'can_review_moderated_content': 0,
         })
         })
 
 
-        actions = ForumActions(user=self.user, forum=self.forum)
+        actions = CategoryActions(user=self.user, category=self.category)
         self.assertEqual(actions.available_actions, [])
         self.assertEqual(actions.available_actions, [])
 
 
         self.override_acl({
         self.override_acl({
             'can_review_moderated_content': 1,
             'can_review_moderated_content': 1,
         })
         })
 
 
-        actions = ForumActions(user=self.user, forum=self.forum)
+        actions = CategoryActions(user=self.user, category=self.category)
         self.assertEqual(actions.available_actions, [
         self.assertEqual(actions.available_actions, [
             {
             {
                 'action': 'approve',
                 'action': 'approve',
@@ -141,19 +142,19 @@ class ActionsTests(ForumViewHelperTestCase):
         ])
         ])
 
 
     def test_move_action(self):
     def test_move_action(self):
-        """ForumActions initializes list with move threads action"""
+        """CategoryActions initializes list with move threads action"""
         self.override_acl({
         self.override_acl({
             'can_move_threads': 0,
             'can_move_threads': 0,
         })
         })
 
 
-        actions = ForumActions(user=self.user, forum=self.forum)
+        actions = CategoryActions(user=self.user, category=self.category)
         self.assertEqual(actions.available_actions, [])
         self.assertEqual(actions.available_actions, [])
 
 
         self.override_acl({
         self.override_acl({
             'can_move_threads': 1,
             'can_move_threads': 1,
         })
         })
 
 
-        actions = ForumActions(user=self.user, forum=self.forum)
+        actions = CategoryActions(user=self.user, category=self.category)
         self.assertEqual(actions.available_actions, [
         self.assertEqual(actions.available_actions, [
             {
             {
                 'action': 'move',
                 'action': 'move',
@@ -163,19 +164,19 @@ class ActionsTests(ForumViewHelperTestCase):
         ])
         ])
 
 
     def test_merge_action(self):
     def test_merge_action(self):
-        """ForumActions initializes list with merge threads action"""
+        """CategoryActions initializes list with merge threads action"""
         self.override_acl({
         self.override_acl({
             'can_merge_threads': 0,
             'can_merge_threads': 0,
         })
         })
 
 
-        actions = ForumActions(user=self.user, forum=self.forum)
+        actions = CategoryActions(user=self.user, category=self.category)
         self.assertEqual(actions.available_actions, [])
         self.assertEqual(actions.available_actions, [])
 
 
         self.override_acl({
         self.override_acl({
             'can_merge_threads': 1,
             'can_merge_threads': 1,
         })
         })
 
 
-        actions = ForumActions(user=self.user, forum=self.forum)
+        actions = CategoryActions(user=self.user, category=self.category)
         self.assertEqual(actions.available_actions, [
         self.assertEqual(actions.available_actions, [
             {
             {
                 'action': 'merge',
                 'action': 'merge',
@@ -185,19 +186,19 @@ class ActionsTests(ForumViewHelperTestCase):
         ])
         ])
 
 
     def test_close_open_actions(self):
     def test_close_open_actions(self):
-        """ForumActions initializes list with close and open actions"""
+        """CategoryActions initializes list with close and open actions"""
         self.override_acl({
         self.override_acl({
             'can_close_threads': 0,
             'can_close_threads': 0,
         })
         })
 
 
-        actions = ForumActions(user=self.user, forum=self.forum)
+        actions = CategoryActions(user=self.user, category=self.category)
         self.assertEqual(actions.available_actions, [])
         self.assertEqual(actions.available_actions, [])
 
 
         self.override_acl({
         self.override_acl({
             'can_close_threads': 1,
             'can_close_threads': 1,
         })
         })
 
 
-        actions = ForumActions(user=self.user, forum=self.forum)
+        actions = CategoryActions(user=self.user, category=self.category)
         self.assertEqual(actions.available_actions, [
         self.assertEqual(actions.available_actions, [
             {
             {
                 'action': 'open',
                 'action': 'open',
@@ -212,19 +213,19 @@ class ActionsTests(ForumViewHelperTestCase):
         ])
         ])
 
 
     def test_hide_delete_actions(self):
     def test_hide_delete_actions(self):
-        """ForumActions initializes list with hide/delete actions"""
+        """CategoryActions initializes list with hide/delete actions"""
         self.override_acl({
         self.override_acl({
             'can_hide_threads': 0,
             'can_hide_threads': 0,
         })
         })
 
 
-        actions = ForumActions(user=self.user, forum=self.forum)
+        actions = CategoryActions(user=self.user, category=self.category)
         self.assertEqual(actions.available_actions, [])
         self.assertEqual(actions.available_actions, [])
 
 
         self.override_acl({
         self.override_acl({
             'can_hide_threads': 1,
             'can_hide_threads': 1,
         })
         })
 
 
-        actions = ForumActions(user=self.user, forum=self.forum)
+        actions = CategoryActions(user=self.user, category=self.category)
         self.assertEqual(actions.available_actions, [
         self.assertEqual(actions.available_actions, [
             {
             {
                 'action': 'unhide',
                 'action': 'unhide',
@@ -242,7 +243,7 @@ class ActionsTests(ForumViewHelperTestCase):
             'can_hide_threads': 2,
             'can_hide_threads': 2,
         })
         })
 
 
-        actions = ForumActions(user=self.user, forum=self.forum)
+        actions = CategoryActions(user=self.user, category=self.category)
         self.assertEqual(actions.available_actions, [
         self.assertEqual(actions.available_actions, [
             {
             {
                 'action': 'unhide',
                 'action': 'unhide',
@@ -264,17 +265,17 @@ class ActionsTests(ForumViewHelperTestCase):
         ])
         ])
 
 
 
 
-class ForumFilteringTests(ForumViewHelperTestCase):
+class CategoryFilteringTests(CategoryViewHelperTestCase):
     def setUp(self):
     def setUp(self):
-        super(ForumFilteringTests, self).setUp()
+        super(CategoryFilteringTests, self).setUp()
         Label.objects.clear_cache()
         Label.objects.clear_cache()
 
 
     def tearDown(self):
     def tearDown(self):
-        super(ForumFilteringTests, self).tearDown()
+        super(CategoryFilteringTests, self).tearDown()
         Label.objects.clear_cache()
         Label.objects.clear_cache()
 
 
     def test_get_available_filters(self):
     def test_get_available_filters(self):
-        """get_available_filters returns filters varying on forum acl"""
+        """get_available_filters returns filters varying on category acl"""
         default_acl = {
         default_acl = {
             'can_see_all_threads': False,
             'can_see_all_threads': False,
             'can_see_reports': False,
             'can_see_reports': False,
@@ -289,9 +290,9 @@ class ForumFilteringTests(ForumViewHelperTestCase):
 
 
         for permission, filter_type in cases:
         for permission, filter_type in cases:
             self.override_acl(default_acl)
             self.override_acl(default_acl)
-            filtering = ForumFiltering(self.forum, 'misago:forum', {
+            filtering = CategoryFiltering(self.category, 'misago:category', {
-                'forum_id': self.forum.id,
+                'category_id': self.category.id,
-                'forum_slug': self.forum.slug,
+                'category_slug': self.category.slug,
             })
             })
 
 
             available_filters = filtering.get_available_filters()
             available_filters = filtering.get_available_filters()
@@ -302,16 +303,16 @@ class ForumFilteringTests(ForumViewHelperTestCase):
             acl[permission] = True
             acl[permission] = True
             self.override_acl(acl)
             self.override_acl(acl)
 
 
-            filtering = ForumFiltering(self.forum, 'misago:forum', {
+            filtering = CategoryFiltering(self.category, 'misago:category', {
-                'forum_id': self.forum.id,
+                'category_id': self.category.id,
-                'forum_slug': self.forum.slug,
+                'category_slug': self.category.slug,
             })
             })
 
 
             available_filters = filtering.get_available_filters()
             available_filters = filtering.get_available_filters()
             available_filters = [f['type'] for f in available_filters]
             available_filters = [f['type'] for f in available_filters]
             self.assertIn(filter_type, available_filters)
             self.assertIn(filter_type, available_filters)
 
 
-        self.forum.labels = [
+        self.category.labels = [
             Label(name='Label A', slug='label-a'),
             Label(name='Label A', slug='label-a'),
             Label(name='Label B', slug='label-b'),
             Label(name='Label B', slug='label-b'),
             Label(name='Label C', slug='label-c'),
             Label(name='Label C', slug='label-c'),
@@ -319,16 +320,16 @@ class ForumFilteringTests(ForumViewHelperTestCase):
         ]
         ]
 
 
         self.override_acl(default_acl)
         self.override_acl(default_acl)
-        ForumFiltering(self.forum, 'misago:forum', {
+        CategoryFiltering(self.category, 'misago:category', {
-                'forum_id': self.forum.id,
+                'category_id': self.category.id,
-                'forum_slug': self.forum.slug,
+                'category_slug': self.category.slug,
             })
             })
 
 
         available_filters = filtering.get_available_filters()
         available_filters = filtering.get_available_filters()
         available_filters = [f['type'] for f in available_filters]
         available_filters = [f['type'] for f in available_filters]
 
 
-        self.assertEqual(len(available_filters), len(self.forum.labels))
+        self.assertEqual(len(available_filters), len(self.category.labels))
-        for label in self.forum.labels:
+        for label in self.category.labels:
             self.assertIn(label.slug, available_filters)
             self.assertIn(label.slug, available_filters)
 
 
     def test_clean_kwargs(self):
     def test_clean_kwargs(self):
@@ -339,9 +340,9 @@ class ForumFilteringTests(ForumViewHelperTestCase):
             'can_review_moderated_content': True,
             'can_review_moderated_content': True,
         })
         })
 
 
-        filtering = ForumFiltering(self.forum, 'misago:forum', {
+        filtering = CategoryFiltering(self.category, 'misago:category', {
-            'forum_id': self.forum.id,
+            'category_id': self.category.id,
-            'forum_slug': self.forum.slug,
+            'category_slug': self.category.slug,
         })
         })
 
 
         available_filters = filtering.get_available_filters()
         available_filters = filtering.get_available_filters()
@@ -387,9 +388,9 @@ class ForumFilteringTests(ForumViewHelperTestCase):
         )
         )
 
 
         for filter_type, name in test_cases:
         for filter_type, name in test_cases:
-            filtering = ForumFiltering(self.forum, 'misago:forum', {
+            filtering = CategoryFiltering(self.category, 'misago:category', {
-                'forum_id': self.forum.id,
+                'category_id': self.category.id,
-                'forum_slug': self.forum.slug,
+                'category_slug': self.category.slug,
             })
             })
             filtering.clean_kwargs({'show': filter_type})
             filtering.clean_kwargs({'show': filter_type})
             self.assertEqual(filtering.current['name'], name)
             self.assertEqual(filtering.current['name'], name)
@@ -410,9 +411,9 @@ class ForumFilteringTests(ForumViewHelperTestCase):
         )
         )
 
 
         for filter_type in test_cases:
         for filter_type in test_cases:
-            filtering = ForumFiltering(self.forum, 'misago:forum', {
+            filtering = CategoryFiltering(self.category, 'misago:category', {
-                'forum_id': self.forum.id,
+                'category_id': self.category.id,
-                'forum_slug': self.forum.slug,
+                'category_slug': self.category.slug,
             })
             })
             filtering.clean_kwargs({'show': filter_type})
             filtering.clean_kwargs({'show': filter_type})
 
 
@@ -420,7 +421,7 @@ class ForumFilteringTests(ForumViewHelperTestCase):
             self.assertNotIn(filter_type, choices)
             self.assertNotIn(filter_type, choices)
 
 
 
 
-class ForumThreadsTests(ForumViewHelperTestCase):
+class CategoryThreadsTests(CategoryViewHelperTestCase):
     def test_empty_list(self):
     def test_empty_list(self):
         """list returns empty list of items"""
         """list returns empty list of items"""
         self.override_acl({
         self.override_acl({
@@ -428,7 +429,7 @@ class ForumThreadsTests(ForumViewHelperTestCase):
             'can_review_moderated_content': False
             'can_review_moderated_content': False
         })
         })
 
 
-        threads = ForumThreads(self.user, self.forum)
+        threads = CategoryThreads(self.user, self.category)
         threads_list = threads.list()
         threads_list = threads.list()
 
 
         self.assertEqual(threads_list, [])
         self.assertEqual(threads_list, [])
@@ -443,7 +444,7 @@ class ForumThreadsTests(ForumViewHelperTestCase):
             'can_review_moderated_content': False
             'can_review_moderated_content': False
         })
         })
 
 
-        threads = ForumThreads(self.user, self.forum)
+        threads = CategoryThreads(self.user, self.category)
 
 
         with self.assertRaises(AttributeError):
         with self.assertRaises(AttributeError):
             threads.page
             threads.page
@@ -455,22 +456,22 @@ class ForumThreadsTests(ForumViewHelperTestCase):
         """list returns list of visible threads"""
         """list returns list of visible threads"""
         test_threads = [
         test_threads = [
             testutils.post_thread(
             testutils.post_thread(
-                forum=self.forum,
+                category=self.category,
                 title="Hello, I am thread",
                 title="Hello, I am thread",
                 is_moderated=False,
                 is_moderated=False,
                 poster=self.user),
                 poster=self.user),
             testutils.post_thread(
             testutils.post_thread(
-                forum=self.forum,
+                category=self.category,
                 title="Hello, I am moderated thread",
                 title="Hello, I am moderated thread",
                 is_moderated=True,
                 is_moderated=True,
                 poster=self.user),
                 poster=self.user),
             testutils.post_thread(
             testutils.post_thread(
-                forum=self.forum,
+                category=self.category,
                 title="Hello, I am other user thread",
                 title="Hello, I am other user thread",
                 is_moderated=False,
                 is_moderated=False,
                 poster="Bob"),
                 poster="Bob"),
             testutils.post_thread(
             testutils.post_thread(
-                forum=self.forum,
+                category=self.category,
                 title="Hello, I am other user moderated thread",
                 title="Hello, I am other user moderated thread",
                 is_moderated=True,
                 is_moderated=True,
                 poster="Bob"),
                 poster="Bob"),
@@ -481,7 +482,7 @@ class ForumThreadsTests(ForumViewHelperTestCase):
             'can_review_moderated_content': False
             'can_review_moderated_content': False
         })
         })
 
 
-        threads = ForumThreads(self.user, self.forum)
+        threads = CategoryThreads(self.user, self.category)
         self.assertEqual(threads.list(), [test_threads[1], test_threads[0]])
         self.assertEqual(threads.list(), [test_threads[1], test_threads[0]])
 
 
         self.override_acl({
         self.override_acl({
@@ -489,7 +490,7 @@ class ForumThreadsTests(ForumViewHelperTestCase):
             'can_review_moderated_content': False
             'can_review_moderated_content': False
         })
         })
 
 
-        threads = ForumThreads(self.user, self.forum)
+        threads = CategoryThreads(self.user, self.category)
         self.assertEqual(threads.list(),
         self.assertEqual(threads.list(),
                          [test_threads[2], test_threads[1], test_threads[0]])
                          [test_threads[2], test_threads[1], test_threads[0]])
 
 
@@ -498,7 +499,7 @@ class ForumThreadsTests(ForumViewHelperTestCase):
             'can_review_moderated_content': True
             'can_review_moderated_content': True
         })
         })
 
 
-        threads = ForumThreads(self.user, self.forum)
+        threads = CategoryThreads(self.user, self.category)
         test_threads.reverse()
         test_threads.reverse()
         self.assertEqual(threads.list(), test_threads)
         self.assertEqual(threads.list(), test_threads)
 
 
@@ -506,33 +507,33 @@ class ForumThreadsTests(ForumViewHelperTestCase):
         self.assertTrue(threads.paginator)
         self.assertTrue(threads.paginator)
 
 
 
 
-class ForumThreadsViewTests(AuthenticatedUserTestCase):
+class CategoryThreadsViewTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
-        super(ForumThreadsViewTests, self).setUp()
+        super(CategoryThreadsViewTests, self).setUp()
 
 
-        self.forum = Forum.objects.all_forums().filter(role="forum")[:1][0]
+        self.category = Category.objects.all_categories().filter(role='category')[:1][0]
-        self.link = self.forum.get_absolute_url()
+        self.link = self.category.get_absolute_url()
-        self.forum.delete_content()
+        self.category.delete_content()
 
 
         Label.objects.clear_cache()
         Label.objects.clear_cache()
 
 
     def tearDown(self):
     def tearDown(self):
-        super(ForumThreadsViewTests, self).tearDown()
+        super(CategoryThreadsViewTests, self).tearDown()
         Label.objects.clear_cache()
         Label.objects.clear_cache()
 
 
-    def override_acl(self, new_acl, forum=None):
+    def override_acl(self, new_acl, category=None):
-        forum = forum or self.forum
+        category = category or self.category
 
 
-        forums_acl = self.user.acl
+        categories_acl = self.user.acl
         if new_acl['can_see']:
         if new_acl['can_see']:
-            forums_acl['visible_forums'].append(forum.pk)
+            categories_acl['visible_categories'].append(category.pk)
         else:
         else:
-            forums_acl['visible_forums'].remove(forum.pk)
+            categories_acl['visible_categories'].remove(category.pk)
-        forums_acl['forums'][forum.pk] = new_acl
+        categories_acl['categories'][category.pk] = new_acl
-        override_acl(self.user, forums_acl)
+        override_acl(self.user, categories_acl)
 
 
     def test_cant_see(self):
     def test_cant_see(self):
-        """has no permission to see forum"""
+        """has no permission to see category"""
         self.override_acl({
         self.override_acl({
             'can_see': 0,
             'can_see': 0,
             'can_browse': 0,
             'can_browse': 0,
@@ -542,7 +543,7 @@ class ForumThreadsViewTests(AuthenticatedUserTestCase):
         self.assertEqual(response.status_code, 404)
         self.assertEqual(response.status_code, 404)
 
 
     def test_cant_browse(self):
     def test_cant_browse(self):
-        """has no permission to browse forum"""
+        """has no permission to browse category"""
         self.override_acl({
         self.override_acl({
             'can_see': 1,
             'can_see': 1,
             'can_browse': 0,
             'can_browse': 0,
@@ -552,7 +553,7 @@ class ForumThreadsViewTests(AuthenticatedUserTestCase):
         self.assertEqual(response.status_code, 403)
         self.assertEqual(response.status_code, 403)
 
 
     def test_can_browse_empty(self):
     def test_can_browse_empty(self):
-        """has permission to browse forum, sees empty list"""
+        """has permission to browse category, sees empty list"""
         self.override_acl({
         self.override_acl({
             'can_see': 1,
             'can_see': 1,
             'can_browse': 1,
             'can_browse': 1,
@@ -575,20 +576,20 @@ class ForumThreadsViewTests(AuthenticatedUserTestCase):
 
 
         other_moderated_title = "Test other user moderated thread"
         other_moderated_title = "Test other user moderated thread"
         testutils.post_thread(
         testutils.post_thread(
-            forum=self.forum, title=other_moderated_title, is_moderated=True)
+            category=self.category, title=other_moderated_title, is_moderated=True)
 
 
         other_title = "Test other user thread"
         other_title = "Test other user thread"
-        testutils.post_thread(forum=self.forum, title=other_title)
+        testutils.post_thread(category=self.category, title=other_title)
 
 
         owned_title = "Test authenticated user thread"
         owned_title = "Test authenticated user thread"
         testutils.post_thread(
         testutils.post_thread(
-            forum=self.forum,
+            category=self.category,
             title=owned_title,
             title=owned_title,
             poster=self.user)
             poster=self.user)
 
 
         owned_moderated_title = "Test authenticated user moderated thread"
         owned_moderated_title = "Test authenticated user moderated thread"
         testutils.post_thread(
         testutils.post_thread(
-            forum=self.forum,
+            category=self.category,
             title=owned_moderated_title,
             title=owned_moderated_title,
             poster=self.user,
             poster=self.user,
             is_moderated=True)
             is_moderated=True)
@@ -613,7 +614,7 @@ class ForumThreadsViewTests(AuthenticatedUserTestCase):
 
 
         test_title = "Test moderated thread"
         test_title = "Test moderated thread"
         thread = testutils.post_thread(
         thread = testutils.post_thread(
-            forum=self.forum, title=test_title, is_moderated=True)
+            category=self.category, title=test_title, is_moderated=True)
 
 
         self.override_acl(test_acl)
         self.override_acl(test_acl)
         response = self.client.get(self.link)
         response = self.client.get(self.link)
@@ -622,7 +623,7 @@ class ForumThreadsViewTests(AuthenticatedUserTestCase):
 
 
         test_title = "Test owned moderated thread"
         test_title = "Test owned moderated thread"
         thread = testutils.post_thread(
         thread = testutils.post_thread(
-            forum=self.forum,
+            category=self.category,
             title=test_title,
             title=test_title,
             is_moderated=True,
             is_moderated=True,
             poster=self.user)
             poster=self.user)
@@ -643,20 +644,20 @@ class ForumThreadsViewTests(AuthenticatedUserTestCase):
 
 
         other_moderated_title = "Test other user moderated thread"
         other_moderated_title = "Test other user moderated thread"
         testutils.post_thread(
         testutils.post_thread(
-            forum=self.forum, title=other_moderated_title, is_moderated=True)
+            category=self.category, title=other_moderated_title, is_moderated=True)
 
 
         other_title = "Test other user thread"
         other_title = "Test other user thread"
-        testutils.post_thread(forum=self.forum, title=other_title)
+        testutils.post_thread(category=self.category, title=other_title)
 
 
         owned_title = "Test authenticated user thread"
         owned_title = "Test authenticated user thread"
         testutils.post_thread(
         testutils.post_thread(
-            forum=self.forum,
+            category=self.category,
             title=owned_title,
             title=owned_title,
             poster=self.user)
             poster=self.user)
 
 
         owned_moderated_title = "Test authenticated user moderated thread"
         owned_moderated_title = "Test authenticated user moderated thread"
         testutils.post_thread(
         testutils.post_thread(
-            forum=self.forum,
+            category=self.category,
             title=owned_moderated_title,
             title=owned_moderated_title,
             poster=self.user,
             poster=self.user,
             is_moderated=True)
             is_moderated=True)
@@ -671,9 +672,9 @@ class ForumThreadsViewTests(AuthenticatedUserTestCase):
         self.assertIn('show-my-threads', response.content)
         self.assertIn('show-my-threads', response.content)
 
 
         self.override_acl(test_acl)
         self.override_acl(test_acl)
-        response = self.client.get(reverse('misago:forum', kwargs={
+        response = self.client.get(reverse('misago:category', kwargs={
-                'forum_id': self.forum.id,
+                'category_id': self.category.id,
-                'forum_slug': self.forum.slug,
+                'category_slug': self.category.slug,
                 'show': 'my-threads',
                 'show': 'my-threads',
             }))
             }))
 
 
@@ -693,15 +694,15 @@ class ForumThreadsViewTests(AuthenticatedUserTestCase):
         }
         }
 
 
         not_moderated_title = "Not moderated thread"
         not_moderated_title = "Not moderated thread"
-        testutils.post_thread(forum=self.forum, title=not_moderated_title)
+        testutils.post_thread(category=self.category, title=not_moderated_title)
 
 
         hidden_title = "Test moderated thread"
         hidden_title = "Test moderated thread"
         testutils.post_thread(
         testutils.post_thread(
-            forum=self.forum, title=hidden_title, is_moderated=True)
+            category=self.category, title=hidden_title, is_moderated=True)
 
 
         visible_title = "Test owned moderated thread"
         visible_title = "Test owned moderated thread"
         testutils.post_thread(
         testutils.post_thread(
-            forum=self.forum,
+            category=self.category,
             title=visible_title,
             title=visible_title,
             is_moderated=True,
             is_moderated=True,
             poster=self.user)
             poster=self.user)
@@ -716,17 +717,17 @@ class ForumThreadsViewTests(AuthenticatedUserTestCase):
         self.assertNotIn('show-moderated-posts', response.content)
         self.assertNotIn('show-moderated-posts', response.content)
 
 
         self.override_acl(test_acl)
         self.override_acl(test_acl)
-        response = self.client.get(reverse('misago:forum', kwargs={
+        response = self.client.get(reverse('misago:category', kwargs={
-                'forum_id': self.forum.id,
+                'category_id': self.category.id,
-                'forum_slug': self.forum.slug,
+                'category_slug': self.category.slug,
                 'show': 'moderated-threads',
                 'show': 'moderated-threads',
             }))
             }))
         self.assertEqual(response.status_code, 302)
         self.assertEqual(response.status_code, 302)
 
 
         self.override_acl(test_acl)
         self.override_acl(test_acl)
-        response = self.client.get(reverse('misago:forum', kwargs={
+        response = self.client.get(reverse('misago:category', kwargs={
-                'forum_id': self.forum.id,
+                'category_id': self.category.id,
-                'forum_slug': self.forum.slug,
+                'category_slug': self.category.slug,
                 'show': 'moderated-posts',
                 'show': 'moderated-posts',
             }))
             }))
         self.assertEqual(response.status_code, 302)
         self.assertEqual(response.status_code, 302)
@@ -748,9 +749,9 @@ class ForumThreadsViewTests(AuthenticatedUserTestCase):
         self.assertIn('show-moderated-posts', response.content)
         self.assertIn('show-moderated-posts', response.content)
 
 
         self.override_acl(test_acl)
         self.override_acl(test_acl)
-        response = self.client.get(reverse('misago:forum', kwargs={
+        response = self.client.get(reverse('misago:category', kwargs={
-                'forum_id': self.forum.id,
+                'category_id': self.category.id,
-                'forum_slug': self.forum.slug,
+                'category_slug': self.category.slug,
                 'show': 'moderated-threads',
                 'show': 'moderated-threads',
             }))
             }))
 
 
@@ -764,7 +765,7 @@ class ForumThreadsViewTests(AuthenticatedUserTestCase):
     def test_anonymous_request(self):
     def test_anonymous_request(self):
         """view renders to anonymous users"""
         """view renders to anonymous users"""
         anon_title = "Hello Anon!"
         anon_title = "Hello Anon!"
-        testutils.post_thread(forum=self.forum, title=anon_title)
+        testutils.post_thread(category=self.category, title=anon_title)
 
 
         response = self.client.get(self.link)
         response = self.client.get(self.link)
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
@@ -772,7 +773,7 @@ class ForumThreadsViewTests(AuthenticatedUserTestCase):
 
 
     def test_change_threads_labels(self):
     def test_change_threads_labels(self):
         """moderation allows for changing threads labels"""
         """moderation allows for changing threads labels"""
-        threads = [testutils.post_thread(self.forum) for t in xrange(10)]
+        threads = [testutils.post_thread(self.category) for t in xrange(10)]
 
 
         test_acl = {
         test_acl = {
             'can_see': 1,
             'can_see': 1,
@@ -787,7 +788,7 @@ class ForumThreadsViewTests(AuthenticatedUserTestCase):
         ]
         ]
         for label in labels:
         for label in labels:
             label.save()
             label.save()
-            label.forums.add(self.forum)
+            label.categories.add(self.category)
 
 
         self.override_acl(test_acl)
         self.override_acl(test_acl)
         response = self.client.get(self.link)
         response = self.client.get(self.link)
@@ -879,8 +880,8 @@ class ForumThreadsViewTests(AuthenticatedUserTestCase):
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
         self.assertIn("Pin threads", response.content)
         self.assertIn("Pin threads", response.content)
 
 
-        pinned = testutils.post_thread(self.forum, is_pinned=True)
+        pinned = testutils.post_thread(self.category, is_pinned=True)
-        thread = testutils.post_thread(self.forum, is_pinned=False)
+        thread = testutils.post_thread(self.category, is_pinned=False)
 
 
         # pin nothing
         # pin nothing
         self.override_acl(test_acl)
         self.override_acl(test_acl)
@@ -953,8 +954,8 @@ class ForumThreadsViewTests(AuthenticatedUserTestCase):
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
         self.assertIn("Approve threads", response.content)
         self.assertIn("Approve threads", response.content)
 
 
-        thread = testutils.post_thread(self.forum, is_moderated=False)
+        thread = testutils.post_thread(self.category, is_moderated=False)
-        moderated_thread = testutils.post_thread(self.forum, is_moderated=True)
+        moderated_thread = testutils.post_thread(self.category, is_moderated=True)
 
 
         # approve approved thread
         # approve approved thread
         self.override_acl(test_acl)
         self.override_acl(test_acl)
@@ -982,10 +983,10 @@ class ForumThreadsViewTests(AuthenticatedUserTestCase):
 
 
     def test_move_threads(self):
     def test_move_threads(self):
         """moderation allows for moving threads"""
         """moderation allows for moving threads"""
-        new_forum = Forum(name="New Forum",
+        new_category = Category(name="New Category",
-                          slug="new-forum",
+                          slug="new-category",
-                          role="forum")
+                          role='category')
-        new_forum.insert_at(self.forum, save=True)
+        new_category.insert_at(self.category, save=True)
 
 
         test_acl = {
         test_acl = {
             'can_see': 1,
             'can_see': 1,
@@ -999,7 +1000,7 @@ class ForumThreadsViewTests(AuthenticatedUserTestCase):
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
         self.assertIn("Move threads", response.content)
         self.assertIn("Move threads", response.content)
 
 
-        threads = [testutils.post_thread(self.forum) for t in xrange(10)]
+        threads = [testutils.post_thread(self.category) for t in xrange(10)]
 
 
         # see move threads form
         # see move threads form
         self.override_acl(test_acl)
         self.override_acl(test_acl)
@@ -1011,26 +1012,26 @@ class ForumThreadsViewTests(AuthenticatedUserTestCase):
         for thread in threads[:5]:
         for thread in threads[:5]:
             self.assertIn(thread.title, response.content)
             self.assertIn(thread.title, response.content)
 
 
-        # submit form with non-existing forum
+        # submit form with non-existing category
         self.override_acl(test_acl)
         self.override_acl(test_acl)
         response = self.client.post(self.link, data={
         response = self.client.post(self.link, data={
             'action': 'move',
             'action': 'move',
             'item': [t.pk for t in threads[:5]],
             'item': [t.pk for t in threads[:5]],
             'submit': '',
             'submit': '',
-            'new_forum': new_forum.pk + 1234
+            'new_category': new_category.pk + 1234
         })
         })
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
-        self.assertIn("Select valid forum.", response.content)
+        self.assertIn("Select valid category.", response.content)
 
 
         # attempt move to category
         # attempt move to category
         self.override_acl(test_acl)
         self.override_acl(test_acl)
 
 
-        category = Forum.objects.all_forums().filter(role="category")[:1][0]
+        category = Category.objects.all_categories().filter(role='category')[:1][0]
         response = self.client.post(self.link, data={
         response = self.client.post(self.link, data={
             'action': 'move',
             'action': 'move',
             'item': [t.pk for t in threads[:5]],
             'item': [t.pk for t in threads[:5]],
             'submit': '',
             'submit': '',
-            'new_forum': category.pk
+            'new_category': category.pk
         })
         })
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
         self.assertIn("You can&#39;t move threads to category.",
         self.assertIn("You can&#39;t move threads to category.",
@@ -1039,37 +1040,37 @@ class ForumThreadsViewTests(AuthenticatedUserTestCase):
         # attempt move to redirect
         # attempt move to redirect
         self.override_acl(test_acl)
         self.override_acl(test_acl)
 
 
-        redirect = Forum.objects.all_forums().filter(role="redirect")[:1][0]
+        redirect = Category.objects.all_categories().filter(role="redirect")[:1][0]
         response = self.client.post(self.link, data={
         response = self.client.post(self.link, data={
             'action': 'move',
             'action': 'move',
             'item': [t.pk for t in threads[:5]],
             'item': [t.pk for t in threads[:5]],
             'submit': '',
             'submit': '',
-            'new_forum': redirect.pk
+            'new_category': redirect.pk
         })
         })
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
         self.assertIn("You can&#39;t move threads to redirect.",
         self.assertIn("You can&#39;t move threads to redirect.",
                       response.content)
                       response.content)
 
 
-        # move to new_forum
+        # move to new_category
         self.override_acl(test_acl)
         self.override_acl(test_acl)
-        self.override_acl(test_acl, new_forum)
+        self.override_acl(test_acl, new_category)
         response = self.client.post(self.link, data={
         response = self.client.post(self.link, data={
             'action': 'move',
             'action': 'move',
             'item': [t.pk for t in threads[:5]],
             'item': [t.pk for t in threads[:5]],
             'submit': '',
             'submit': '',
-            'new_forum': new_forum.pk
+            'new_category': new_category.pk
         })
         })
         self.assertEqual(response.status_code, 302)
         self.assertEqual(response.status_code, 302)
 
 
         self.override_acl(test_acl)
         self.override_acl(test_acl)
         response = self.client.get(self.link)
         response = self.client.get(self.link)
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
-        self.assertIn("5 threads were moved to &quot;New Forum&quot;.",
+        self.assertIn("5 threads were moved to &quot;New Category&quot;.",
                       response.content)
                       response.content)
 
 
-        for thread in new_forum.thread_set.all():
+        for thread in new_category.thread_set.all():
             self.assertIn(thread, threads[:5])
             self.assertIn(thread, threads[:5])
-        for thread in self.forum.thread_set.all():
+        for thread in self.category.thread_set.all():
             self.assertIn(thread, threads[5:])
             self.assertIn(thread, threads[5:])
 
 
     def test_merge_threads(self):
     def test_merge_threads(self):
@@ -1086,7 +1087,7 @@ class ForumThreadsViewTests(AuthenticatedUserTestCase):
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
         self.assertIn("Merge threads", response.content)
         self.assertIn("Merge threads", response.content)
 
 
-        threads = [testutils.post_thread(self.forum) for t in xrange(10)]
+        threads = [testutils.post_thread(self.category) for t in xrange(10)]
 
 
         # see merge threads form
         # see merge threads form
         self.override_acl(test_acl)
         self.override_acl(test_acl)
@@ -1156,7 +1157,7 @@ class ForumThreadsViewTests(AuthenticatedUserTestCase):
         self.assertIn("Close threads", response.content)
         self.assertIn("Close threads", response.content)
         self.assertIn("Open threads", response.content)
         self.assertIn("Open threads", response.content)
 
 
-        threads = [testutils.post_thread(self.forum) for t in xrange(10)]
+        threads = [testutils.post_thread(self.category) for t in xrange(10)]
 
 
         # close threads
         # close threads
         self.override_acl(test_acl)
         self.override_acl(test_acl)
@@ -1221,7 +1222,7 @@ class ForumThreadsViewTests(AuthenticatedUserTestCase):
         self.assertIn("Reveal threads", response.content)
         self.assertIn("Reveal threads", response.content)
         self.assertIn("Hide threads", response.content)
         self.assertIn("Hide threads", response.content)
 
 
-        threads = [testutils.post_thread(self.forum) for t in xrange(10)]
+        threads = [testutils.post_thread(self.category) for t in xrange(10)]
 
 
         # hide threads
         # hide threads
         self.override_acl(test_acl)
         self.override_acl(test_acl)
@@ -1273,10 +1274,10 @@ class ForumThreadsViewTests(AuthenticatedUserTestCase):
 
 
     def test_delete_threads(self):
     def test_delete_threads(self):
         """moderation allows for deleting threads"""
         """moderation allows for deleting threads"""
-        threads = [testutils.post_thread(self.forum) for t in xrange(10)]
+        threads = [testutils.post_thread(self.category) for t in xrange(10)]
 
 
-        self.forum.synchronize()
+        self.category.synchronize()
-        self.assertEqual(self.forum.threads, 10)
+        self.assertEqual(self.category.threads, 10)
 
 
         test_acl = {
         test_acl = {
             'can_see': 1,
             'can_see': 1,
@@ -1297,14 +1298,14 @@ class ForumThreadsViewTests(AuthenticatedUserTestCase):
         self.assertEqual(response.status_code, 302)
         self.assertEqual(response.status_code, 302)
         self.assertTrue(response['location'].endswith(self.link))
         self.assertTrue(response['location'].endswith(self.link))
 
 
-        forum = Forum.objects.get(pk=self.forum.pk)
+        category = Category.objects.get(pk=self.category.pk)
-        self.assertEqual(forum.threads, 0)
+        self.assertEqual(category.threads, 0)
 
 
-        threads = [testutils.post_thread(self.forum) for t in xrange(60)]
+        threads = [testutils.post_thread(self.category) for t in xrange(60)]
 
 
-        second_page_link = reverse('misago:forum', kwargs={
+        second_page_link = reverse('misago:category', kwargs={
-            'forum_id': self.forum.id,
+            'category_id': self.category.id,
-            'forum_slug': self.forum.slug,
+            'category_slug': self.category.slug,
             'page': 2
             'page': 2
         })
         })
 
 
@@ -1315,8 +1316,8 @@ class ForumThreadsViewTests(AuthenticatedUserTestCase):
         self.assertEqual(response.status_code, 302)
         self.assertEqual(response.status_code, 302)
         self.assertTrue(response['location'].endswith(second_page_link))
         self.assertTrue(response['location'].endswith(second_page_link))
 
 
-        forum = Forum.objects.get(pk=self.forum.pk)
+        category = Category.objects.get(pk=self.category.pk)
-        self.assertEqual(forum.threads, 40)
+        self.assertEqual(category.threads, 40)
 
 
         self.override_acl(test_acl)
         self.override_acl(test_acl)
         response = self.client.post(second_page_link, data={
         response = self.client.post(second_page_link, data={

+ 5 - 5
misago/threads/tests/-test_goto_views.py

@@ -1,5 +1,5 @@
 from misago.acl import add_acl
 from misago.acl import add_acl
-from misago.forums.models import Forum
+from misago.categories.models import Category
 from misago.users.testutils import AuthenticatedUserTestCase
 from misago.users.testutils import AuthenticatedUserTestCase
 
 
 from misago.threads import goto
 from misago.threads import goto
@@ -10,11 +10,11 @@ class GotoViewsTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
         super(GotoViewsTests, self).setUp()
         super(GotoViewsTests, self).setUp()
 
 
-        self.forum = Forum.objects.all_forums().filter(role="forum")[:1][0]
+        self.category = Category.objects.all_categories().filter(role='forum')[:1][0]
-        self.forum.labels = []
+        self.category.labels = []
 
 
-        self.thread = post_thread(self.forum)
+        self.thread = post_thread(self.category)
-        add_acl(self.user, self.forum)
+        add_acl(self.user, self.category)
         add_acl(self.user, self.thread)
         add_acl(self.user, self.thread)
 
 
     def test_goto_last(self):
     def test_goto_last(self):

+ 10 - 10
misago/threads/tests/-test_gotolists_views.py

@@ -1,6 +1,6 @@
 from misago.acl import add_acl
 from misago.acl import add_acl
 from misago.acl.testutils import override_acl
 from misago.acl.testutils import override_acl
-from misago.forums.models import Forum
+from misago.categories.models import Category
 from misago.users.testutils import AuthenticatedUserTestCase
 from misago.users.testutils import AuthenticatedUserTestCase
 
 
 from misago.threads.testutils import post_thread, reply_thread
 from misago.threads.testutils import post_thread, reply_thread
@@ -12,10 +12,10 @@ class GotoListsViewsTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
         super(GotoListsViewsTests, self).setUp()
         super(GotoListsViewsTests, self).setUp()
 
 
-        self.forum = Forum.objects.all_forums().filter(role="forum")[:1][0]
+        self.category = Category.objects.all_categories().filter(role='category')[:1][0]
-        self.forum.labels = []
+        self.category.labels = []
 
 
-        self.thread = post_thread(self.forum)
+        self.thread = post_thread(self.category)
 
 
     def override_acl(self, new_acl):
     def override_acl(self, new_acl):
         new_acl.update({
         new_acl.update({
@@ -24,13 +24,13 @@ class GotoListsViewsTests(AuthenticatedUserTestCase):
             'can_see_all_threads': True,
             'can_see_all_threads': True,
         })
         })
 
 
-        forums_acl = self.user.acl
+        categories_acl = self.user.acl
-        forums_acl['visible_forums'].append(self.forum.pk)
+        categories_acl['visible_categories'].append(self.category.pk)
-        forums_acl['forums'][self.forum.pk] = new_acl
+        categories_acl['categories'][self.category.pk] = new_acl
-        override_acl(self.user, forums_acl)
+        override_acl(self.user, categories_acl)
 
 
-        self.forum.acl = {}
+        self.category.acl = {}
-        add_acl(self.user, self.forum)
+        add_acl(self.user, self.category)
 
 
     def test_moderated_list(self):
     def test_moderated_list(self):
         """moderated posts list works"""
         """moderated posts list works"""

+ 12 - 12
misago/threads/tests/-test_moderatedcontent_view.py

@@ -2,7 +2,7 @@ from django.core.urlresolvers import reverse
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
 
 
 from misago.acl.testutils import override_acl
 from misago.acl.testutils import override_acl
-from misago.forums.models import Forum
+from misago.categories.models import Category
 from misago.users.testutils import UserTestCase, AuthenticatedUserTestCase
 from misago.users.testutils import UserTestCase, AuthenticatedUserTestCase
 
 
 from misago.threads import testutils
 from misago.threads import testutils
@@ -17,14 +17,14 @@ class AuthenticatedTests(AuthenticatedUserTestCase):
             'can_review_moderated_content': True,
             'can_review_moderated_content': True,
         }
         }
 
 
-        forums_acl = self.user.acl
+        categories_acl = self.user.acl
 
 
-        for forum in Forum.objects.all():
+        for category in Category.objects.all():
-            forums_acl['visible_forums'].append(forum.pk)
+            categories_acl['visible_categories'].append(category.pk)
-            forums_acl['can_review_moderated_content'].append(forum.pk)
+            categories_acl['can_review_moderated_content'].append(category.pk)
-            forums_acl['forums'][forum.pk] = new_acl
+            categories_acl['categories'][category.pk] = new_acl
 
 
-        override_acl(self.user, forums_acl)
+        override_acl(self.user, categories_acl)
 
 
     def test_cant_see_threads_list(self):
     def test_cant_see_threads_list(self):
         """user has no permission to see moderated list"""
         """user has no permission to see moderated list"""
@@ -34,8 +34,8 @@ class AuthenticatedTests(AuthenticatedUserTestCase):
 
 
     def test_empty_threads_list(self):
     def test_empty_threads_list(self):
         """empty threads list is rendered"""
         """empty threads list is rendered"""
-        forum = Forum.objects.all_forums().filter(role="forum")[:1][0]
+        category = Category.objects.all_categories().filter(role="category")[:1][0]
-        [testutils.post_thread(forum) for t in xrange(10)]
+        [testutils.post_thread(category) for t in xrange(10)]
 
 
         self.override_acl();
         self.override_acl();
         response = self.client.get(reverse('misago:moderated_content'))
         response = self.client.get(reverse('misago:moderated_content'))
@@ -44,14 +44,14 @@ class AuthenticatedTests(AuthenticatedUserTestCase):
 
 
     def test_filled_threads_list(self):
     def test_filled_threads_list(self):
         """filled threads list is rendered"""
         """filled threads list is rendered"""
-        forum = Forum.objects.all_forums().filter(role="forum")[:1][0]
+        category = Category.objects.all_categories().filter(role="category")[:1][0]
 
 
         threads = []
         threads = []
         for t in xrange(10):
         for t in xrange(10):
-            threads.append(testutils.post_thread(forum, is_moderated=True))
+            threads.append(testutils.post_thread(category, is_moderated=True))
 
 
         for t in xrange(10):
         for t in xrange(10):
-            threads.append(testutils.post_thread(forum))
+            threads.append(testutils.post_thread(category))
             testutils.reply_thread(threads[-1], is_moderated=True)
             testutils.reply_thread(threads[-1], is_moderated=True)
 
 
         self.override_acl();
         self.override_acl();

+ 5 - 5
misago/threads/tests/-test_newthreads_view.py

@@ -1,7 +1,7 @@
 from django.core.urlresolvers import reverse
 from django.core.urlresolvers import reverse
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
 
 
-from misago.forums.models import Forum
+from misago.categories.models import Category
 from misago.users.testutils import UserTestCase, AuthenticatedUserTestCase
 from misago.users.testutils import UserTestCase, AuthenticatedUserTestCase
 
 
 from misago.threads import testutils
 from misago.threads import testutils
@@ -16,8 +16,8 @@ class AuthenticatedTests(AuthenticatedUserTestCase):
 
 
     def test_single_page_threads_list(self):
     def test_single_page_threads_list(self):
         """filled threads list is rendered"""
         """filled threads list is rendered"""
-        forum = Forum.objects.all_forums().filter(role="forum")[:1][0]
+        category = Category.objects.all_categories().filter(role='forum')[:1][0]
-        threads = [testutils.post_thread(forum) for t in xrange(10)]
+        threads = [testutils.post_thread(category) for t in xrange(10)]
 
 
         response = self.client.get(reverse('misago:new_threads'))
         response = self.client.get(reverse('misago:new_threads'))
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
@@ -45,8 +45,8 @@ class AuthenticatedTests(AuthenticatedUserTestCase):
 
 
     def test_multipage_threads_list(self):
     def test_multipage_threads_list(self):
         """multipage threads list is rendered"""
         """multipage threads list is rendered"""
-        forum = Forum.objects.all_forums().filter(role="forum")[:1][0]
+        category = Category.objects.all_categories().filter(role='forum')[:1][0]
-        threads = [testutils.post_thread(forum) for t in xrange(80)]
+        threads = [testutils.post_thread(category) for t in xrange(80)]
 
 
         response = self.client.get(reverse('misago:new_threads'))
         response = self.client.get(reverse('misago:new_threads'))
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)

+ 10 - 10
misago/threads/tests/-test_post_views.py

@@ -1,7 +1,7 @@
 from django.core.urlresolvers import reverse
 from django.core.urlresolvers import reverse
 
 
 from misago.acl.testutils import override_acl
 from misago.acl.testutils import override_acl
-from misago.forums.models import Forum
+from misago.categories.models import Category
 from misago.users.testutils import AuthenticatedUserTestCase
 from misago.users.testutils import AuthenticatedUserTestCase
 
 
 from misago.threads.models import Thread, Post
 from misago.threads.models import Thread, Post
@@ -12,12 +12,12 @@ class PostViewTestCase(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
         super(PostViewTestCase, self).setUp()
         super(PostViewTestCase, self).setUp()
 
 
-        self.forum = Forum.objects.all_forums().filter(role="forum")[:1][0]
+        self.category = Category.objects.all_categories().filter(role='forum')[:1][0]
-        self.forum.labels = []
+        self.category.labels = []
 
 
-        self.thread = post_thread(self.forum)
+        self.thread = post_thread(self.category)
 
 
-    def override_acl(self, new_acl, forum=None):
+    def override_acl(self, new_acl, category=None):
         new_acl.update({
         new_acl.update({
             'can_see': True,
             'can_see': True,
             'can_browse': True,
             'can_browse': True,
@@ -25,12 +25,12 @@ class PostViewTestCase(AuthenticatedUserTestCase):
             'can_see_own_threads': False
             'can_see_own_threads': False
         })
         })
 
 
-        forum = forum or self.forum
+        category = category or self.category
 
 
-        forums_acl = self.user.acl
+        categories_acl = self.user.acl
-        forums_acl['visible_forums'].append(forum.pk)
+        categories_acl['visible_categories'].append(category.pk)
-        forums_acl['forums'][forum.pk] = new_acl
+        categories_acl['categories'][category.pk] = new_acl
-        override_acl(self.user, forums_acl)
+        override_acl(self.user, categories_acl)
 
 
 
 
 class ApprovePostViewTests(PostViewTestCase):
 class ApprovePostViewTests(PostViewTestCase):

+ 3 - 3
misago/threads/tests/-test_privatethread_view.py

@@ -2,7 +2,7 @@ from django.contrib.auth import get_user_model
 from django.utils import timezone
 from django.utils import timezone
 
 
 from misago.acl.testutils import override_acl
 from misago.acl.testutils import override_acl
-from misago.forums.models import Forum
+from misago.categories.models import Category
 from misago.users.testutils import AuthenticatedUserTestCase
 from misago.users.testutils import AuthenticatedUserTestCase
 
 
 from misago.threads import testutils
 from misago.threads import testutils
@@ -13,8 +13,8 @@ class PrivateThreadTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
         super(PrivateThreadTests, self).setUp()
         super(PrivateThreadTests, self).setUp()
 
 
-        self.forum = Forum.objects.private_threads()
+        self.category = Category.objects.private_threads()
-        self.thread = testutils.post_thread(self.forum)
+        self.thread = testutils.post_thread(self.category)
 
 
     def test_anon_access_to_view(self):
     def test_anon_access_to_view(self):
         """anonymous user has no access to private thread"""
         """anonymous user has no access to private thread"""

+ 7 - 7
misago/threads/tests/-test_privatethreads_view.py

@@ -2,7 +2,7 @@ from django.core.urlresolvers import reverse
 from django.utils import timezone
 from django.utils import timezone
 
 
 from misago.acl.testutils import override_acl
 from misago.acl.testutils import override_acl
-from misago.forums.models import Forum
+from misago.categories.models import Category
 from misago.users.testutils import UserTestCase, AuthenticatedUserTestCase
 from misago.users.testutils import UserTestCase, AuthenticatedUserTestCase
 
 
 from misago.threads import testutils
 from misago.threads import testutils
@@ -30,9 +30,9 @@ class AuthenticatedTests(AuthenticatedUserTestCase):
         """private threads list displays threads user participates in"""
         """private threads list displays threads user participates in"""
         override_acl(self.user, {'can_moderate_private_threads': False})
         override_acl(self.user, {'can_moderate_private_threads': False})
 
 
-        forum = Forum.objects.private_threads()
+        category = Category.objects.private_threads()
-        invisible_threads = [testutils.post_thread(forum) for t in xrange(10)]
+        invisible_threads = [testutils.post_thread(category) for t in xrange(10)]
-        visible_threads = [testutils.post_thread(forum) for t in xrange(10)]
+        visible_threads = [testutils.post_thread(category) for t in xrange(10)]
 
 
         for thread in visible_threads:
         for thread in visible_threads:
             ThreadParticipant.objects.set_owner(thread, self.user)
             ThreadParticipant.objects.set_owner(thread, self.user)
@@ -50,9 +50,9 @@ class AuthenticatedTests(AuthenticatedUserTestCase):
         """private threads list displays threads with reports"""
         """private threads list displays threads with reports"""
         override_acl(self.user, {'can_moderate_private_threads': True})
         override_acl(self.user, {'can_moderate_private_threads': True})
 
 
-        forum = Forum.objects.private_threads()
+        category = Category.objects.private_threads()
-        invisible_threads = [testutils.post_thread(forum) for t in xrange(10)]
+        invisible_threads = [testutils.post_thread(category) for t in xrange(10)]
-        visible_threads = [testutils.post_thread(forum) for t in xrange(10)]
+        visible_threads = [testutils.post_thread(category) for t in xrange(10)]
 
 
         for thread in visible_threads:
         for thread in visible_threads:
             thread.has_reported_posts = True
             thread.has_reported_posts = True

+ 38 - 38
misago/threads/tests/-test_replythread_view.py

@@ -4,7 +4,7 @@ from django.conf import settings
 from django.core.urlresolvers import reverse
 from django.core.urlresolvers import reverse
 
 
 from misago.acl.testutils import override_acl
 from misago.acl.testutils import override_acl
-from misago.forums.models import Forum
+from misago.categories.models import Category
 from misago.users.testutils import AuthenticatedUserTestCase
 from misago.users.testutils import AuthenticatedUserTestCase
 
 
 from misago.threads.models import Thread
 from misago.threads.models import Thread
@@ -17,70 +17,70 @@ class ReplyThreadTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
         super(ReplyThreadTests, self).setUp()
         super(ReplyThreadTests, self).setUp()
 
 
-        self.forum = Forum.objects.all_forums().filter(role="forum")[:1][0]
+        self.category = Category.objects.all_categories().filter(role='forum')[:1][0]
-        self.thread = post_thread(self.forum)
+        self.thread = post_thread(self.category)
         self.link = reverse('misago:reply_thread', kwargs={
         self.link = reverse('misago:reply_thread', kwargs={
-            'forum_id': self.forum.id,
+            'category_id': self.category.id,
             'thread_id': self.thread.id,
             'thread_id': self.thread.id,
         })
         })
 
 
     def allow_reply_thread(self, extra_acl=None):
     def allow_reply_thread(self, extra_acl=None):
-        forums_acl = self.user.acl
+        categories_acl = self.user.acl
-        forums_acl['visible_forums'].append(self.forum.pk)
+        categories_acl['visible_categories'].append(self.category.pk)
-        forums_acl['forums'][self.forum.pk] = {
+        categories_acl['categories'][self.category.pk] = {
             'can_see': 1,
             'can_see': 1,
             'can_browse': 1,
             'can_browse': 1,
             'can_see_all_threads': 1,
             'can_see_all_threads': 1,
             'can_reply_threads': 2,
             'can_reply_threads': 2,
         }
         }
         if extra_acl:
         if extra_acl:
-            forums_acl['forums'][self.forum.pk].update(extra_acl)
+            categories_acl['categories'][self.category.pk].update(extra_acl)
-        override_acl(self.user, forums_acl)
+        override_acl(self.user, categories_acl)
 
 
     def test_cant_see(self):
     def test_cant_see(self):
-        """has no permission to see forum"""
+        """has no permission to see category"""
-        forums_acl = self.user.acl
+        categories_acl = self.user.acl
-        forums_acl['visible_forums'].remove(self.forum.pk)
+        categories_acl['visible_categories'].remove(self.category.pk)
-        forums_acl['forums'][self.forum.pk] = {
+        categories_acl['categories'][self.category.pk] = {
             'can_see': 0,
             'can_see': 0,
             'can_browse': 0,
             'can_browse': 0,
             'can_see_all_threads': 1,
             'can_see_all_threads': 1,
             'can_reply_threads': 1,
             'can_reply_threads': 1,
         }
         }
-        override_acl(self.user, forums_acl)
+        override_acl(self.user, categories_acl)
 
 
         response = self.client.get(self.link, **self.ajax_header)
         response = self.client.get(self.link, **self.ajax_header)
         self.assertEqual(response.status_code, 404)
         self.assertEqual(response.status_code, 404)
 
 
     def test_cant_browse(self):
     def test_cant_browse(self):
-        """has no permission to browse forum"""
+        """has no permission to browse category"""
-        forums_acl = self.user.acl
+        categories_acl = self.user.acl
-        forums_acl['visible_forums'].append(self.forum.pk)
+        categories_acl['visible_categories'].append(self.category.pk)
-        forums_acl['forums'][self.forum.pk] = {
+        categories_acl['categories'][self.category.pk] = {
             'can_see': 1,
             'can_see': 1,
             'can_browse': 0,
             'can_browse': 0,
             'can_see_all_threads': 1,
             'can_see_all_threads': 1,
             'can_reply_threads': 1,
             'can_reply_threads': 1,
         }
         }
-        override_acl(self.user, forums_acl)
+        override_acl(self.user, categories_acl)
 
 
         response = self.client.get(self.link, **self.ajax_header)
         response = self.client.get(self.link, **self.ajax_header)
         self.assertEqual(response.status_code, 404)
         self.assertEqual(response.status_code, 404)
 
 
-    def test_cant_reply_thread_in_locked_forum(self):
+    def test_cant_reply_thread_in_locked_category(self):
-        """can't post in closed forum"""
+        """can't post in closed category"""
-        self.forum.is_closed = True
+        self.category.is_closed = True
-        self.forum.save()
+        self.category.save()
 
 
-        forums_acl = self.user.acl
+        categories_acl = self.user.acl
-        forums_acl['visible_forums'].append(self.forum.pk)
+        categories_acl['visible_categories'].append(self.category.pk)
-        forums_acl['forums'][self.forum.pk] = {
+        categories_acl['categories'][self.category.pk] = {
             'can_see': 1,
             'can_see': 1,
             'can_browse': 1,
             'can_browse': 1,
             'can_see_all_threads': 1,
             'can_see_all_threads': 1,
             'can_reply_threads': 1,
             'can_reply_threads': 1,
         }
         }
-        override_acl(self.user, forums_acl)
+        override_acl(self.user, categories_acl)
 
 
         response = self.client.get(self.link, **self.ajax_header)
         response = self.client.get(self.link, **self.ajax_header)
         self.assertEqual(response.status_code, 403)
         self.assertEqual(response.status_code, 403)
@@ -148,28 +148,28 @@ class ReplyThreadTests(AuthenticatedUserTestCase):
         self.thread = Thread.objects.get(id=self.thread.id)
         self.thread = Thread.objects.get(id=self.thread.id)
 
 
         self.assertEqual(self.thread.replies, 1)
         self.assertEqual(self.thread.replies, 1)
-        self.assertEqual(self.thread.forum_id, self.forum.pk)
+        self.assertEqual(self.thread.category_id, self.category.pk)
         self.assertEqual(self.thread.last_poster_id, updated_user.id)
         self.assertEqual(self.thread.last_poster_id, updated_user.id)
         self.assertEqual(self.thread.last_poster_name, updated_user.username)
         self.assertEqual(self.thread.last_poster_name, updated_user.username)
         self.assertEqual(self.thread.last_poster_slug, updated_user.slug)
         self.assertEqual(self.thread.last_poster_slug, updated_user.slug)
 
 
         last_post = self.user.post_set.all()[:1][0]
         last_post = self.user.post_set.all()[:1][0]
-        self.assertEqual(last_post.forum_id, self.forum.pk)
+        self.assertEqual(last_post.category_id, self.category.pk)
         self.assertEqual(last_post.original, 'Hello, I am test reply!')
         self.assertEqual(last_post.original, 'Hello, I am test reply!')
         self.assertEqual(last_post.poster_id, updated_user.id)
         self.assertEqual(last_post.poster_id, updated_user.id)
         self.assertEqual(last_post.poster_name, updated_user.username)
         self.assertEqual(last_post.poster_name, updated_user.username)
 
 
-        updated_forum = Forum.objects.get(id=self.forum.id)
+        updated_category = Category.objects.get(id=self.category.id)
-        self.assertEqual(updated_forum.threads, 1)
+        self.assertEqual(updated_category.threads, 1)
-        self.assertEqual(updated_forum.posts, 2)
+        self.assertEqual(updated_category.posts, 2)
-        self.assertEqual(updated_forum.last_thread_id, self.thread.id)
+        self.assertEqual(updated_category.last_thread_id, self.thread.id)
-        self.assertEqual(updated_forum.last_thread_title, self.thread.title)
+        self.assertEqual(updated_category.last_thread_title, self.thread.title)
-        self.assertEqual(updated_forum.last_thread_slug, self.thread.slug)
+        self.assertEqual(updated_category.last_thread_slug, self.thread.slug)
 
 
-        self.assertEqual(updated_forum.last_poster_id, updated_user.id)
+        self.assertEqual(updated_category.last_poster_id, updated_user.id)
-        self.assertEqual(updated_forum.last_poster_name,
+        self.assertEqual(updated_category.last_poster_name,
                          updated_user.username)
                          updated_user.username)
-        self.assertEqual(updated_forum.last_poster_slug, updated_user.slug)
+        self.assertEqual(updated_category.last_poster_slug, updated_user.slug)
 
 
     def test_can_close_replied_thread(self):
     def test_can_close_replied_thread(self):
         """can close/open thread while replying to it"""
         """can close/open thread while replying to it"""

+ 38 - 38
misago/threads/tests/-test_startthread_view.py

@@ -7,7 +7,7 @@ from django.conf import settings
 from django.core.urlresolvers import reverse
 from django.core.urlresolvers import reverse
 
 
 from misago.acl.testutils import override_acl
 from misago.acl.testutils import override_acl
-from misago.forums.models import Forum
+from misago.categories.models import Category
 from misago.users.testutils import AuthenticatedUserTestCase
 from misago.users.testutils import AuthenticatedUserTestCase
 
 
 from misago.threads.models import Label, Thread
 from misago.threads.models import Label, Thread
@@ -19,9 +19,9 @@ class StartThreadTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
         super(StartThreadTests, self).setUp()
         super(StartThreadTests, self).setUp()
 
 
-        self.forum = Forum.objects.all_forums().filter(role="forum")[:1][0]
+        self.category = Category.objects.all_categories().filter(role='forum')[:1][0]
         self.link = reverse('misago:start_thread', kwargs={
         self.link = reverse('misago:start_thread', kwargs={
-            'forum_id': self.forum.id
+            'category_id': self.category.id
         })
         })
 
 
         Label.objects.clear_cache()
         Label.objects.clear_cache()
@@ -30,59 +30,59 @@ class StartThreadTests(AuthenticatedUserTestCase):
         Label.objects.clear_cache()
         Label.objects.clear_cache()
 
 
     def allow_start_thread(self, extra_acl=None):
     def allow_start_thread(self, extra_acl=None):
-        forums_acl = self.user.acl
+        categories_acl = self.user.acl
-        forums_acl['visible_forums'].append(self.forum.pk)
+        categories_acl['visible_categories'].append(self.category.pk)
-        forums_acl['forums'][self.forum.pk] = {
+        categories_acl['categories'][self.category.pk] = {
             'can_see': 1,
             'can_see': 1,
             'can_browse': 1,
             'can_browse': 1,
             'can_start_threads': 1,
             'can_start_threads': 1,
         }
         }
 
 
         if extra_acl:
         if extra_acl:
-            forums_acl['forums'][self.forum.pk].update(extra_acl)
+            categories_acl['categories'][self.category.pk].update(extra_acl)
-        override_acl(self.user, forums_acl)
+        override_acl(self.user, categories_acl)
 
 
     def test_cant_see(self):
     def test_cant_see(self):
-        """has no permission to see forum"""
+        """has no permission to see category"""
-        forums_acl = self.user.acl
+        categories_acl = self.user.acl
-        forums_acl['visible_forums'].remove(self.forum.pk)
+        categories_acl['visible_categories'].remove(self.category.pk)
-        forums_acl['forums'][self.forum.pk] = {
+        categories_acl['categories'][self.category.pk] = {
             'can_see': 0,
             'can_see': 0,
             'can_browse': 0,
             'can_browse': 0,
             'can_start_threads': 1,
             'can_start_threads': 1,
         }
         }
-        override_acl(self.user, forums_acl)
+        override_acl(self.user, categories_acl)
 
 
         response = self.client.get(self.link, **self.ajax_header)
         response = self.client.get(self.link, **self.ajax_header)
         self.assertEqual(response.status_code, 404)
         self.assertEqual(response.status_code, 404)
 
 
     def test_cant_browse(self):
     def test_cant_browse(self):
-        """has no permission to browse forum"""
+        """has no permission to browse category"""
-        forums_acl = self.user.acl
+        categories_acl = self.user.acl
-        forums_acl['visible_forums'].append(self.forum.pk)
+        categories_acl['visible_categories'].append(self.category.pk)
-        forums_acl['forums'][self.forum.pk] = {
+        categories_acl['categories'][self.category.pk] = {
             'can_see': 1,
             'can_see': 1,
             'can_browse': 0,
             'can_browse': 0,
             'can_start_threads': 1,
             'can_start_threads': 1,
         }
         }
-        override_acl(self.user, forums_acl)
+        override_acl(self.user, categories_acl)
 
 
         response = self.client.get(self.link, **self.ajax_header)
         response = self.client.get(self.link, **self.ajax_header)
         self.assertEqual(response.status_code, 403)
         self.assertEqual(response.status_code, 403)
 
 
-    def test_cant_start_thread_in_locked_forum(self):
+    def test_cant_start_thread_in_locked_category(self):
-        """can't post in closed forum"""
+        """can't post in closed category"""
-        self.forum.is_closed = True
+        self.category.is_closed = True
-        self.forum.save()
+        self.category.save()
 
 
-        forums_acl = self.user.acl
+        categories_acl = self.user.acl
-        forums_acl['visible_forums'].append(self.forum.pk)
+        categories_acl['visible_categories'].append(self.category.pk)
-        forums_acl['forums'][self.forum.pk] = {
+        categories_acl['categories'][self.category.pk] = {
             'can_see': 1,
             'can_see': 1,
             'can_browse': 1,
             'can_browse': 1,
             'can_start_threads': 1,
             'can_start_threads': 1,
         }
         }
-        override_acl(self.user, forums_acl)
+        override_acl(self.user, categories_acl)
 
 
         response = self.client.get(self.link, **self.ajax_header)
         response = self.client.get(self.link, **self.ajax_header)
         self.assertEqual(response.status_code, 403)
         self.assertEqual(response.status_code, 403)
@@ -136,7 +136,7 @@ class StartThreadTests(AuthenticatedUserTestCase):
         self.assertEqual(updated_user.threads, 1)
         self.assertEqual(updated_user.threads, 1)
         self.assertEqual(updated_user.posts, 1)
         self.assertEqual(updated_user.posts, 1)
 
 
-        self.assertEqual(last_thread.forum_id, self.forum.pk)
+        self.assertEqual(last_thread.category_id, self.category.pk)
         self.assertEqual(last_thread.title, "Hello, I am test thread!")
         self.assertEqual(last_thread.title, "Hello, I am test thread!")
         self.assertEqual(last_thread.starter_id, updated_user.id)
         self.assertEqual(last_thread.starter_id, updated_user.id)
         self.assertEqual(last_thread.starter_name, updated_user.username)
         self.assertEqual(last_thread.starter_name, updated_user.username)
@@ -146,22 +146,22 @@ class StartThreadTests(AuthenticatedUserTestCase):
         self.assertEqual(last_thread.last_poster_slug, updated_user.slug)
         self.assertEqual(last_thread.last_poster_slug, updated_user.slug)
 
 
         last_post = self.user.post_set.all()[:1][0]
         last_post = self.user.post_set.all()[:1][0]
-        self.assertEqual(last_post.forum_id, self.forum.pk)
+        self.assertEqual(last_post.category_id, self.category.pk)
         self.assertEqual(last_post.original, 'Lorem ipsum dolor met!')
         self.assertEqual(last_post.original, 'Lorem ipsum dolor met!')
         self.assertEqual(last_post.poster_id, updated_user.id)
         self.assertEqual(last_post.poster_id, updated_user.id)
         self.assertEqual(last_post.poster_name, updated_user.username)
         self.assertEqual(last_post.poster_name, updated_user.username)
 
 
-        updated_forum = Forum.objects.get(id=self.forum.id)
+        updated_category = Category.objects.get(id=self.category.id)
-        self.assertEqual(updated_forum.threads, 1)
+        self.assertEqual(updated_category.threads, 1)
-        self.assertEqual(updated_forum.posts, 1)
+        self.assertEqual(updated_category.posts, 1)
-        self.assertEqual(updated_forum.last_thread_id, last_thread.id)
+        self.assertEqual(updated_category.last_thread_id, last_thread.id)
-        self.assertEqual(updated_forum.last_thread_title, last_thread.title)
+        self.assertEqual(updated_category.last_thread_title, last_thread.title)
-        self.assertEqual(updated_forum.last_thread_slug, last_thread.slug)
+        self.assertEqual(updated_category.last_thread_slug, last_thread.slug)
 
 
-        self.assertEqual(updated_forum.last_poster_id, updated_user.id)
+        self.assertEqual(updated_category.last_poster_id, updated_user.id)
-        self.assertEqual(updated_forum.last_poster_name,
+        self.assertEqual(updated_category.last_poster_name,
                          updated_user.username)
                          updated_user.username)
-        self.assertEqual(updated_forum.last_poster_slug, updated_user.slug)
+        self.assertEqual(updated_category.last_poster_slug, updated_user.slug)
 
 
     def test_start_closed_thread(self):
     def test_start_closed_thread(self):
         """can post closed thread"""
         """can post closed thread"""
@@ -215,7 +215,7 @@ class StartThreadTests(AuthenticatedUserTestCase):
         field_name = '%s-label' % prefix
         field_name = '%s-label' % prefix
 
 
         label = Label.objects.create(name="Label", slug="label")
         label = Label.objects.create(name="Label", slug="label")
-        label.forums.add(self.forum)
+        label.categories.add(self.category)
 
 
         self.allow_start_thread({'can_change_threads_labels': 1})
         self.allow_start_thread({'can_change_threads_labels': 1})
         response = self.client.get(self.link, **self.ajax_header)
         response = self.client.get(self.link, **self.ajax_header)

+ 34 - 32
misago/threads/tests/-test_thread_view.py

@@ -1,7 +1,7 @@
 from django.core.urlresolvers import reverse
 from django.core.urlresolvers import reverse
 
 
 from misago.acl.testutils import override_acl
 from misago.acl.testutils import override_acl
-from misago.forums.models import Forum
+from misago.categories.models import Category
 from misago.users.testutils import AuthenticatedUserTestCase
 from misago.users.testutils import AuthenticatedUserTestCase
 
 
 from misago.threads.models import Thread, Label
 from misago.threads.models import Thread, Label
@@ -12,20 +12,20 @@ class ThreadViewTestCase(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
         super(ThreadViewTestCase, self).setUp()
         super(ThreadViewTestCase, self).setUp()
 
 
-        self.forum = Forum.objects.all_forums().filter(role="forum")[:1][0]
+        self.category = Category.objects.all_categories().filter(role='forum')[:1][0]
-        self.forum.labels = []
+        self.category.labels = []
 
 
-        self.thread = post_thread(self.forum)
+        self.thread = post_thread(self.category)
 
 
-    def override_acl(self, new_acl, forum=None):
+    def override_acl(self, new_acl, category=None):
-        forum = forum or self.forum
+        category = category or self.category
 
 
         new_acl.update({'can_see': True, 'can_browse': True})
         new_acl.update({'can_see': True, 'can_browse': True})
 
 
-        forums_acl = self.user.acl
+        categories_acl = self.user.acl
-        forums_acl['visible_forums'].append(forum.pk)
+        categories_acl['visible_categories'].append(category.pk)
-        forums_acl['forums'][forum.pk] = new_acl
+        categories_acl['categories'][category.pk] = new_acl
-        override_acl(self.user, forums_acl)
+        override_acl(self.user, categories_acl)
 
 
     def reload_thread(self):
     def reload_thread(self):
         self.thread = Thread.objects.get(id=self.thread.id)
         self.thread = Thread.objects.get(id=self.thread.id)
@@ -44,7 +44,7 @@ class ThreadViewTests(ThreadViewTestCase):
         self.assertEqual(response.status_code, 404)
         self.assertEqual(response.status_code, 404)
 
 
     def test_can_see_all_threads_false_owned_thread(self):
     def test_can_see_all_threads_false_owned_thread(self):
-        """user can see thread he started in private forum"""
+        """user can see thread he started in private category"""
         self.override_acl({
         self.override_acl({
             'can_see_all_threads': False,
             'can_see_all_threads': False,
             'can_see_own_threads': True
             'can_see_own_threads': True
@@ -78,12 +78,12 @@ class ThreadViewModerationTests(ThreadViewTestCase):
         super(ThreadViewModerationTests, self).tearDown()
         super(ThreadViewModerationTests, self).tearDown()
         Label.objects.clear_cache()
         Label.objects.clear_cache()
 
 
-    def override_acl(self, new_acl, forum=None):
+    def override_acl(self, new_acl, category=None):
         new_acl.update({
         new_acl.update({
             'can_see_all_threads': True,
             'can_see_all_threads': True,
             'can_see_own_threads': False
             'can_see_own_threads': False
         })
         })
-        super(ThreadViewModerationTests, self).override_acl(new_acl, forum)
+        super(ThreadViewModerationTests, self).override_acl(new_acl, category)
 
 
     def test_label_thread(self):
     def test_label_thread(self):
         """its possible to set thread label"""
         """its possible to set thread label"""
@@ -98,7 +98,7 @@ class ThreadViewModerationTests(ThreadViewTestCase):
         self.assertNotIn("Thread actions", response.content)
         self.assertNotIn("Thread actions", response.content)
 
 
         test_label = Label.objects.create(name="Foxtrot", slug="foxtrot")
         test_label = Label.objects.create(name="Foxtrot", slug="foxtrot")
-        test_label.forums.add(self.forum)
+        test_label.categories.add(self.category)
         Label.objects.clear_cache()
         Label.objects.clear_cache()
 
 
         self.override_acl({'can_change_threads_labels': 0})
         self.override_acl({'can_change_threads_labels': 0})
@@ -122,9 +122,9 @@ class ThreadViewModerationTests(ThreadViewTestCase):
     def test_change_thread_label(self):
     def test_change_thread_label(self):
         """its possible to change thread label"""
         """its possible to change thread label"""
         test_label = Label.objects.create(name="Foxtrot", slug="foxtrot")
         test_label = Label.objects.create(name="Foxtrot", slug="foxtrot")
-        test_label.forums.add(self.forum)
+        test_label.categories.add(self.category)
         other_label = Label.objects.create(name="Uniform", slug="uniform")
         other_label = Label.objects.create(name="Uniform", slug="uniform")
-        other_label.forums.add(self.forum)
+        other_label.categories.add(self.category)
 
 
         Label.objects.clear_cache()
         Label.objects.clear_cache()
 
 
@@ -155,7 +155,7 @@ class ThreadViewModerationTests(ThreadViewTestCase):
     def test_unlabel_thread(self):
     def test_unlabel_thread(self):
         """its possible to unset thread label"""
         """its possible to unset thread label"""
         test_label = Label.objects.create(name="Foxtrot", slug="foxtrot")
         test_label = Label.objects.create(name="Foxtrot", slug="foxtrot")
-        test_label.forums.add(self.forum)
+        test_label.categories.add(self.category)
         Label.objects.clear_cache()
         Label.objects.clear_cache()
 
 
         self.thread.label = test_label
         self.thread.label = test_label
@@ -267,24 +267,26 @@ class ThreadViewModerationTests(ThreadViewTestCase):
         response = self.client.post(self.thread.get_absolute_url(),
         response = self.client.post(self.thread.get_absolute_url(),
                                     data={'thread_action': 'move'})
                                     data={'thread_action': 'move'})
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
-        self.assertIn("Move thread to forum:", response.content)
+        self.assertIn("Move thread to category:", response.content)
 
 
-        new_forum = Forum(name="New Forum",
+        new_category = Category(
-                          slug="new-forum",
+            name="New Category",
-                          role="forum")
+            slug="new-category",
-        new_forum.insert_at(self.forum.parent, save=True)
+            role='forum'
+        )
+        new_category.insert_at(self.category.parent, save=True)
 
 
         self.override_acl({'can_move_threads': 1})
         self.override_acl({'can_move_threads': 1})
-        self.override_acl({'can_move_threads': 1}, new_forum)
+        self.override_acl({'can_move_threads': 1}, new_category)
         response = self.client.post(self.thread.get_absolute_url(), data={
         response = self.client.post(self.thread.get_absolute_url(), data={
             'thread_action': 'move',
             'thread_action': 'move',
-            'new_forum': unicode(new_forum.id),
+            'new_category': unicode(new_category.id),
             'submit': '1'
             'submit': '1'
         })
         })
         self.assertEqual(response.status_code, 302)
         self.assertEqual(response.status_code, 302)
-        self.assertEqual(self.reload_thread().forum, new_forum)
+        self.assertEqual(self.reload_thread().category, new_category)
 
 
-        # we made forum "empty", assert that board index renders
+        # we made category "empty", assert that board index renders
         response = self.client.get(reverse('misago:index'))
         response = self.client.get(reverse('misago:index'))
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
 
 
@@ -301,7 +303,7 @@ class ThreadViewModerationTests(ThreadViewTestCase):
         self.assertEqual(response.status_code, 302)
         self.assertEqual(response.status_code, 302)
         self.assertTrue(self.reload_thread().is_hidden)
         self.assertTrue(self.reload_thread().is_hidden)
 
 
-        # we made forum "empty", assert that board index renders
+        # we made category "empty", assert that board index renders
         response = self.client.get(reverse('misago:index'))
         response = self.client.get(reverse('misago:index'))
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
 
 
@@ -330,7 +332,7 @@ class ThreadViewModerationTests(ThreadViewTestCase):
                                     data={'thread_action': 'delete'})
                                     data={'thread_action': 'delete'})
         self.assertEqual(response.status_code, 302)
         self.assertEqual(response.status_code, 302)
 
 
-        # we made forum empty, assert that board index renders
+        # we made category empty, assert that board index renders
         response = self.client.get(reverse('misago:index'))
         response = self.client.get(reverse('misago:index'))
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
 
 
@@ -461,7 +463,7 @@ class ThreadViewModerationTests(ThreadViewTestCase):
         response = self.client.post(self.thread.get_absolute_url(), data={
         response = self.client.post(self.thread.get_absolute_url(), data={
             'action': 'move',
             'action': 'move',
             'item': [p.pk for p in posts],
             'item': [p.pk for p in posts],
-            'new_thread_url': self.forum.get_absolute_url(),
+            'new_thread_url': self.category.get_absolute_url(),
             'submit': ''
             'submit': ''
         })
         })
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
@@ -485,7 +487,7 @@ class ThreadViewModerationTests(ThreadViewTestCase):
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
         self.assertIn('Move posts', response.content)
         self.assertIn('Move posts', response.content)
 
 
-        other_thread = post_thread(self.forum)
+        other_thread = post_thread(self.category)
 
 
         self.override_acl(test_acl)
         self.override_acl(test_acl)
         response = self.client.post(self.thread.get_absolute_url(), data={
         response = self.client.post(self.thread.get_absolute_url(), data={
@@ -543,7 +545,7 @@ class ThreadViewModerationTests(ThreadViewTestCase):
         response = self.client.post(self.thread.get_absolute_url(), data={
         response = self.client.post(self.thread.get_absolute_url(), data={
             'action': 'split',
             'action': 'split',
             'item': [p.pk for p in posts[:3]],
             'item': [p.pk for p in posts[:3]],
-            'forum': self.forum.id,
+            'category': self.category.id,
             'thread_title': 'Split thread',
             'thread_title': 'Split thread',
             'submit': ''
             'submit': ''
         })
         })
@@ -556,7 +558,7 @@ class ThreadViewModerationTests(ThreadViewTestCase):
         response = self.client.post(self.thread.get_absolute_url(), data={
         response = self.client.post(self.thread.get_absolute_url(), data={
             'action': 'split',
             'action': 'split',
             'item': [posts[-1].pk],
             'item': [posts[-1].pk],
-            'forum': self.forum.id,
+            'category': self.category.id,
             'thread_title': 'Split thread',
             'thread_title': 'Split thread',
             'follow': ''
             'follow': ''
         })
         })

+ 3 - 3
misago/threads/tests/-test_threadparticipants_views.py

@@ -3,7 +3,7 @@ from django.core.urlresolvers import reverse
 from django.utils import timezone
 from django.utils import timezone
 
 
 from misago.acl.testutils import override_acl
 from misago.acl.testutils import override_acl
-from misago.forums.models import Forum
+from misago.categories.models import Category
 from misago.users.testutils import AuthenticatedUserTestCase
 from misago.users.testutils import AuthenticatedUserTestCase
 
 
 from misago.threads import testutils
 from misago.threads import testutils
@@ -16,8 +16,8 @@ class ThreadParticipantsTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
         super(ThreadParticipantsTests, self).setUp()
         super(ThreadParticipantsTests, self).setUp()
 
 
-        self.forum = Forum.objects.private_threads()
+        self.category = Category.objects.private_threads()
-        self.thread = testutils.post_thread(self.forum)
+        self.thread = testutils.post_thread(self.category)
 
 
     def test_participants_list(self):
     def test_participants_list(self):
         """participants list displays thread participants"""
         """participants list displays thread participants"""

+ 3 - 3
misago/threads/tests/-test_unreadthreads_view.py

@@ -2,7 +2,7 @@ from django.core.urlresolvers import reverse
 from django.utils import timezone
 from django.utils import timezone
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
 
 
-from misago.forums.models import Forum
+from misago.categories.models import Category
 from misago.users.testutils import UserTestCase, AuthenticatedUserTestCase
 from misago.users.testutils import UserTestCase, AuthenticatedUserTestCase
 
 
 from misago.threads import testutils
 from misago.threads import testutils
@@ -17,8 +17,8 @@ class AuthenticatedTests(AuthenticatedUserTestCase):
 
 
     def test_filled_threads_list(self):
     def test_filled_threads_list(self):
         """filled threads list is rendered"""
         """filled threads list is rendered"""
-        forum = Forum.objects.all_forums().filter(role="forum")[:1][0]
+        category = Category.objects.all_categories().filter(role='forum')[:1][0]
-        threads = [testutils.post_thread(forum) for t in xrange(10)]
+        threads = [testutils.post_thread(category) for t in xrange(10)]
 
 
         # only unread tracker threads are shown on unread list
         # only unread tracker threads are shown on unread list
         response = self.client.get(reverse('misago:unread_threads'))
         response = self.client.get(reverse('misago:unread_threads'))

+ 12 - 12
misago/threads/tests/test_counters.py

@@ -5,7 +5,7 @@ from django.core.urlresolvers import reverse
 from django.utils import timezone
 from django.utils import timezone
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
 
 
-from misago.forums.models import Forum
+from misago.categories.models import Category
 from misago.readtracker.models import ThreadRead
 from misago.readtracker.models import ThreadRead
 from misago.users.testutils import AuthenticatedUserTestCase
 from misago.users.testutils import AuthenticatedUserTestCase
 
 
@@ -18,14 +18,14 @@ class TestNewThreadsCount(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
         super(TestNewThreadsCount, self).setUp()
         super(TestNewThreadsCount, self).setUp()
 
 
-        self.forum = Forum.objects.all_forums().filter(role="forum")[:1][0]
+        self.category = Category.objects.all_categories().filter(role='category')[:1][0]
 
 
     def test_cast_to_int(self):
     def test_cast_to_int(self):
         """counter is castable to int"""
         """counter is castable to int"""
         counter = NewThreadsCount(self.user, {})
         counter = NewThreadsCount(self.user, {})
         self.assertEqual(int(counter), 0)
         self.assertEqual(int(counter), 0)
 
 
-        threads = [testutils.post_thread(self.forum) for t in xrange(42)]
+        threads = [testutils.post_thread(self.category) for t in xrange(42)]
         counter = NewThreadsCount(self.user, {})
         counter = NewThreadsCount(self.user, {})
         self.assertEqual(int(counter), 42)
         self.assertEqual(int(counter), 42)
 
 
@@ -34,7 +34,7 @@ class TestNewThreadsCount(AuthenticatedUserTestCase):
         counter = NewThreadsCount(self.user, {})
         counter = NewThreadsCount(self.user, {})
         self.assertFalse(counter)
         self.assertFalse(counter)
 
 
-        threads = [testutils.post_thread(self.forum) for t in xrange(42)]
+        threads = [testutils.post_thread(self.category) for t in xrange(42)]
         counter = NewThreadsCount(self.user, {})
         counter = NewThreadsCount(self.user, {})
         self.assertTrue(counter)
         self.assertTrue(counter)
 
 
@@ -69,7 +69,7 @@ class TestNewThreadsCount(AuthenticatedUserTestCase):
         self.assertEqual(counter.get_current_count_dict()['threads'], 0)
         self.assertEqual(counter.get_current_count_dict()['threads'], 0)
 
 
         # create 10 new threads
         # create 10 new threads
-        threads = [testutils.post_thread(self.forum) for t in xrange(10)]
+        threads = [testutils.post_thread(self.category) for t in xrange(10)]
         self.assertEqual(counter.get_current_count_dict()['threads'], 10)
         self.assertEqual(counter.get_current_count_dict()['threads'], 10)
 
 
         # create new counter
         # create new counter
@@ -110,7 +110,7 @@ class TestSyncUnreadPrivateThreadsCount(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
         super(TestSyncUnreadPrivateThreadsCount, self).setUp()
         super(TestSyncUnreadPrivateThreadsCount, self).setUp()
 
 
-        self.forum = Forum.objects.private_threads()
+        self.category = Category.objects.private_threads()
         self.user.sync_unread_private_threads = True
         self.user.sync_unread_private_threads = True
 
 
     def test_user_with_no_threads(self):
     def test_user_with_no_threads(self):
@@ -118,7 +118,7 @@ class TestSyncUnreadPrivateThreadsCount(AuthenticatedUserTestCase):
         for i in range(5):
         for i in range(5):
             # post 5 invisible threads
             # post 5 invisible threads
             testutils.post_thread(
             testutils.post_thread(
-                self.forum, started_on=timezone.now() - timedelta(days=2))
+                self.category, started_on=timezone.now() - timedelta(days=2))
 
 
         sync_user_unread_private_threads_count(self.user)
         sync_user_unread_private_threads_count(self.user)
         self.assertEqual(self.user.unread_private_threads, 0)
         self.assertEqual(self.user.unread_private_threads, 0)
@@ -128,10 +128,10 @@ class TestSyncUnreadPrivateThreadsCount(AuthenticatedUserTestCase):
         for i in range(5):
         for i in range(5):
             # post 5 invisible threads
             # post 5 invisible threads
             testutils.post_thread(
             testutils.post_thread(
-                self.forum, started_on=timezone.now() - timedelta(days=2))
+                self.category, started_on=timezone.now() - timedelta(days=2))
 
 
         thread = testutils.post_thread(
         thread = testutils.post_thread(
-            self.forum, started_on=timezone.now() - timedelta(days=2))
+            self.category, started_on=timezone.now() - timedelta(days=2))
         thread.threadparticipant_set.create(user=self.user)
         thread.threadparticipant_set.create(user=self.user)
 
 
         sync_user_unread_private_threads_count(self.user)
         sync_user_unread_private_threads_count(self.user)
@@ -142,15 +142,15 @@ class TestSyncUnreadPrivateThreadsCount(AuthenticatedUserTestCase):
         for i in range(5):
         for i in range(5):
             # post 5 invisible threads
             # post 5 invisible threads
             testutils.post_thread(
             testutils.post_thread(
-                self.forum, started_on=timezone.now() - timedelta(days=2))
+                self.category, started_on=timezone.now() - timedelta(days=2))
 
 
         thread = testutils.post_thread(
         thread = testutils.post_thread(
-            self.forum, started_on=timezone.now() - timedelta(days=2))
+            self.category, started_on=timezone.now() - timedelta(days=2))
         thread.threadparticipant_set.create(user=self.user)
         thread.threadparticipant_set.create(user=self.user)
 
 
         ThreadRead.objects.create(
         ThreadRead.objects.create(
             user=self.user,
             user=self.user,
-            forum=self.forum,
+            category=self.category,
             thread=thread,
             thread=thread,
             last_read_on=timezone.now() - timedelta(days=3))
             last_read_on=timezone.now() - timedelta(days=3))
 
 

+ 4 - 4
misago/threads/tests/test_event_model.py

@@ -2,7 +2,7 @@ from django.contrib.auth import get_user_model
 from django.test import TestCase
 from django.test import TestCase
 from django.utils import timezone
 from django.utils import timezone
 
 
-from misago.forums.models import Forum
+from misago.categories.models import Category
 
 
 from misago.threads.checksums import is_event_valid, update_event_checksum
 from misago.threads.checksums import is_event_valid, update_event_checksum
 from misago.threads.models import Thread, Event
 from misago.threads.models import Thread, Event
@@ -15,9 +15,9 @@ class EventModelTests(TestCase):
 
 
         datetime = timezone.now()
         datetime = timezone.now()
 
 
-        self.forum = Forum.objects.filter(role="forum")[:1][0]
+        self.category = Category.objects.filter(role='forum')[:1][0]
         self.thread = Thread(
         self.thread = Thread(
-            forum=self.forum,
+            category=self.category,
             started_on=datetime,
             started_on=datetime,
             starter_name='Tester',
             starter_name='Tester',
             starter_slug='tester',
             starter_slug='tester',
@@ -31,7 +31,7 @@ class EventModelTests(TestCase):
     def test_is_event_valid(self):
     def test_is_event_valid(self):
         """event is_valid flag returns valid value"""
         """event is_valid flag returns valid value"""
         event = Event.objects.create(
         event = Event.objects.create(
-            forum=self.forum,
+            category=self.category,
             thread=self.thread,
             thread=self.thread,
             author=self.user,
             author=self.user,
             message="Lorem ipsum",
             message="Lorem ipsum",

+ 4 - 4
misago/threads/tests/test_events.py

@@ -4,7 +4,7 @@ from django.test import TestCase
 from django.utils import timezone
 from django.utils import timezone
 
 
 from misago.acl import add_acl
 from misago.acl import add_acl
-from misago.forums.models import Forum
+from misago.categories.models import Category
 
 
 from misago.threads.events import record_event, add_events_to_posts
 from misago.threads.events import record_event, add_events_to_posts
 from misago.threads.models import Thread, Event
 from misago.threads.models import Thread, Event
@@ -18,9 +18,9 @@ class EventsAPITests(TestCase):
 
 
         datetime = timezone.now()
         datetime = timezone.now()
 
 
-        self.forum = Forum.objects.filter(role="forum")[:1][0]
+        self.category = Category.objects.filter(role='forum')[:1][0]
         self.thread = Thread(
         self.thread = Thread(
-            forum=self.forum,
+            category=self.category,
             started_on=datetime,
             started_on=datetime,
             starter_name='Tester',
             starter_name='Tester',
             starter_slug='tester',
             starter_slug='tester',
@@ -31,7 +31,7 @@ class EventsAPITests(TestCase):
         self.thread.set_title("Test thread")
         self.thread.set_title("Test thread")
         self.thread.save()
         self.thread.save()
 
 
-        add_acl(self.user, self.forum)
+        add_acl(self.user, self.category)
         add_acl(self.user, self.thread)
         add_acl(self.user, self.thread)
 
 
     def test_record_event(self):
     def test_record_event(self):

+ 8 - 8
misago/threads/tests/test_events_view.py

@@ -1,7 +1,7 @@
 from django.core.urlresolvers import reverse
 from django.core.urlresolvers import reverse
 
 
 from misago.acl.testutils import override_acl
 from misago.acl.testutils import override_acl
-from misago.forums.models import Forum
+from misago.categories.models import Category
 from misago.users.testutils import AuthenticatedUserTestCase
 from misago.users.testutils import AuthenticatedUserTestCase
 
 
 from misago.threads.models import Thread, Event
 from misago.threads.models import Thread, Event
@@ -14,10 +14,10 @@ class EventsViewTestCase(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
         super(EventsViewTestCase, self).setUp()
         super(EventsViewTestCase, self).setUp()
 
 
-        self.forum = Forum.objects.all_forums().filter(role="forum")[:1][0]
+        self.category = Category.objects.all_categories().filter(role='forum')[:1][0]
-        self.forum.labels = []
+        self.category.labels = []
 
 
-        self.thread = post_thread(self.forum)
+        self.thread = post_thread(self.category)
 
 
     def override_acl(self, new_acl):
     def override_acl(self, new_acl):
         new_acl.update({
         new_acl.update({
@@ -28,10 +28,10 @@ class EventsViewTestCase(AuthenticatedUserTestCase):
             'can_pin_threads': True
             'can_pin_threads': True
         })
         })
 
 
-        forums_acl = self.user.acl
+        categories_acl = self.user.acl
-        forums_acl['visible_forums'].append(self.forum.pk)
+        categories_acl['visible_categories'].append(self.category.pk)
-        forums_acl['forums'][self.forum.pk] = new_acl
+        categories_acl['categories'][self.category.pk] = new_acl
-        override_acl(self.user, forums_acl)
+        override_acl(self.user, categories_acl)
 
 
     def test_hide_event(self):
     def test_hide_event(self):
         """its possible to hide event"""
         """its possible to hide event"""

+ 5 - 5
misago/threads/tests/test_goto.py

@@ -1,7 +1,7 @@
 from django.conf import settings
 from django.conf import settings
 
 
 from misago.acl import add_acl
 from misago.acl import add_acl
-from misago.forums.models import Forum
+from misago.categories.models import Category
 from misago.readtracker import threadstracker
 from misago.readtracker import threadstracker
 from misago.users.testutils import AuthenticatedUserTestCase
 from misago.users.testutils import AuthenticatedUserTestCase
 
 
@@ -27,11 +27,11 @@ class GotoTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
         super(GotoTests, self).setUp()
         super(GotoTests, self).setUp()
 
 
-        self.forum = Forum.objects.all_forums().filter(role="forum")[:1][0]
+        self.category = Category.objects.all_categories().filter(role='forum')[:1][0]
-        self.forum.labels = []
+        self.category.labels = []
 
 
-        self.thread = post_thread(self.forum)
+        self.thread = post_thread(self.category)
-        add_acl(self.user, self.forum)
+        add_acl(self.user, self.category)
         add_acl(self.user, self.thread)
         add_acl(self.user, self.thread)
 
 
     def test_get_thread_pages(self):
     def test_get_thread_pages(self):

+ 12 - 14
misago/threads/tests/test_label_model.py

@@ -1,7 +1,5 @@
 from django.test import TestCase
 from django.test import TestCase
-
+from misago.categories.models import Category
-from misago.forums.models import Forum
-
 from misago.threads.models import Label
 from misago.threads.models import Label
 
 
 
 
@@ -31,9 +29,9 @@ class LabelsManagerTests(TestCase):
         for label in test_labels:
         for label in test_labels:
             self.assertEqual(db_labels[label.pk], label)
             self.assertEqual(db_labels[label.pk], label)
 
 
-    def test_get_forum_labels(self):
+    def test_get_category_labels(self):
-        """get_forum_labels returns labels for forum"""
+        """get_category_labels returns labels for category"""
-        forum = Forum.objects.all_forums().filter(role='forum')[:1][0]
+        category = Category.objects.all_categories().filter(role='forum')[:1][0]
 
 
         test_labels = (
         test_labels = (
             Label.objects.create(name="Label 1", slug="label-1"),
             Label.objects.create(name="Label 1", slug="label-1"),
@@ -42,12 +40,12 @@ class LabelsManagerTests(TestCase):
             Label.objects.create(name="Label 4", slug="label-4"),
             Label.objects.create(name="Label 4", slug="label-4"),
         )
         )
 
 
-        test_labels[0].forums.add(forum)
+        test_labels[0].categories.add(category)
-        test_labels[2].forums.add(forum)
+        test_labels[2].categories.add(category)
 
 
-        forum_labels = Label.objects.get_forum_labels(forum)
+        category_labels = Label.objects.get_category_labels(category)
-        self.assertEqual(len(forum_labels), 2)
+        self.assertEqual(len(category_labels), 2)
-        self.assertIn(test_labels[0], forum_labels)
+        self.assertIn(test_labels[0], category_labels)
-        self.assertIn(test_labels[2], forum_labels)
+        self.assertIn(test_labels[2], category_labels)
-        self.assertNotIn(test_labels[1], forum_labels)
+        self.assertNotIn(test_labels[1], category_labels)
-        self.assertNotIn(test_labels[3], forum_labels)
+        self.assertNotIn(test_labels[3], category_labels)

+ 23 - 23
misago/threads/tests/test_labelsadmin_views.py

@@ -1,7 +1,7 @@
 from django.core.urlresolvers import reverse
 from django.core.urlresolvers import reverse
 
 
 from misago.admin.testutils import AdminTestCase
 from misago.admin.testutils import AdminTestCase
-from misago.forums.models import Forum
+from misago.categories.models import Category
 
 
 from misago.threads.models import Label
 from misago.threads.models import Label
 
 
@@ -10,14 +10,14 @@ class LabelAdminViewsTests(AdminTestCase):
     def test_link_registered(self):
     def test_link_registered(self):
         """admin nav contains labels link"""
         """admin nav contains labels link"""
         response = self.client.get(
         response = self.client.get(
-            reverse('misago:admin:forums:nodes:index'))
+            reverse('misago:admin:categories:nodes:index'))
-        self.assertIn(reverse('misago:admin:forums:labels:index'),
+        self.assertIn(reverse('misago:admin:categories:labels:index'),
                       response.content)
                       response.content)
 
 
     def test_list_view(self):
     def test_list_view(self):
         """labels list view returns 200"""
         """labels list view returns 200"""
         response = self.client.get(
         response = self.client.get(
-            reverse('misago:admin:forums:labels:index'))
+            reverse('misago:admin:categories:labels:index'))
 
 
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
         self.assertIn('No thread labels', response.content)
         self.assertIn('No thread labels', response.content)
@@ -25,61 +25,61 @@ class LabelAdminViewsTests(AdminTestCase):
     def test_new_view(self):
     def test_new_view(self):
         """new label view has no showstoppers"""
         """new label view has no showstoppers"""
         response = self.client.get(
         response = self.client.get(
-            reverse('misago:admin:forums:labels:new'))
+            reverse('misago:admin:categories:labels:new'))
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
 
 
         response = self.client.post(
         response = self.client.post(
-            reverse('misago:admin:forums:labels:new'),
+            reverse('misago:admin:categories:labels:new'),
             data={
             data={
                 'name': 'Test Label',
                 'name': 'Test Label',
                 'css_class': 'test_label',
                 'css_class': 'test_label',
-                'forums': [f.pk for f in Forum.objects.all_forums()],
+                'categories': [f.pk for f in Category.objects.all_categories()],
             })
             })
         self.assertEqual(response.status_code, 302)
         self.assertEqual(response.status_code, 302)
 
 
         response = self.client.get(
         response = self.client.get(
-            reverse('misago:admin:forums:labels:index'))
+            reverse('misago:admin:categories:labels:index'))
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
         self.assertIn('Test Label', response.content)
         self.assertIn('Test Label', response.content)
         self.assertIn('test_label', response.content)
         self.assertIn('test_label', response.content)
 
 
         test_label = Label.objects.get(slug='test-label')
         test_label = Label.objects.get(slug='test-label')
-        self.assertEqual(len(test_label.forums.all()),
+        self.assertEqual(len(test_label.categories.all()),
-                         len(Forum.objects.all_forums()))
+                         len(Category.objects.all_categories()))
-        for forum in Forum.objects.all_forums():
+        for category in Category.objects.all_categories():
-            self.assertIn(forum, test_label.forums.all())
+            self.assertIn(category, test_label.categories.all())
 
 
     def test_edit_view(self):
     def test_edit_view(self):
         """edit label view has no showstoppers"""
         """edit label view has no showstoppers"""
         self.client.post(
         self.client.post(
-            reverse('misago:admin:forums:labels:new'),
+            reverse('misago:admin:categories:labels:new'),
             data={
             data={
                 'name': 'Test Label',
                 'name': 'Test Label',
                 'css_class': 'test_label',
                 'css_class': 'test_label',
-                'forums': [f.pk for f in Forum.objects.all_forums()],
+                'categories': [f.pk for f in Category.objects.all_categories()],
             })
             })
         test_label = Label.objects.get(slug='test-label')
         test_label = Label.objects.get(slug='test-label')
 
 
         response = self.client.get(
         response = self.client.get(
-            reverse('misago:admin:forums:labels:edit',
+            reverse('misago:admin:categories:labels:edit',
                     kwargs={'label_id': test_label.pk}))
                     kwargs={'label_id': test_label.pk}))
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
         self.assertIn(test_label.name, response.content)
         self.assertIn(test_label.name, response.content)
         self.assertIn(test_label.css_class, response.content)
         self.assertIn(test_label.css_class, response.content)
 
 
         response = self.client.post(
         response = self.client.post(
-            reverse('misago:admin:forums:labels:edit',
+            reverse('misago:admin:categories:labels:edit',
                     kwargs={'label_id': test_label.pk}),
                     kwargs={'label_id': test_label.pk}),
             data={
             data={
                 'name': 'Top Lel',
                 'name': 'Top Lel',
                 'css_class': 'test_lel',
                 'css_class': 'test_lel',
-                'forums': [f.pk for f in Forum.objects.all_forums()],
+                'categories': [f.pk for f in Category.objects.all_categories()],
             })
             })
         self.assertEqual(response.status_code, 302)
         self.assertEqual(response.status_code, 302)
 
 
         test_label = Label.objects.get(slug='top-lel')
         test_label = Label.objects.get(slug='top-lel')
         response = self.client.get(
         response = self.client.get(
-            reverse('misago:admin:forums:labels:index'))
+            reverse('misago:admin:categories:labels:index'))
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
         self.assertIn(test_label.name, response.content)
         self.assertIn(test_label.name, response.content)
         self.assertIn(test_label.css_class, response.content)
         self.assertIn(test_label.css_class, response.content)
@@ -87,22 +87,22 @@ class LabelAdminViewsTests(AdminTestCase):
     def test_delete_view(self):
     def test_delete_view(self):
         """delete label view has no showstoppers"""
         """delete label view has no showstoppers"""
         self.client.post(
         self.client.post(
-            reverse('misago:admin:forums:labels:new'),
+            reverse('misago:admin:categories:labels:new'),
             data={
             data={
                 'name': 'Test Label',
                 'name': 'Test Label',
                 'css_class': 'test_label',
                 'css_class': 'test_label',
-                'forums': [f.pk for f in Forum.objects.all_forums()],
+                'categories': [f.pk for f in Category.objects.all_categories()],
             })
             })
         test_label = Label.objects.get(slug='test-label')
         test_label = Label.objects.get(slug='test-label')
 
 
         response = self.client.post(
         response = self.client.post(
-            reverse('misago:admin:forums:labels:delete',
+            reverse('misago:admin:categories:labels:delete',
                     kwargs={'label_id': test_label.pk}))
                     kwargs={'label_id': test_label.pk}))
         self.assertEqual(response.status_code, 302)
         self.assertEqual(response.status_code, 302)
 
 
-        self.client.get(reverse('misago:admin:forums:labels:index'))
+        self.client.get(reverse('misago:admin:categories:labels:index'))
         response = self.client.get(
         response = self.client.get(
-            reverse('misago:admin:forums:labels:index'))
+            reverse('misago:admin:categories:labels:index'))
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
 
 
         self.assertNotIn(test_label.name, response.content)
         self.assertNotIn(test_label.name, response.content)

+ 5 - 6
misago/threads/tests/test_participants.py

@@ -2,7 +2,7 @@ from django.contrib.auth import get_user_model
 from django.test import TestCase
 from django.test import TestCase
 from django.utils import timezone
 from django.utils import timezone
 
 
-from misago.forums.models import Forum
+from misago.categories.models import Category
 
 
 from misago.threads.models import Thread, ThreadParticipant, Post
 from misago.threads.models import Thread, ThreadParticipant, Post
 from misago.threads.participants import (thread_has_participants,
 from misago.threads.participants import (thread_has_participants,
@@ -10,17 +10,16 @@ from misago.threads.participants import (thread_has_participants,
                                          set_thread_owner,
                                          set_thread_owner,
                                          set_user_unread_private_threads_sync,
                                          set_user_unread_private_threads_sync,
                                          add_owner,
                                          add_owner,
-                                         remove_participant
+                                         remove_participant)
-                                         )
 
 
 
 
 class ParticipantsTests(TestCase):
 class ParticipantsTests(TestCase):
     def setUp(self):
     def setUp(self):
         datetime = timezone.now()
         datetime = timezone.now()
 
 
-        self.forum = Forum.objects.filter(role="forum")[:1][0]
+        self.category = Category.objects.filter(role='forum')[:1][0]
         self.thread = Thread(
         self.thread = Thread(
-            forum=self.forum,
+            category=self.category,
             started_on=datetime,
             started_on=datetime,
             starter_name='Tester',
             starter_name='Tester',
             starter_slug='tester',
             starter_slug='tester',
@@ -32,7 +31,7 @@ class ParticipantsTests(TestCase):
         self.thread.save()
         self.thread.save()
 
 
         post = Post.objects.create(
         post = Post.objects.create(
-            forum=self.forum,
+            category=self.category,
             thread=self.thread,
             thread=self.thread,
             poster_name='Tester',
             poster_name='Tester',
             poster_ip='127.0.0.1',
             poster_ip='127.0.0.1',

+ 10 - 10
misago/threads/tests/test_post_model.py

@@ -4,7 +4,7 @@ from django.contrib.auth import get_user_model
 from django.test import TestCase
 from django.test import TestCase
 from django.utils import timezone
 from django.utils import timezone
 
 
-from misago.forums.models import Forum
+from misago.categories.models import Category
 
 
 from misago.threads.checksums import update_post_checksum
 from misago.threads.checksums import update_post_checksum
 from misago.threads.models import Thread, Post
 from misago.threads.models import Thread, Post
@@ -17,9 +17,9 @@ class PostModelTests(TestCase):
 
 
         datetime = timezone.now()
         datetime = timezone.now()
 
 
-        self.forum = Forum.objects.filter(role="forum")[:1][0]
+        self.category = Category.objects.filter(role='forum')[:1][0]
         self.thread = Thread(
         self.thread = Thread(
-            forum=self.forum,
+            category=self.category,
             started_on=datetime,
             started_on=datetime,
             starter_name='Tester',
             starter_name='Tester',
             starter_slug='tester',
             starter_slug='tester',
@@ -31,7 +31,7 @@ class PostModelTests(TestCase):
         self.thread.save()
         self.thread.save()
 
 
         self.post = Post.objects.create(
         self.post = Post.objects.create(
-            forum=self.forum,
+            category=self.category,
             thread=self.thread,
             thread=self.thread,
             poster=self.user,
             poster=self.user,
             poster_name=self.user.username,
             poster_name=self.user.username,
@@ -58,7 +58,7 @@ class PostModelTests(TestCase):
         other_user = User.objects.create_user("Jeff", "Je@ff.com", "Pass.123")
         other_user = User.objects.create_user("Jeff", "Je@ff.com", "Pass.123")
 
 
         other_post = Post.objects.create(
         other_post = Post.objects.create(
-            forum=self.forum,
+            category=self.category,
             thread=self.thread,
             thread=self.thread,
             poster=other_user,
             poster=other_user,
             poster_name=other_user.username,
             poster_name=other_user.username,
@@ -73,7 +73,7 @@ class PostModelTests(TestCase):
             self.post.merge(other_post)
             self.post.merge(other_post)
 
 
         other_thread = Thread.objects.create(
         other_thread = Thread.objects.create(
-            forum=self.forum,
+            category=self.category,
             started_on=timezone.now(),
             started_on=timezone.now(),
             starter_name='Tester',
             starter_name='Tester',
             starter_slug='tester',
             starter_slug='tester',
@@ -82,7 +82,7 @@ class PostModelTests(TestCase):
             last_poster_slug='tester')
             last_poster_slug='tester')
 
 
         other_post = Post.objects.create(
         other_post = Post.objects.create(
-            forum=self.forum,
+            category=self.category,
             thread=other_thread,
             thread=other_thread,
             poster=self.user,
             poster=self.user,
             poster_name=self.user.username,
             poster_name=self.user.username,
@@ -97,7 +97,7 @@ class PostModelTests(TestCase):
             self.post.merge(other_post)
             self.post.merge(other_post)
 
 
         other_post = Post.objects.create(
         other_post = Post.objects.create(
-            forum=self.forum,
+            category=self.category,
             thread=self.thread,
             thread=self.thread,
             poster_name=other_user.username,
             poster_name=other_user.username,
             poster_ip='127.0.0.1',
             poster_ip='127.0.0.1',
@@ -115,7 +115,7 @@ class PostModelTests(TestCase):
     def test_merge(self):
     def test_merge(self):
         """merge method merges two posts into one"""
         """merge method merges two posts into one"""
         other_post = Post.objects.create(
         other_post = Post.objects.create(
-            forum=self.forum,
+            category=self.category,
             thread=self.thread,
             thread=self.thread,
             poster=self.user,
             poster=self.user,
             poster_name=self.user.username,
             poster_name=self.user.username,
@@ -135,7 +135,7 @@ class PostModelTests(TestCase):
     def test_move(self):
     def test_move(self):
         """move method moves post to other thread"""
         """move method moves post to other thread"""
         new_thread = Thread.objects.create(
         new_thread = Thread.objects.create(
-            forum=self.forum,
+            category=self.category,
             started_on=timezone.now(),
             started_on=timezone.now(),
             starter_name='Tester',
             starter_name='Tester',
             starter_slug='tester',
             starter_slug='tester',

+ 3 - 3
misago/threads/tests/test_posts_moderation.py

@@ -1,4 +1,4 @@
-from misago.forums.models import Forum
+from misago.categories.models import Category
 from misago.users.testutils import AuthenticatedUserTestCase
 from misago.users.testutils import AuthenticatedUserTestCase
 
 
 from misago.threads import moderation, testutils
 from misago.threads import moderation, testutils
@@ -9,8 +9,8 @@ class PostsModerationTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
         super(PostsModerationTests, self).setUp()
         super(PostsModerationTests, self).setUp()
 
 
-        self.forum = Forum.objects.all_forums().filter(role="forum")[:1][0]
+        self.category = Category.objects.all_categories().filter(role='forum')[:1][0]
-        self.thread = testutils.post_thread(self.forum)
+        self.thread = testutils.post_thread(self.category)
         self.post = testutils.reply_thread(self.thread)
         self.post = testutils.reply_thread(self.thread)
 
 
     def reload_thread(self):
     def reload_thread(self):

+ 4 - 4
misago/threads/tests/test_synchronizethreads.py

@@ -1,7 +1,7 @@
 from django.test import TestCase
 from django.test import TestCase
 from django.utils.six import StringIO
 from django.utils.six import StringIO
 
 
-from misago.forums.models import Forum
+from misago.categories.models import Category
 
 
 from misago.threads import testutils
 from misago.threads import testutils
 from misago.threads.management.commands import synchronizethreads
 from misago.threads.management.commands import synchronizethreads
@@ -20,9 +20,9 @@ class SynchronizeThreadsTests(TestCase):
 
 
     def test_threads_sync(self):
     def test_threads_sync(self):
         """command synchronizes threads"""
         """command synchronizes threads"""
-        forum = Forum.objects.all_forums().filter(role="forum")[:1][0]
+        category = Category.objects.all_categories().filter(role='forum')[:1][0]
 
 
-        threads = [testutils.post_thread(forum) for t in xrange(10)]
+        threads = [testutils.post_thread(category) for t in xrange(10)]
         for i, thread in enumerate(threads):
         for i, thread in enumerate(threads):
             [testutils.reply_thread(thread) for r in xrange(i)]
             [testutils.reply_thread(thread) for r in xrange(i)]
             thread.replies = 0
             thread.replies = 0
@@ -34,7 +34,7 @@ class SynchronizeThreadsTests(TestCase):
         command.execute(stdout=out)
         command.execute(stdout=out)
 
 
         for i, thread in enumerate(threads):
         for i, thread in enumerate(threads):
-            db_thread = forum.thread_set.get(id=thread.id)
+            db_thread = category.thread_set.get(id=thread.id)
             self.assertEqual(db_thread.replies, i)
             self.assertEqual(db_thread.replies, i)
 
 
         command_output = out.getvalue().splitlines()[-1].strip()
         command_output = out.getvalue().splitlines()[-1].strip()

+ 18 - 18
misago/threads/tests/test_thread_model.py

@@ -4,7 +4,7 @@ from django.contrib.auth import get_user_model
 from django.test import TestCase
 from django.test import TestCase
 from django.utils import timezone
 from django.utils import timezone
 
 
-from misago.forums.models import Forum
+from misago.categories.models import Category
 
 
 from misago.threads.models import Thread, ThreadParticipant, Post, Event
 from misago.threads.models import Thread, ThreadParticipant, Post, Event
 
 
@@ -13,9 +13,9 @@ class ThreadModelTests(TestCase):
     def setUp(self):
     def setUp(self):
         datetime = timezone.now()
         datetime = timezone.now()
 
 
-        self.forum = Forum.objects.filter(role="forum")[:1][0]
+        self.category = Category.objects.filter(role='forum')[:1][0]
         self.thread = Thread(
         self.thread = Thread(
-            forum=self.forum,
+            category=self.category,
             started_on=datetime,
             started_on=datetime,
             starter_name='Tester',
             starter_name='Tester',
             starter_slug='tester',
             starter_slug='tester',
@@ -27,7 +27,7 @@ class ThreadModelTests(TestCase):
         self.thread.save()
         self.thread.save()
 
 
         post = Post.objects.create(
         post = Post.objects.create(
-            forum=self.forum,
+            category=self.category,
             thread=self.thread,
             thread=self.thread,
             poster_name='Tester',
             poster_name='Tester',
             poster_ip='127.0.0.1',
             poster_ip='127.0.0.1',
@@ -50,7 +50,7 @@ class ThreadModelTests(TestCase):
 
 
         datetime = timezone.now() + timedelta(5)
         datetime = timezone.now() + timedelta(5)
         post = Post.objects.create(
         post = Post.objects.create(
-            forum=self.forum,
+            category=self.category,
             thread=self.thread,
             thread=self.thread,
             poster=user,
             poster=user,
             poster_name=user.username,
             poster_name=user.username,
@@ -77,7 +77,7 @@ class ThreadModelTests(TestCase):
 
 
         # add moderated post
         # add moderated post
         moderated_post = Post.objects.create(
         moderated_post = Post.objects.create(
-            forum=self.forum,
+            category=self.category,
             thread=self.thread,
             thread=self.thread,
             poster=user,
             poster=user,
             poster_name=user.username,
             poster_name=user.username,
@@ -103,7 +103,7 @@ class ThreadModelTests(TestCase):
 
 
         # add hidden post
         # add hidden post
         hidden_post = Post.objects.create(
         hidden_post = Post.objects.create(
-            forum=self.forum,
+            category=self.category,
             thread=self.thread,
             thread=self.thread,
             poster=user,
             poster=user,
             poster_name=user.username,
             poster_name=user.username,
@@ -163,7 +163,7 @@ class ThreadModelTests(TestCase):
 
 
         # add event
         # add event
         event = Event.objects.create(
         event = Event.objects.create(
-            forum=self.forum,
+            category=self.category,
             thread=self.thread,
             thread=self.thread,
             author_name=user.username,
             author_name=user.username,
             author_slug=user.slug,
             author_slug=user.slug,
@@ -186,7 +186,7 @@ class ThreadModelTests(TestCase):
         datetime = timezone.now() + timedelta(5)
         datetime = timezone.now() + timedelta(5)
 
 
         post = Post.objects.create(
         post = Post.objects.create(
-            forum=self.forum,
+            category=self.category,
             thread=self.thread,
             thread=self.thread,
             poster=user,
             poster=user,
             poster_name=user.username,
             poster_name=user.username,
@@ -212,7 +212,7 @@ class ThreadModelTests(TestCase):
         datetime = timezone.now() + timedelta(5)
         datetime = timezone.now() + timedelta(5)
 
 
         post = Post.objects.create(
         post = Post.objects.create(
-            forum=self.forum,
+            category=self.category,
             thread=self.thread,
             thread=self.thread,
             poster=user,
             poster=user,
             poster_name=user.username,
             poster_name=user.username,
@@ -231,15 +231,15 @@ class ThreadModelTests(TestCase):
         self.assertEqual(self.thread.last_poster_slug, user.slug)
         self.assertEqual(self.thread.last_poster_slug, user.slug)
 
 
     def test_move(self):
     def test_move(self):
-        """move(new_forum) moves thread to other forum"""
+        """move(new_category) moves thread to other category"""
-        # pick category instead of forum (so we don't have to create one)
+        # pick category instead of category (so we don't have to create one)
-        new_forum = Forum.objects.filter(role="category")[:1][0]
+        new_category = Category.objects.filter(role='forum')[:1][0]
 
 
-        self.thread.move(new_forum)
+        self.thread.move(new_category)
-        self.assertEqual(self.thread.forum, new_forum)
+        self.assertEqual(self.thread.category, new_category)
 
 
         for post in self.thread.post_set.all():
         for post in self.thread.post_set.all():
-            self.assertEqual(post.forum_id, new_forum.id)
+            self.assertEqual(post.category_id, new_category.id)
 
 
     def test_merge(self):
     def test_merge(self):
         """merge(other_thread) moves other thread content to this thread"""
         """merge(other_thread) moves other thread content to this thread"""
@@ -249,7 +249,7 @@ class ThreadModelTests(TestCase):
         datetime = timezone.now() + timedelta(5)
         datetime = timezone.now() + timedelta(5)
 
 
         other_thread = Thread(
         other_thread = Thread(
-            forum=self.forum,
+            category=self.category,
             started_on=datetime,
             started_on=datetime,
             starter_name='Tester',
             starter_name='Tester',
             starter_slug='tester',
             starter_slug='tester',
@@ -261,7 +261,7 @@ class ThreadModelTests(TestCase):
         other_thread.save()
         other_thread.save()
 
 
         post = Post.objects.create(
         post = Post.objects.create(
-            forum=self.forum,
+            category=self.category,
             thread=other_thread,
             thread=other_thread,
             poster_name='Admin',
             poster_name='Admin',
             poster_ip='127.0.0.1',
             poster_ip='127.0.0.1',

+ 4 - 4
misago/threads/tests/test_threadparticipant_model.py

@@ -2,7 +2,7 @@ from django.contrib.auth import get_user_model
 from django.test import TestCase
 from django.test import TestCase
 from django.utils import timezone
 from django.utils import timezone
 
 
-from misago.forums.models import Forum
+from misago.categories.models import Category
 
 
 from misago.threads.models import Thread, ThreadParticipant, Post
 from misago.threads.models import Thread, ThreadParticipant, Post
 
 
@@ -11,9 +11,9 @@ class ThreadParticipantTests(TestCase):
     def setUp(self):
     def setUp(self):
         datetime = timezone.now()
         datetime = timezone.now()
 
 
-        self.forum = Forum.objects.filter(role="forum")[:1][0]
+        self.category = Category.objects.filter(role='forum')[:1][0]
         self.thread = Thread(
         self.thread = Thread(
-            forum=self.forum,
+            category=self.category,
             started_on=datetime,
             started_on=datetime,
             starter_name='Tester',
             starter_name='Tester',
             starter_slug='tester',
             starter_slug='tester',
@@ -25,7 +25,7 @@ class ThreadParticipantTests(TestCase):
         self.thread.save()
         self.thread.save()
 
 
         post = Post.objects.create(
         post = Post.objects.create(
-            forum=self.forum,
+            category=self.category,
             thread=self.thread,
             thread=self.thread,
             poster_name='Tester',
             poster_name='Tester',
             poster_ip='127.0.0.1',
             poster_ip='127.0.0.1',

+ 13 - 13
misago/threads/tests/test_threads_moderation.py

@@ -1,4 +1,4 @@
-from misago.forums.models import Forum
+from misago.categories.models import Category
 from misago.users.testutils import AuthenticatedUserTestCase
 from misago.users.testutils import AuthenticatedUserTestCase
 
 
 from misago.threads import moderation, testutils
 from misago.threads import moderation, testutils
@@ -9,8 +9,8 @@ class ThreadsModerationTests(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
         super(ThreadsModerationTests, self).setUp()
         super(ThreadsModerationTests, self).setUp()
 
 
-        self.forum = Forum.objects.all_forums().filter(role="forum")[:1][0]
+        self.category = Category.objects.all_categories().filter(role='forum')[:1][0]
-        self.thread = testutils.post_thread(self.forum)
+        self.thread = testutils.post_thread(self.category)
         Label.objects.clear_cache()
         Label.objects.clear_cache()
 
 
     def tearDown(self):
     def tearDown(self):
@@ -97,7 +97,7 @@ class ThreadsModerationTests(AuthenticatedUserTestCase):
 
 
     def test_approve_thread(self):
     def test_approve_thread(self):
         """approve_thread approves moderated thread"""
         """approve_thread approves moderated thread"""
-        thread = testutils.post_thread(self.forum, is_moderated=True)
+        thread = testutils.post_thread(self.category, is_moderated=True)
 
 
         self.assertTrue(thread.is_moderated)
         self.assertTrue(thread.is_moderated)
         self.assertTrue(thread.first_post.is_moderated)
         self.assertTrue(thread.first_post.is_moderated)
@@ -114,28 +114,28 @@ class ThreadsModerationTests(AuthenticatedUserTestCase):
 
 
     def test_move_thread(self):
     def test_move_thread(self):
         """moves_thread moves moderated thread to other froum"""
         """moves_thread moves moderated thread to other froum"""
-        new_forum = Forum.objects.all_forums().filter(role="category")[:1][0]
+        new_category = Category.objects.all_categories().filter(role='forum')[:1][0]
 
 
-        self.assertEqual(self.thread.forum, self.forum)
+        self.assertEqual(self.thread.category, self.category)
         self.assertTrue(
         self.assertTrue(
-            moderation.move_thread(self.user, self.thread, new_forum))
+            moderation.move_thread(self.user, self.thread, new_category))
 
 
         self.reload_thread()
         self.reload_thread()
-        self.assertEqual(self.thread.forum, new_forum)
+        self.assertEqual(self.thread.category, new_category)
         self.assertTrue(self.thread.has_events)
         self.assertTrue(self.thread.has_events)
         event = self.thread.event_set.last()
         event = self.thread.event_set.last()
 
 
         self.assertIn("moved thread", event.message)
         self.assertIn("moved thread", event.message)
         self.assertEqual(event.icon, "arrow-right")
         self.assertEqual(event.icon, "arrow-right")
 
 
-    def test_move_thread_to_same_forum(self):
+    def test_move_thread_to_same_category(self):
-        """moves_thread does not move thread to same forum it is in"""
+        """moves_thread does not move thread to same category it is in"""
-        self.assertEqual(self.thread.forum, self.forum)
+        self.assertEqual(self.thread.category, self.category)
         self.assertFalse(
         self.assertFalse(
-            moderation.move_thread(self.user, self.thread, self.forum))
+            moderation.move_thread(self.user, self.thread, self.category))
 
 
         self.reload_thread()
         self.reload_thread()
-        self.assertEqual(self.thread.forum, self.forum)
+        self.assertEqual(self.thread.category, self.category)
         self.assertFalse(self.thread.has_events)
         self.assertFalse(self.thread.has_events)
 
 
     def test_close_thread(self):
     def test_close_thread(self):

+ 3 - 3
misago/threads/tests/test_threadslist_view.py

@@ -133,9 +133,9 @@ class ActionsTests(AuthenticatedUserTestCase):
 
 
 class SortingTests(TestCase):
 class SortingTests(TestCase):
     def setUp(self):
     def setUp(self):
-        self.sorting = Sorting('misago:forum', {
+        self.sorting = Sorting('misago:category', {
-            'forum_slug': "test-forum",
+            'category_slug': "test-category",
-            'forum_id': 42,
+            'category_id': 42,
         })
         })
 
 
     def test_clean_kwargs_removes_default_sorting(self):
     def test_clean_kwargs_removes_default_sorting(self):

+ 7 - 7
misago/threads/testutils.py

@@ -7,13 +7,13 @@ from misago.core.utils import slugify
 from misago.threads.models import Thread, Post
 from misago.threads.models import Thread, Post
 
 
 
 
-def post_thread(forum, title='Test thread', poster='Tester', is_pinned=False,
+def post_thread(category, title='Test thread', poster='Tester',
-                is_moderated=False, is_hidden=False, is_closed=False,
+                is_pinned=False, is_moderated=False, is_hidden=False,
-                started_on=None):
+                is_closed=False, started_on=None):
     started_on = started_on or timezone.now()
     started_on = started_on or timezone.now()
 
 
     kwargs = {
     kwargs = {
-        'forum': forum,
+        'category': category,
         'title': title,
         'title': title,
         'slug': slugify(title),
         'slug': slugify(title),
         'started_on': started_on,
         'started_on': started_on,
@@ -56,7 +56,7 @@ def reply_thread(thread, poster="Tester", message='I am test message',
     posted_on = posted_on or thread.last_post_on + timedelta(minutes=5)
     posted_on = posted_on or thread.last_post_on + timedelta(minutes=5)
 
 
     kwargs = {
     kwargs = {
-        'forum': thread.forum,
+        'category': thread.category,
         'thread': thread,
         'thread': thread,
         'original': message,
         'original': message,
         'parsed': message,
         'parsed': message,
@@ -78,7 +78,7 @@ def reply_thread(thread, poster="Tester", message='I am test message',
     post = Post.objects.create(**kwargs)
     post = Post.objects.create(**kwargs)
     thread.synchronize()
     thread.synchronize()
     thread.save()
     thread.save()
-    thread.forum.synchronize()
+    thread.category.synchronize()
-    thread.forum.save()
+    thread.category.save()
 
 
     return post
     return post

+ 2 - 2
misago/threads/threadtypes/__init__.py

@@ -7,8 +7,8 @@ from django.utils.translation import ugettext_lazy as _
 class ThreadTypeBase(object):
 class ThreadTypeBase(object):
     type_name = 'undefined'
     type_name = 'undefined'
 
 
-    def get_forum_name(self, forum):
+    def get_category_name(self, category):
-        return forum.name
+        return category.name
 
 
 
 
 def load_types(types_list):
 def load_types(types_list):

+ 9 - 9
misago/threads/threadtypes/forumthread.py

@@ -3,28 +3,28 @@ from django.core.urlresolvers import reverse
 from misago.threads.threadtypes import ThreadTypeBase
 from misago.threads.threadtypes import ThreadTypeBase
 
 
 
 
-class ForumThread(ThreadTypeBase):
+class CategoryThread(ThreadTypeBase):
-    type_name = 'forum'
+    type_name = 'category'
 
 
-    def get_forum_absolute_url(self, forum):
+    def get_category_absolute_url(self, category):
-            return reverse('misago:%s' % forum.role, kwargs={
+            return reverse('misago:%s' % category.role, kwargs={
-                'forum_id': forum.id, 'forum_slug': forum.slug
+                'category_id': category.id, 'category_slug': category.slug
             })
             })
 
 
-    def get_new_thread_url(self, forum):
+    def get_new_thread_url(self, category):
         return reverse('misago:thread_new', kwargs={
         return reverse('misago:thread_new', kwargs={
-            'forum_id': forum.id, 'forum_slug': forum.slug
+            'category_id': category.id, 'category_slug': category.slug
         })
         })
 
 
     def get_reply_url(self, thread):
     def get_reply_url(self, thread):
         return reverse('misago:reply_thread', kwargs={
         return reverse('misago:reply_thread', kwargs={
-            'forum_id': thread.forum.id,
+            'category_id': thread.category.id,
             'thread_id': thread.id,
             'thread_id': thread.id,
         })
         })
 
 
     def get_edit_post_url(self, post):
     def get_edit_post_url(self, post):
         return reverse('misago:edit_post', kwargs={
         return reverse('misago:edit_post', kwargs={
-            'forum_id': post.forum_id,
+            'category_id': post.category_id,
             'thread_id': post.thread_id,
             'thread_id': post.thread_id,
             'post_id': post.id
             'post_id': post.id
         })
         })

+ 4 - 4
misago/threads/threadtypes/privatethread.py

@@ -7,13 +7,13 @@ from misago.threads.threadtypes import ThreadTypeBase
 class PrivateThread(ThreadTypeBase):
 class PrivateThread(ThreadTypeBase):
     type_name = 'private_threads'
     type_name = 'private_threads'
 
 
-    def get_forum_name(self, forum):
+    def get_category_name(self, category):
         return _('Private Threads')
         return _('Private Threads')
 
 
-    def get_forum_absolute_url(self, forum):
+    def get_category_absolute_url(self, category):
         return reverse('misago:private_threads')
         return reverse('misago:private_threads')
 
 
-    def get_new_thread_url(self, forum):
+    def get_new_thread_url(self, category):
         return reverse('misago:private_thread_new')
         return reverse('misago:private_thread_new')
 
 
     def get_reply_url(self, thread):
     def get_reply_url(self, thread):
@@ -23,7 +23,7 @@ class PrivateThread(ThreadTypeBase):
 
 
     def get_edit_post_url(self, post):
     def get_edit_post_url(self, post):
         return reverse('misago:edit_private_post', kwargs={
         return reverse('misago:edit_private_post', kwargs={
-            'forum_id': post.forum_id,
+            'category_id': post.category_id,
             'thread_id': post.thread_id,
             'thread_id': post.thread_id,
             'post_id': post.id
             'post_id': post.id
         })
         })

+ 1 - 1
misago/threads/threadtypes/report.py

@@ -7,5 +7,5 @@ from misago.threads.threadtypes import ThreadTypeBase
 class Report(ThreadTypeBase):
 class Report(ThreadTypeBase):
     type_name = 'reports'
     type_name = 'reports'
 
 
-    def get_forum_name(self, forum):
+    def get_category_name(self, category):
         return _('Reports')
         return _('Reports')

+ 13 - 13
misago/threads/urls/threads.py

@@ -1,17 +1,17 @@
 from django.conf.urls import patterns, include, url
 from django.conf.urls import patterns, include, url
 
 
 
 
-# forum view
+# category view
-from misago.threads.views.threads import ForumView
+from misago.threads.views.threads import CategoryView
 urlpatterns = patterns('',
 urlpatterns = patterns('',
-    url(r'^forum/(?P<forum_slug>[\w\d-]+)-(?P<forum_id>\d+)/$', ForumView.as_view(), name='forum'),
+    url(r'^category/(?P<category_slug>[\w\d-]+)-(?P<category_id>\d+)/$', CategoryView.as_view(), name='category'),
-    url(r'^forum/(?P<forum_slug>[\w\d-]+)-(?P<forum_id>\d+)/(?P<page>\d+)/$', ForumView.as_view(), name='forum'),
+    url(r'^category/(?P<category_slug>[\w\d-]+)-(?P<category_id>\d+)/(?P<page>\d+)/$', CategoryView.as_view(), name='category'),
-    url(r'^forum/(?P<forum_slug>[\w\d-]+)-(?P<forum_id>\d+)/sort-(?P<sort>[\w-]+)/$', ForumView.as_view(), name='forum'),
+    url(r'^category/(?P<category_slug>[\w\d-]+)-(?P<category_id>\d+)/sort-(?P<sort>[\w-]+)/$', CategoryView.as_view(), name='category'),
-    url(r'^forum/(?P<forum_slug>[\w\d-]+)-(?P<forum_id>\d+)/sort-(?P<sort>[\w-]+)/(?P<page>\d+)/$', ForumView.as_view(), name='forum'),
+    url(r'^category/(?P<category_slug>[\w\d-]+)-(?P<category_id>\d+)/sort-(?P<sort>[\w-]+)/(?P<page>\d+)/$', CategoryView.as_view(), name='category'),
-    url(r'^forum/(?P<forum_slug>[\w\d-]+)-(?P<forum_id>\d+)/show-(?P<show>[\w-]+)/$', ForumView.as_view(), name='forum'),
+    url(r'^category/(?P<category_slug>[\w\d-]+)-(?P<category_id>\d+)/show-(?P<show>[\w-]+)/$', CategoryView.as_view(), name='category'),
-    url(r'^forum/(?P<forum_slug>[\w\d-]+)-(?P<forum_id>\d+)/show-(?P<show>[\w-]+)/(?P<page>\d+)/$', ForumView.as_view(), name='forum'),
+    url(r'^category/(?P<category_slug>[\w\d-]+)-(?P<category_id>\d+)/show-(?P<show>[\w-]+)/(?P<page>\d+)/$', CategoryView.as_view(), name='category'),
-    url(r'^forum/(?P<forum_slug>[\w\d-]+)-(?P<forum_id>\d+)/sort-(?P<sort>[\w-]+)/show-(?P<show>[\w-]+)/$', ForumView.as_view(), name='forum'),
+    url(r'^category/(?P<category_slug>[\w\d-]+)-(?P<category_id>\d+)/sort-(?P<sort>[\w-]+)/show-(?P<show>[\w-]+)/$', CategoryView.as_view(), name='category'),
-    url(r'^forum/(?P<forum_slug>[\w\d-]+)-(?P<forum_id>\d+)/sort-(?P<sort>[\w-]+)/show-(?P<show>[\w-]+)/(?P<page>\d+)/$', ForumView.as_view(), name='forum'),
+    url(r'^category/(?P<category_slug>[\w\d-]+)-(?P<category_id>\d+)/sort-(?P<sort>[\w-]+)/show-(?P<show>[\w-]+)/(?P<page>\d+)/$', CategoryView.as_view(), name='category'),
 )
 )
 
 
 
 
@@ -66,9 +66,9 @@ urlpatterns += patterns('',
 # posting views
 # posting views
 from misago.threads.views.threads import PostingView
 from misago.threads.views.threads import PostingView
 urlpatterns += patterns('',
 urlpatterns += patterns('',
-    url(r'^start-thread/(?P<forum_id>\d+)/$', PostingView.as_view(), name='start_thread'),
+    url(r'^start-thread/(?P<category_id>\d+)/$', PostingView.as_view(), name='start_thread'),
-    url(r'^reply-thread/(?P<forum_id>\d+)/(?P<thread_id>\d+)/$', PostingView.as_view(), name='reply_thread'),
+    url(r'^reply-thread/(?P<category_id>\d+)/(?P<thread_id>\d+)/$', PostingView.as_view(), name='reply_thread'),
-    url(r'^edit-post/(?P<forum_id>\d+)/(?P<thread_id>\d+)/(?P<post_id>\d+)/edit/$', PostingView.as_view(), name='edit_post'),
+    url(r'^edit-post/(?P<category_id>\d+)/(?P<thread_id>\d+)/(?P<post_id>\d+)/edit/$', PostingView.as_view(), name='edit_post'),
 )
 )
 
 
 
 

+ 1 - 1
misago/threads/views/generic/__init__.py

@@ -7,4 +7,4 @@ from misago.threads.views.generic.posting import *
 from misago.threads.views.generic.post import *
 from misago.threads.views.generic.post import *
 from misago.threads.views.generic.thread import *
 from misago.threads.views.generic.thread import *
 from misago.threads.views.generic.threads import *
 from misago.threads.views.generic.threads import *
-from misago.threads.views.generic.forum import *
+from misago.threads.views.generic.category import *

+ 38 - 37
misago/threads/views/generic/base.py

@@ -3,46 +3,47 @@ from django.shortcuts import render
 from django.views.generic import View
 from django.views.generic import View
 
 
 from misago.acl import add_acl
 from misago.acl import add_acl
+from misago.categories.models import Category
+from misago.categories.permissions import (allow_see_category,
+                                           allow_browse_category)
 from misago.core.shortcuts import get_object_or_404, validate_slug
 from misago.core.shortcuts import get_object_or_404, validate_slug
-from misago.forums.models import Forum
-from misago.forums.permissions import allow_see_forum, allow_browse_forum
 
 
 from misago.threads.models import Thread, Post
 from misago.threads.models import Thread, Post
 from misago.threads.permissions import (allow_see_thread, allow_see_post,
 from misago.threads.permissions import (allow_see_thread, allow_see_post,
                                         exclude_invisible_posts)
                                         exclude_invisible_posts)
 
 
 
 
-__all__ = ['ForumMixin', 'ThreadMixin', 'PostMixin', 'ViewBase']
+__all__ = ['CategoryMixin', 'ThreadMixin', 'PostMixin', 'ViewBase']
 
 
 
 
-class ForumMixin(object):
+class CategoryMixin(object):
     """
     """
-    Mixin for getting forums
+    Mixin for getting categories
     """
     """
-    def get_forum(self, request, lock=False, **kwargs):
+    def get_category(self, request, lock=False, **kwargs):
-        forum = self.fetch_forum(request, lock, **kwargs)
+        category = self.fetch_category(request, lock, **kwargs)
-        self.check_forum_permissions(request, forum)
+        self.check_category_permissions(request, category)
 
 
-        if kwargs.get('forum_slug'):
+        if kwargs.get('category_slug'):
-            validate_slug(forum, kwargs.get('forum_slug'))
+            validate_slug(category, kwargs.get('category_slug'))
 
 
-        return forum
+        return category
 
 
-    def fetch_forum(self, request, lock=False, **kwargs):
+    def fetch_category(self, request, lock=False, **kwargs):
-        queryset = Forum.objects
+        queryset = Category.objects
         if lock:
         if lock:
             queryset = queryset.select_for_update()
             queryset = queryset.select_for_update()
 
 
         return get_object_or_404(
         return get_object_or_404(
-            queryset, id=kwargs.get('forum_id'), role='forum')
+            queryset, id=kwargs.get('category_id'), role='forum')
 
 
-    def check_forum_permissions(self, request, forum):
+    def check_category_permissions(self, request, category):
-        if forum.special_role:
+        if category.special_role:
             raise Http404()
             raise Http404()
 
 
-        add_acl(request.user, forum)
+        add_acl(request.user, category)
-        allow_see_forum(request.user, forum)
+        allow_see_category(request.user, category)
-        allow_browse_forum(request.user, forum)
+        allow_browse_category(request.user, category)
 
 
 
 
 class ThreadMixin(object):
 class ThreadMixin(object):
@@ -65,31 +66,31 @@ class ThreadMixin(object):
             queryset = queryset.select_for_update()
             queryset = queryset.select_for_update()
 
 
         select_related = select_related or []
         select_related = select_related or []
-        if not 'forum' in select_related:
+        if not 'category' in select_related:
-            select_related.append('forum')
+            select_related.append('category')
         queryset = queryset.select_related(*select_related)
         queryset = queryset.select_related(*select_related)
 
 
         where = {'id': kwargs.get('thread_id')}
         where = {'id': kwargs.get('thread_id')}
-        if 'forum_id' in kwargs:
+        if 'category_id' in kwargs:
-            where['forum_id'] = kwargs.get('forum_id')
+            where['category_id'] = kwargs.get('category_id')
         return get_object_or_404(queryset, **where)
         return get_object_or_404(queryset, **where)
 
 
     def check_thread_permissions(self, request, thread):
     def check_thread_permissions(self, request, thread):
-        if thread.forum.special_role:
+        if thread.category.special_role:
             raise Http404()
             raise Http404()
 
 
-        add_acl(request.user, thread.forum)
+        add_acl(request.user, thread.category)
         add_acl(request.user, thread)
         add_acl(request.user, thread)
 
 
         allow_see_thread(request.user, thread)
         allow_see_thread(request.user, thread)
-        allow_see_forum(request.user, thread.forum)
+        allow_see_category(request.user, thread.category)
 
 
 
 
 class PostMixin(object):
 class PostMixin(object):
     def get_post(self, request, lock=False, **kwargs):
     def get_post(self, request, lock=False, **kwargs):
         post = self.fetch_post(request, lock, **kwargs)
         post = self.fetch_post(request, lock, **kwargs)
 
 
-        post.thread.forum = post.forum
+        post.thread.category = post.category
 
 
         self.check_post_permissions(request, post)
         self.check_post_permissions(request, post)
         return post
         return post
@@ -101,8 +102,8 @@ class PostMixin(object):
             queryset = queryset.select_for_update()
             queryset = queryset.select_for_update()
 
 
         select_related = select_related or []
         select_related = select_related or []
-        if not 'forum' in select_related:
+        if not 'category' in select_related:
-            select_related.append('forum')
+            select_related.append('category')
         if not 'thread' in select_related:
         if not 'thread' in select_related:
             select_related.append('thread')
             select_related.append('thread')
         queryset = queryset.select_related(*select_related)
         queryset = queryset.select_related(*select_related)
@@ -110,28 +111,28 @@ class PostMixin(object):
         where = {'id': kwargs.get('post_id')}
         where = {'id': kwargs.get('post_id')}
         if 'thread_id' in kwargs:
         if 'thread_id' in kwargs:
             where['thread_id'] = kwargs.get('thread_id')
             where['thread_id'] = kwargs.get('thread_id')
-        if 'forum_id' in kwargs:
+        if 'category_id' in kwargs:
-            where['forum_id'] = kwargs.get('forum_id')
+            where['category_id'] = kwargs.get('category_id')
 
 
         return get_object_or_404(queryset, **where)
         return get_object_or_404(queryset, **where)
 
 
     def check_post_permissions(self, request, post):
     def check_post_permissions(self, request, post):
-        if post.forum.special_role:
+        if post.category.special_role:
             raise Http404()
             raise Http404()
 
 
-        add_acl(request.user, post.forum)
+        add_acl(request.user, post.category)
         add_acl(request.user, post.thread)
         add_acl(request.user, post.thread)
         add_acl(request.user, post)
         add_acl(request.user, post)
 
 
         allow_see_post(request.user, post)
         allow_see_post(request.user, post)
         allow_see_thread(request.user, post.thread)
         allow_see_thread(request.user, post.thread)
-        allow_see_forum(request.user, post.forum)
+        allow_see_category(request.user, post.category)
 
 
-    def exclude_invisible_posts(self, queryset, user, forum, thread):
+    def exclude_invisible_posts(self, queryset, user, category, thread):
-        return exclude_invisible_posts(queryset, user, forum)
+        return exclude_invisible_posts(queryset, user, category)
 
 
 
 
-class ViewBase(ForumMixin, ThreadMixin, PostMixin, View):
+class ViewBase(CategoryMixin, ThreadMixin, PostMixin, View):
     def process_context(self, request, context):
     def process_context(self, request, context):
         """
         """
         Simple hook for extending and manipulating template context.
         Simple hook for extending and manipulating template context.

+ 6 - 6
misago/threads/views/generic/events.py

@@ -35,22 +35,22 @@ class EventsView(ViewBase):
         @atomic
         @atomic
         def real_view(request, event_id):
         def real_view(request, event_id):
             queryset = Event.objects.select_for_update()
             queryset = Event.objects.select_for_update()
-            queryset = queryset.select_related('forum', 'thread')
+            queryset = queryset.select_related('category', 'thread')
             event = get_object_or_404(queryset, id=event_id)
             event = get_object_or_404(queryset, id=event_id)
 
 
-            forum = event.forum
+            category = event.category
             thread = event.thread
             thread = event.thread
-            thread.forum = forum
+            thread.category = category
 
 
-            self.check_forum_permissions(request, forum)
+            self.check_category_permissions(request, category)
             self.check_thread_permissions(request, thread)
             self.check_thread_permissions(request, thread)
 
 
             if request.POST.get('action') == 'toggle':
             if request.POST.get('action') == 'toggle':
-                if not forum.acl.get('can_hide_events'):
+                if not category.acl.get('can_hide_events'):
                     raise PermissionDenied(_("You can't hide events."))
                     raise PermissionDenied(_("You can't hide events."))
                 return toggle_event(request, event)
                 return toggle_event(request, event)
             elif request.POST.get('action') == 'delete':
             elif request.POST.get('action') == 'delete':
-                if forum.acl.get('can_hide_events') != 2:
+                if category.acl.get('can_hide_events') != 2:
                     raise PermissionDenied(_("You can't delete events."))
                     raise PermissionDenied(_("You can't delete events."))
                 return delete_event(request, event)
                 return delete_event(request, event)
             else:
             else:

+ 4 - 4
misago/threads/views/generic/forum/__init__.py

@@ -1,5 +1,5 @@
 # flake8: noqa
 # flake8: noqa
-from misago.threads.views.generic.forum.actions import ForumActions
+from misago.threads.views.generic.category.actions import CategoryActions
-from misago.threads.views.generic.forum.filtering import ForumFiltering
+from misago.threads.views.generic.category.filtering import CategoryFiltering
-from misago.threads.views.generic.forum.threads import ForumThreads
+from misago.threads.views.generic.category.threads import CategoryThreads
-from misago.threads.views.generic.forum.view import ForumView
+from misago.threads.views.generic.category.view import CategoryView

+ 43 - 43
misago/threads/views/generic/forum/actions.py

@@ -4,7 +4,7 @@ from django.shortcuts import render
 from django.utils import timezone
 from django.utils import timezone
 from django.utils.translation import ugettext_lazy, ugettext as _, ungettext
 from django.utils.translation import ugettext_lazy, ugettext as _, ungettext
 
 
-from misago.forums.lists import get_forum_path
+from misago.categories.lists import get_category_path
 
 
 from misago.threads import moderation
 from misago.threads import moderation
 from misago.threads.forms.moderation import MergeThreadsForm, MoveThreadsForm
 from misago.threads.forms.moderation import MergeThreadsForm, MoveThreadsForm
@@ -17,26 +17,26 @@ __all__ = ['ForumActions', 'ReloadAfterDelete']
 
 
 class ForumActions(Actions):
 class ForumActions(Actions):
     def get_available_actions(self, kwargs):
     def get_available_actions(self, kwargs):
-        self.forum = kwargs['forum']
+        self.category = kwargs['category']
 
 
         actions = []
         actions = []
 
 
-        if self.forum.acl['can_change_threads_labels'] == 2:
+        if self.category.acl['can_change_threads_labels'] == 2:
-            for label in self.forum.labels:
+            for label in self.category.labels:
                 actions.append({
                 actions.append({
                     'action': 'label:%s' % label.slug,
                     'action': 'label:%s' % label.slug,
                     'icon': 'tag',
                     'icon': 'tag',
                     'name': _('Label as "%(label)s"') % {'label': label.name}
                     'name': _('Label as "%(label)s"') % {'label': label.name}
                 })
                 })
 
 
-            if self.forum.labels:
+            if self.category.labels:
                 actions.append({
                 actions.append({
                     'action': 'unlabel',
                     'action': 'unlabel',
                     'icon': 'times-circle',
                     'icon': 'times-circle',
                     'name': _("Remove labels")
                     'name': _("Remove labels")
                 })
                 })
 
 
-        if self.forum.acl['can_pin_threads']:
+        if self.category.acl['can_pin_threads']:
             actions.append({
             actions.append({
                 'action': 'pin',
                 'action': 'pin',
                 'icon': 'star',
                 'icon': 'star',
@@ -48,28 +48,28 @@ class ForumActions(Actions):
                 'name': _("Unpin threads")
                 'name': _("Unpin threads")
             })
             })
 
 
-        if self.forum.acl['can_review_moderated_content']:
+        if self.category.acl['can_review_moderated_content']:
             actions.append({
             actions.append({
                 'action': 'approve',
                 'action': 'approve',
                 'icon': 'check',
                 'icon': 'check',
                 'name': _("Approve threads")
                 'name': _("Approve threads")
             })
             })
 
 
-        if self.forum.acl['can_move_threads']:
+        if self.category.acl['can_move_threads']:
             actions.append({
             actions.append({
                 'action': 'move',
                 'action': 'move',
                 'icon': 'arrow-right',
                 'icon': 'arrow-right',
                 'name': _("Move threads")
                 'name': _("Move threads")
             })
             })
 
 
-        if self.forum.acl['can_merge_threads']:
+        if self.category.acl['can_merge_threads']:
             actions.append({
             actions.append({
                 'action': 'merge',
                 'action': 'merge',
                 'icon': 'reply-all',
                 'icon': 'reply-all',
                 'name': _("Merge threads")
                 'name': _("Merge threads")
             })
             })
 
 
-        if self.forum.acl['can_close_threads']:
+        if self.category.acl['can_close_threads']:
             actions.append({
             actions.append({
                 'action': 'open',
                 'action': 'open',
                 'icon': 'unlock-alt',
                 'icon': 'unlock-alt',
@@ -81,7 +81,7 @@ class ForumActions(Actions):
                 'name': _("Close threads")
                 'name': _("Close threads")
             })
             })
 
 
-        if self.forum.acl['can_hide_threads']:
+        if self.category.acl['can_hide_threads']:
             actions.append({
             actions.append({
                 'action': 'unhide',
                 'action': 'unhide',
                 'icon': 'eye',
                 'icon': 'eye',
@@ -92,7 +92,7 @@ class ForumActions(Actions):
                 'icon': 'eye-slash',
                 'icon': 'eye-slash',
                 'name': _("Hide threads")
                 'name': _("Hide threads")
             })
             })
-        if self.forum.acl['can_hide_threads'] == 2:
+        if self.category.acl['can_hide_threads'] == 2:
             actions.append({
             actions.append({
                 'action': 'delete',
                 'action': 'delete',
                 'icon': 'times',
                 'icon': 'times',
@@ -104,7 +104,7 @@ class ForumActions(Actions):
         return actions
         return actions
 
 
     def action_label(self, request, threads, label_slug):
     def action_label(self, request, threads, label_slug):
-        for label in self.forum.labels:
+        for label in self.category.labels:
             if label.slug == label_slug:
             if label.slug == label_slug:
                 break
                 break
         else:
         else:
@@ -196,34 +196,34 @@ class ForumActions(Actions):
     move_threads_modal_template = 'misago/threads/move/modal.html'
     move_threads_modal_template = 'misago/threads/move/modal.html'
 
 
     def action_move(self, request, threads):
     def action_move(self, request, threads):
-        form = MoveThreadsForm(acl=request.user.acl, forum=self.forum)
+        form = MoveThreadsForm(acl=request.user.acl, category=self.category)
 
 
         if 'submit' in request.POST:
         if 'submit' in request.POST:
             form = MoveThreadsForm(
             form = MoveThreadsForm(
-                request.POST, acl=request.user.acl, forum=self.forum)
+                request.POST, acl=request.user.acl, category=self.category)
             if form.is_valid():
             if form.is_valid():
-                new_forum = form.cleaned_data['new_forum']
+                new_category = form.cleaned_data['new_category']
                 with atomic():
                 with atomic():
 
 
                     for thread in threads:
                     for thread in threads:
-                        moderation.move_thread(request.user, thread, new_forum)
+                        moderation.move_thread(request.user, thread, new_category)
 
 
-                    self.forum.lock()
+                    self.category.lock()
-                    new_forum.lock()
+                    new_category.lock()
 
 
-                    self.forum.synchronize()
+                    self.category.synchronize()
-                    self.forum.save()
+                    self.category.save()
-                    new_forum.synchronize()
+                    new_category.synchronize()
-                    new_forum.save()
+                    new_category.save()
 
 
                 changed_threads = len(threads)
                 changed_threads = len(threads)
                 message = ungettext(
                 message = ungettext(
-                    '%(changed)d thread was moved to "%(forum)s".',
+                    '%(changed)d thread was moved to "%(category)s".',
-                    '%(changed)d threads were moved to "%(forum)s".',
+                    '%(changed)d threads were moved to "%(category)s".',
                 changed_threads)
                 changed_threads)
                 messages.success(request, message % {
                 messages.success(request, message % {
                     'changed': changed_threads,
                     'changed': changed_threads,
-                    'forum': new_forum.name
+                    'category': new_category.name
                 })
                 })
 
 
                 return None # trigger threads list refresh
                 return None # trigger threads list refresh
@@ -235,8 +235,8 @@ class ForumActions(Actions):
 
 
         return render(request, template, {
         return render(request, template, {
             'form': form,
             'form': form,
-            'forum': self.forum,
+            'category': self.category,
-            'path': get_forum_path(self.forum),
+            'path': get_category_path(self.category),
             'threads': threads
             'threads': threads
         })
         })
 
 
@@ -255,7 +255,7 @@ class ForumActions(Actions):
             if form.is_valid():
             if form.is_valid():
                 with atomic():
                 with atomic():
                     merged_thread = Thread()
                     merged_thread = Thread()
-                    merged_thread.forum = self.forum
+                    merged_thread.category = self.category
                     merged_thread.set_title(
                     merged_thread.set_title(
                         form.cleaned_data['merged_thread_title'])
                         form.cleaned_data['merged_thread_title'])
                     merged_thread.starter_name = "-"
                     merged_thread.starter_name = "-"
@@ -275,9 +275,9 @@ class ForumActions(Actions):
                     merged_thread.synchronize()
                     merged_thread.synchronize()
                     merged_thread.save()
                     merged_thread.save()
 
 
-                    self.forum.lock()
+                    self.category.lock()
-                    self.forum.synchronize()
+                    self.category.synchronize()
-                    self.forum.save()
+                    self.category.save()
 
 
                 changed_threads = len(threads)
                 changed_threads = len(threads)
                 message = ungettext(
                 message = ungettext(
@@ -298,8 +298,8 @@ class ForumActions(Actions):
 
 
         return render(request, template, {
         return render(request, template, {
             'form': form,
             'form': form,
-            'forum': self.forum,
+            'category': self.category,
-            'path': get_forum_path(self.forum),
+            'path': get_category_path(self.category),
             'threads': threads
             'threads': threads
         })
         })
 
 
@@ -343,9 +343,9 @@ class ForumActions(Actions):
 
 
         if changed_threads:
         if changed_threads:
             with atomic():
             with atomic():
-                self.forum.lock()
+                self.category.lock()
-                self.forum.synchronize()
+                self.category.synchronize()
-                self.forum.save()
+                self.category.save()
 
 
             message = ungettext(
             message = ungettext(
                 '%(changed)d thread was made visible.',
                 '%(changed)d thread was made visible.',
@@ -364,9 +364,9 @@ class ForumActions(Actions):
 
 
         if changed_threads:
         if changed_threads:
             with atomic():
             with atomic():
-                self.forum.lock()
+                self.category.lock()
-                self.forum.synchronize()
+                self.category.synchronize()
-                self.forum.save()
+                self.category.save()
 
 
         if changed_threads:
         if changed_threads:
             message = ungettext(
             message = ungettext(
@@ -386,9 +386,9 @@ class ForumActions(Actions):
 
 
         if changed_threads:
         if changed_threads:
             with atomic():
             with atomic():
-                self.forum.lock()
+                self.category.lock()
-                self.forum.synchronize()
+                self.category.synchronize()
-                self.forum.save()
+                self.category.save()
 
 
         if changed_threads:
         if changed_threads:
             message = ungettext(
             message = ungettext(

+ 9 - 9
misago/threads/views/generic/forum/filtering.py

@@ -4,12 +4,12 @@ from django.utils.translation import ugettext as _
 from misago.threads.views.generic.threads import ThreadsFiltering
 from misago.threads.views.generic.threads import ThreadsFiltering
 
 
 
 
-__all__ = ['ForumFiltering']
+__all__ = ['CategoryFiltering']
 
 
 
 
-class ForumFiltering(ThreadsFiltering):
+class CategoryFiltering(ThreadsFiltering):
-    def __init__(self, forum, link_name, link_params):
+    def __init__(self, category, link_name, link_params):
-        self.forum = forum
+        self.category = category
         self.link_name = link_name
         self.link_name = link_name
         self.link_params = link_params.copy()
         self.link_params = link_params.copy()
 
 
@@ -18,21 +18,21 @@ class ForumFiltering(ThreadsFiltering):
     def get_available_filters(self):
     def get_available_filters(self):
         filters = []
         filters = []
 
 
-        if self.forum.acl['can_see_all_threads']:
+        if self.category.acl['can_see_all_threads']:
             filters.append({
             filters.append({
                 'type': 'my-threads',
                 'type': 'my-threads',
                 'name': _("My threads"),
                 'name': _("My threads"),
                 'is_label': False,
                 'is_label': False,
             })
             })
 
 
-        if self.forum.acl['can_see_reports']:
+        if self.category.acl['can_see_reports']:
             filters.append({
             filters.append({
                 'type': 'reported',
                 'type': 'reported',
                 'name': _("With reported posts"),
                 'name': _("With reported posts"),
                 'is_label': False,
                 'is_label': False,
             })
             })
 
 
-        if self.forum.acl['can_review_moderated_content']:
+        if self.category.acl['can_review_moderated_content']:
             filters.extend(({
             filters.extend(({
                 'type': 'moderated-threads',
                 'type': 'moderated-threads',
                 'name': _("Moderated threads"),
                 'name': _("Moderated threads"),
@@ -44,7 +44,7 @@ class ForumFiltering(ThreadsFiltering):
                 'is_label': False,
                 'is_label': False,
             }))
             }))
 
 
-        for label in self.forum.labels:
+        for label in self.category.labels:
             filters.append({
             filters.append({
                 'type': label.slug,
                 'type': label.slug,
                 'name': label.name,
                 'name': label.name,
@@ -57,7 +57,7 @@ class ForumFiltering(ThreadsFiltering):
     def create_dicts(self):
     def create_dicts(self):
         dicts = []
         dicts = []
 
 
-        if self.forum.acl['can_see_all_threads']:
+        if self.category.acl['can_see_all_threads']:
             default_name = _("All threads")
             default_name = _("All threads")
         else:
         else:
             default_name = _("Your threads")
             default_name = _("Your threads")

+ 10 - 10
misago/threads/views/generic/forum/threads.py

@@ -1,5 +1,5 @@
 from misago.core.shortcuts import paginate
 from misago.core.shortcuts import paginate
-from misago.readtracker import forumstracker, threadstracker
+from misago.readtracker import categoriestracker, threadstracker
 
 
 from misago.threads.permissions import exclude_invisible_threads
 from misago.threads.permissions import exclude_invisible_threads
 from misago.threads.views.generic.threads import Threads
 from misago.threads.views.generic.threads import Threads
@@ -9,11 +9,11 @@ __all__ = ['ForumThreads']
 
 
 
 
 class ForumThreads(Threads):
 class ForumThreads(Threads):
-    def __init__(self, user, forum):
+    def __init__(self, user, category):
         self.user = user
         self.user = user
-        self.forum = forum
+        self.category = category
 
 
-        forumstracker.make_read_aware(user, forum)
+        categoriestracker.make_read_aware(user, category)
 
 
         self.pinned_count = 0
         self.pinned_count = 0
         self.filter_by = None
         self.filter_by = None
@@ -38,23 +38,23 @@ class ForumThreads(Threads):
             threads.append(thread)
             threads.append(thread)
 
 
         for thread in threads:
         for thread in threads:
-            thread.forum = self.forum
+            thread.category = self.category
 
 
-        self.label_threads(threads, self.forum.labels)
+        self.label_threads(threads, self.category.labels)
         self.make_threads_read_aware(threads)
         self.make_threads_read_aware(threads)
 
 
         return threads
         return threads
 
 
     def get_queryset(self):
     def get_queryset(self):
         queryset = exclude_invisible_threads(
         queryset = exclude_invisible_threads(
-            self.forum.thread_set, self.user, self.forum)
+            self.category.thread_set, self.user, self.category)
         return self.filter_threads(queryset)
         return self.filter_threads(queryset)
 
 
     def filter_threads(self, queryset):
     def filter_threads(self, queryset):
         if self.filter_by == 'my-threads':
         if self.filter_by == 'my-threads':
             return queryset.filter(starter_id=self.user.id)
             return queryset.filter(starter_id=self.user.id)
         else:
         else:
-            if self.forum.acl['can_see_own_threads']:
+            if self.category.acl['can_see_own_threads']:
                 if self.user.is_authenticated():
                 if self.user.is_authenticated():
                     queryset = queryset.filter(starter_id=self.user.id)
                     queryset = queryset.filter(starter_id=self.user.id)
                 else:
                 else:
@@ -66,11 +66,11 @@ class ForumThreads(Threads):
             elif self.filter_by == 'moderated-posts':
             elif self.filter_by == 'moderated-posts':
                 return queryset.filter(has_moderated_posts=True)
                 return queryset.filter(has_moderated_posts=True)
             else:
             else:
-                for label in self.forum.labels:
+                for label in self.category.labels:
                     if label.slug == self.filter_by:
                     if label.slug == self.filter_by:
                         return queryset.filter(label_id=label.pk)
                         return queryset.filter(label_id=label.pk)
                 else:
                 else:
                     return queryset
                     return queryset
 
 
     def make_threads_read_aware(self, threads):
     def make_threads_read_aware(self, threads):
-        threadstracker.make_threads_read_aware(self.user, threads, self.forum)
+        threadstracker.make_threads_read_aware(self.user, threads, self.category)

+ 18 - 18
misago/threads/views/generic/forum/view.py

@@ -1,13 +1,13 @@
 from django.shortcuts import redirect
 from django.shortcuts import redirect
 
 
+from misago.categories.lists import get_categories_list, get_category_path
 from misago.core.shortcuts import validate_slug
 from misago.core.shortcuts import validate_slug
-from misago.forums.lists import get_forums_list, get_forum_path
+from misago.readtracker import categoriestracker
-from misago.readtracker import forumstracker
 
 
 from misago.threads.models import Label
 from misago.threads.models import Label
-from misago.threads.views.generic.forum.actions import ForumActions
+from misago.threads.views.generic.category.actions import ForumActions
-from misago.threads.views.generic.forum.filtering import ForumFiltering
+from misago.threads.views.generic.category.filtering import ForumFiltering
-from misago.threads.views.generic.forum.threads import ForumThreads
+from misago.threads.views.generic.category.threads import ForumThreads
 from misago.threads.views.generic.threads import Sorting, ThreadsView
 from misago.threads.views.generic.threads import Sorting, ThreadsView
 
 
 
 
@@ -16,9 +16,9 @@ __all__ = ['ForumView']
 
 
 class ForumView(ThreadsView):
 class ForumView(ThreadsView):
     """
     """
-    Basic view for forum threads lists
+    Basic view for category threads lists
     """
     """
-    template = 'misago/threads/forum.html'
+    template = 'misago/threads/category.html'
 
 
     Threads = ForumThreads
     Threads = ForumThreads
     Sorting = Sorting
     Sorting = Sorting
@@ -26,15 +26,15 @@ class ForumView(ThreadsView):
     Actions = ForumActions
     Actions = ForumActions
 
 
     def dispatch(self, request, *args, **kwargs):
     def dispatch(self, request, *args, **kwargs):
-        forum = self.get_forum(request, **kwargs)
+        category = self.get_category(request, **kwargs)
-        validate_slug(forum, kwargs['forum_slug'])
+        validate_slug(category, kwargs['category_slug'])
 
 
-        forum.labels = Label.objects.get_forum_labels(forum)
+        category.labels = Label.objects.get_category_labels(category)
 
 
-        if forum.lft + 1 < forum.rght:
+        if category.lft + 1 < category.rght:
-            forum.subforums = get_forums_list(request.user, forum)
+            category.subcategories = get_categories_list(request.user, category)
         else:
         else:
-            forum.subforums = []
+            category.subcategories = []
 
 
         page_number = kwargs.pop('page', None)
         page_number = kwargs.pop('page', None)
         cleaned_kwargs = self.clean_kwargs(request, kwargs)
         cleaned_kwargs = self.clean_kwargs(request, kwargs)
@@ -44,17 +44,17 @@ class ForumView(ThreadsView):
         sorting = self.Sorting(link_name, cleaned_kwargs)
         sorting = self.Sorting(link_name, cleaned_kwargs)
         cleaned_kwargs = sorting.clean_kwargs(cleaned_kwargs)
         cleaned_kwargs = sorting.clean_kwargs(cleaned_kwargs)
 
 
-        filtering = self.Filtering(forum, link_name, cleaned_kwargs)
+        filtering = self.Filtering(category, link_name, cleaned_kwargs)
         cleaned_kwargs = filtering.clean_kwargs(cleaned_kwargs)
         cleaned_kwargs = filtering.clean_kwargs(cleaned_kwargs)
 
 
         if cleaned_kwargs != kwargs:
         if cleaned_kwargs != kwargs:
             return redirect(link_name, **cleaned_kwargs)
             return redirect(link_name, **cleaned_kwargs)
 
 
-        threads = self.Threads(request.user, forum)
+        threads = self.Threads(request.user, category)
         sorting.sort(threads)
         sorting.sort(threads)
         filtering.filter(threads)
         filtering.filter(threads)
 
 
-        actions = self.Actions(user=request.user, forum=forum)
+        actions = self.Actions(user=request.user, category=category)
         if request.method == 'POST':
         if request.method == 'POST':
             response = actions.handle_post(request, threads.get_queryset())
             response = actions.handle_post(request, threads.get_queryset())
             if response:
             if response:
@@ -64,8 +64,8 @@ class ForumView(ThreadsView):
             'link_name': link_name,
             'link_name': link_name,
             'links_params': cleaned_kwargs,
             'links_params': cleaned_kwargs,
 
 
-            'forum': forum,
+            'category': category,
-            'path': get_forum_path(forum),
+            'path': get_category_path(category),
 
 
             'threads': threads.list(page_number),
             'threads': threads.list(page_number),
             'threads_count': threads.count(),
             'threads_count': threads.count(),

+ 10 - 9
misago/threads/views/generic/goto.py

@@ -19,14 +19,15 @@ class BaseGotoView(ViewBase):
                                   "should define get_redirect method")
                                   "should define get_redirect method")
 
 
     def dispatch(self, request, *args, **kwargs):
     def dispatch(self, request, *args, **kwargs):
-        thread = self.fetch_thread(request, select_related=['forum'], **kwargs)
+        relations = ['categorys']
-        forum = thread.forum
+        thread = self.fetch_thread(request, select_related=relations, **kwargs)
+        categorys = thread.categorys
 
 
-        self.check_forum_permissions(request, forum)
+        self.check_categorys_permissions(request, categorys)
         self.check_thread_permissions(request, thread)
         self.check_thread_permissions(request, thread)
 
 
         posts_qs = self.exclude_invisible_posts(
         posts_qs = self.exclude_invisible_posts(
-            thread.post_set, request.user, forum, thread)
+            thread.post_set, request.user, categorys, thread)
 
 
         return redirect(self.get_redirect(request.user, thread, posts_qs))
         return redirect(self.get_redirect(request.user, thread, posts_qs))
 
 
@@ -47,16 +48,16 @@ class GotoPostView(BaseGotoView):
 
 
     def dispatch(self, request, *args, **kwargs):
     def dispatch(self, request, *args, **kwargs):
         post = self.fetch_post(
         post = self.fetch_post(
-            request, select_related=['thread', 'forum'], **kwargs)
+            request, select_related=['thread', 'categorys'], **kwargs)
-        forum = post.forum
+        categorys = post.categorys
         thread = post.thread
         thread = post.thread
 
 
-        self.check_forum_permissions(request, forum)
+        self.check_categorys_permissions(request, categorys)
-        thread.forum = forum
+        thread.categorys = categorys
         self.check_thread_permissions(request, thread)
         self.check_thread_permissions(request, thread)
         self.check_post_permissions(request, post)
         self.check_post_permissions(request, post)
 
 
         posts_qs = self.exclude_invisible_posts(
         posts_qs = self.exclude_invisible_posts(
-            thread.post_set, request.user, thread.forum, thread)
+            thread.post_set, request.user, thread.categorys, thread)
 
 
         return redirect(self.get_redirect(thread, posts_qs, post))
         return redirect(self.get_redirect(thread, posts_qs, post))

+ 5 - 5
misago/threads/views/generic/gotopostslist.py

@@ -24,22 +24,22 @@ class ModeratedPostsListView(ViewBase):
         if not request.is_ajax():
         if not request.is_ajax():
             return not_allowed(request)
             return not_allowed(request)
 
 
-        relations = ['forum']
+        relations = ['category']
         thread = self.fetch_thread(request, select_related=relations, **kwargs)
         thread = self.fetch_thread(request, select_related=relations, **kwargs)
-        forum = thread.forum
+        category = thread.category
 
 
-        self.check_forum_permissions(request, forum)
+        self.check_category_permissions(request, category)
         self.check_thread_permissions(request, thread)
         self.check_thread_permissions(request, thread)
 
 
         self.allow_action(thread)
         self.allow_action(thread)
 
 
         posts_qs = self.exclude_invisible_posts(
         posts_qs = self.exclude_invisible_posts(
-            thread.post_set, request.user, forum, thread)
+            thread.post_set, request.user, category, thread)
         posts_qs = self.filter_posts_queryset(posts_qs)
         posts_qs = self.filter_posts_queryset(posts_qs)
         final_posts_qs = posts_qs.select_related('poster').order_by('-id')[:15]
         final_posts_qs = posts_qs.select_related('poster').order_by('-id')[:15]
 
 
         return self.render(request, {
         return self.render(request, {
-            'forum': forum,
+            'category': category,
             'thread': thread,
             'thread': thread,
 
 
             'posts_count': posts_qs.count(),
             'posts_count': posts_qs.count(),

+ 10 - 10
misago/threads/views/generic/post.py

@@ -55,7 +55,7 @@ class PostView(ViewBase):
     def redirect_to_post(self, user, post):
     def redirect_to_post(self, user, post):
         posts_qs = self.exclude_invisible_posts(post.thread.post_set,
         posts_qs = self.exclude_invisible_posts(post.thread.post_set,
                                                 user,
                                                 user,
-                                                post.forum,
+                                                post.category,
                                                 post.thread)
                                                 post.thread)
         return redirect(goto.post(post.thread, posts_qs, post))
         return redirect(goto.post(post.thread, posts_qs, post))
 
 
@@ -86,8 +86,8 @@ class ApprovePostView(PostView):
 
 
         post.thread.synchronize()
         post.thread.synchronize()
         post.thread.save()
         post.thread.save()
-        post.forum.synchronize()
+        post.category.synchronize()
-        post.forum.save()
+        post.category.save()
 
 
 
 
 class UnhidePostView(PostView):
 class UnhidePostView(PostView):
@@ -117,14 +117,14 @@ class DeletePostView(PostView):
 
 
         post.thread.synchronize()
         post.thread.synchronize()
         post.thread.save()
         post.thread.save()
-        post.forum.synchronize()
+        post.category.synchronize()
-        post.forum.save()
+        post.category.save()
 
 
         posts_qs = self.exclude_invisible_posts(post.thread.post_set,
         posts_qs = self.exclude_invisible_posts(post.thread.post_set,
                                                 request.user,
                                                 request.user,
-                                                post.forum,
+                                                post.category,
                                                 post.thread)
                                                 post.thread)
-        posts_qs = posts_qs.select_related('thread', 'forum')
+        posts_qs = posts_qs.select_related('thread', 'category')
 
 
         if post_id < post.thread.last_post_id:
         if post_id < post.thread.last_post_id:
             target_post = posts_qs.order_by('id').filter(id__gt=post_id)
             target_post = posts_qs.order_by('id').filter(id__gt=post_id)
@@ -132,9 +132,9 @@ class DeletePostView(PostView):
             target_post = posts_qs.order_by('-id').filter(id__lt=post_id)
             target_post = posts_qs.order_by('-id').filter(id__lt=post_id)
 
 
         target_post = target_post[:1][0]
         target_post = target_post[:1][0]
-        target_post.thread.forum = target_post.forum
+        target_post.thread.category = target_post.category
 
 
-        add_acl(request.user, target_post.forum)
+        add_acl(request.user, target_post.category)
         add_acl(request.user, target_post.thread)
         add_acl(request.user, target_post.thread)
         add_acl(request.user, target_post)
         add_acl(request.user, target_post)
 
 
@@ -192,7 +192,7 @@ class ReportPostView(PostView):
 
 
     def render_alerts(self, request, post):
     def render_alerts(self, request, post):
         return render(request, self.alerts_template, {
         return render(request, self.alerts_template, {
-            'forum': post.forum,
+            'category': post.category,
             'thread': post.thread,
             'thread': post.thread,
             'post': post
             'post': post
         }).content
         }).content

+ 19 - 19
misago/threads/views/generic/posting.py

@@ -6,9 +6,9 @@ from django.utils import html
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
 from django.views.generic import View
 from django.views.generic import View
 
 
+from misago.categories.lists import get_category_path
 from misago.core.errorpages import not_allowed
 from misago.core.errorpages import not_allowed
 from misago.core.exceptions import AjaxError
 from misago.core.exceptions import AjaxError
-from misago.forums.lists import get_forum_path
 
 
 from misago.threads import goto
 from misago.threads import goto
 from misago.threads.posting import (PostingInterrupt, EditorFormset,
 from misago.threads.posting import (PostingInterrupt, EditorFormset,
@@ -36,19 +36,19 @@ class PostingView(ViewBase):
         if is_submit:
         if is_submit:
             request.user.lock()
             request.user.lock()
 
 
-        forum = None
+        category = None
         thread = None
         thread = None
         post = None
         post = None
 
 
         if 'post_id' in kwargs:
         if 'post_id' in kwargs:
             post = self.get_post(request, lock=is_submit, **kwargs)
             post = self.get_post(request, lock=is_submit, **kwargs)
-            forum = post.forum
+            category = post.category
             thread = post.thread
             thread = post.thread
         elif 'thread_id' in kwargs:
         elif 'thread_id' in kwargs:
             thread = self.get_thread(request, lock=is_submit, **kwargs)
             thread = self.get_thread(request, lock=is_submit, **kwargs)
-            forum = thread.forum
+            category = thread.category
         else:
         else:
-            forum = self.get_forum(request, lock=is_submit, **kwargs)
+            category = self.get_category(request, lock=is_submit, **kwargs)
 
 
         if thread:
         if thread:
             if post:
             if post:
@@ -57,26 +57,26 @@ class PostingView(ViewBase):
                 mode = REPLY
                 mode = REPLY
         else:
         else:
             mode = START
             mode = START
-            thread = Thread(forum=forum)
+            thread = Thread(category=category)
 
 
         if not post:
         if not post:
-            post = Post(forum=forum, thread=thread)
+            post = Post(category=category, thread=thread)
 
 
-        return mode, forum, thread, post
+        return mode, category, thread, post
 
 
-    def allow_mode(self, user, mode, forum, thread, post):
+    def allow_mode(self, user, mode, category, thread, post):
         """
         """
         Second step: check start/reply/edit permissions
         Second step: check start/reply/edit permissions
         """
         """
         if mode == START:
         if mode == START:
-            self.allow_start(user, forum)
+            self.allow_start(user, category)
         if mode == REPLY:
         if mode == REPLY:
             self.allow_reply(user, thread)
             self.allow_reply(user, thread)
         if mode == EDIT:
         if mode == EDIT:
             self.allow_edit(user, post)
             self.allow_edit(user, post)
 
 
-    def allow_start(self, user, forum):
+    def allow_start(self, user, category):
-        allow_start_thread(user, forum)
+        allow_start_thread(user, category)
 
 
     def allow_reply(self, user, thread):
     def allow_reply(self, user, thread):
         allow_reply_thread(user, thread)
         allow_reply_thread(user, thread)
@@ -97,13 +97,13 @@ class PostingView(ViewBase):
     def real_dispatch(self, request, *args, **kwargs):
     def real_dispatch(self, request, *args, **kwargs):
         mode_context = self.find_mode(request, *args, **kwargs)
         mode_context = self.find_mode(request, *args, **kwargs)
         self.allow_mode(request.user, *mode_context)
         self.allow_mode(request.user, *mode_context)
-        mode, forum, thread, post = mode_context
+        mode, category, thread, post = mode_context
 
 
-        forum.labels = Label.objects.get_forum_labels(forum)
+        category.labels = Label.objects.get_category_labels(category)
         formset = EditorFormset(request=request,
         formset = EditorFormset(request=request,
                                 mode=mode,
                                 mode=mode,
                                 user=request.user,
                                 user=request.user,
-                                forum=forum,
+                                category=category,
                                 thread=thread,
                                 thread=thread,
                                 post=post)
                                 post=post)
 
 
@@ -128,15 +128,15 @@ class PostingView(ViewBase):
             'forms': formset.get_forms_list(),
             'forms': formset.get_forms_list(),
             'main_forms': formset.get_main_forms(),
             'main_forms': formset.get_main_forms(),
             'supporting_forms': formset.get_supporting_forms(),
             'supporting_forms': formset.get_supporting_forms(),
-            'forum': forum,
+            'category': category,
-            'path': get_forum_path(forum),
+            'path': get_category_path(category),
             'thread': thread,
             'thread': thread,
             'post': post,
             'post': post,
             'api_url': request.path
             'api_url': request.path
         })
         })
 
 
     def handle_submit(self, request, formset):
     def handle_submit(self, request, formset):
-        mode, forum, thread, post = (formset.mode, formset.forum,
+        mode, category, thread, post = (formset.mode, formset.category,
                                      formset.thread, formset.post)
                                      formset.thread, formset.post)
         if mode == EDIT:
         if mode == EDIT:
             message = _("Changes saved.")
             message = _("Changes saved.")
@@ -149,7 +149,7 @@ class PostingView(ViewBase):
 
 
         posts_qs = self.exclude_invisible_posts(thread.post_set,
         posts_qs = self.exclude_invisible_posts(thread.post_set,
                                                 request.user,
                                                 request.user,
-                                                forum,
+                                                category,
                                                 thread)
                                                 thread)
         post_url = goto.post(thread, posts_qs, post)
         post_url = goto.post(thread, posts_qs, post)
 
 

+ 24 - 24
misago/threads/views/generic/thread/postsactions.py

@@ -5,7 +5,7 @@ from django.shortcuts import redirect, render
 from django.utils import timezone
 from django.utils import timezone
 from django.utils.translation import ungettext, ugettext_lazy, ugettext as _
 from django.utils.translation import ungettext, ugettext_lazy, ugettext as _
 
 
-from misago.forums.lists import get_forum_path
+from misago.categories.lists import get_category_path
 
 
 from misago.threads import moderation
 from misago.threads import moderation
 from misago.threads.forms.moderation import MovePostsForm, SplitThreadForm
 from misago.threads.forms.moderation import MovePostsForm, SplitThreadForm
@@ -37,9 +37,9 @@ def changes_thread_state(f):
             self.thread.synchronize()
             self.thread.synchronize()
             self.thread.save()
             self.thread.save()
 
 
-            self.forum.lock()
+            self.category.lock()
-            self.forum.synchronize()
+            self.category.synchronize()
-            self.forum.save()
+            self.category.save()
 
 
             return response
             return response
     return decorator
     return decorator
@@ -67,7 +67,7 @@ class PostsActions(ActionsBase):
 
 
     def get_available_actions(self, kwargs):
     def get_available_actions(self, kwargs):
         self.thread = kwargs['thread']
         self.thread = kwargs['thread']
-        self.forum = self.thread.forum
+        self.category = self.thread.category
 
 
         actions = []
         actions = []
 
 
@@ -79,28 +79,28 @@ class PostsActions(ActionsBase):
                     'name': _("Approve posts")
                     'name': _("Approve posts")
                 })
                 })
 
 
-        if self.forum.acl['can_merge_posts']:
+        if self.category.acl['can_merge_posts']:
             actions.append({
             actions.append({
                 'action': 'merge',
                 'action': 'merge',
                 'icon': 'compress',
                 'icon': 'compress',
                 'name': _("Merge posts into one")
                 'name': _("Merge posts into one")
             })
             })
 
 
-        if self.forum.acl['can_move_posts']:
+        if self.category.acl['can_move_posts']:
             actions.append({
             actions.append({
                 'action': 'move',
                 'action': 'move',
                 'icon': 'arrow-right',
                 'icon': 'arrow-right',
                 'name': _("Move posts to other thread")
                 'name': _("Move posts to other thread")
             })
             })
 
 
-        if self.forum.acl['can_split_threads']:
+        if self.category.acl['can_split_threads']:
             actions.append({
             actions.append({
                 'action': 'split',
                 'action': 'split',
                 'icon': 'code-fork',
                 'icon': 'code-fork',
                 'name': _("Split posts to new thread")
                 'name': _("Split posts to new thread")
             })
             })
 
 
-        if self.forum.acl['can_protect_posts']:
+        if self.category.acl['can_protect_posts']:
             actions.append({
             actions.append({
                 'action': 'unprotect',
                 'action': 'unprotect',
                 'icon': 'unlock-alt',
                 'icon': 'unlock-alt',
@@ -112,7 +112,7 @@ class PostsActions(ActionsBase):
                 'name': _("Protect posts")
                 'name': _("Protect posts")
             })
             })
 
 
-        if self.forum.acl['can_hide_posts']:
+        if self.category.acl['can_hide_posts']:
             actions.append({
             actions.append({
                 'action': 'unhide',
                 'action': 'unhide',
                 'icon': 'eye',
                 'icon': 'eye',
@@ -123,7 +123,7 @@ class PostsActions(ActionsBase):
                 'icon': 'eye-slash',
                 'icon': 'eye-slash',
                 'name': _("Hide posts")
                 'name': _("Hide posts")
             })
             })
-        if self.forum.acl['can_hide_posts'] == 2:
+        if self.category.acl['can_hide_posts'] == 2:
             actions.append({
             actions.append({
                 'action': 'delete',
                 'action': 'delete',
                 'icon': 'times',
                 'icon': 'times',
@@ -201,10 +201,10 @@ class PostsActions(ActionsBase):
                 form.new_thread.synchronize()
                 form.new_thread.synchronize()
                 form.new_thread.save()
                 form.new_thread.save()
 
 
-                if form.new_thread.forum != self.forum:
+                if form.new_thread.category != self.category:
-                    form.new_thread.forum.lock()
+                    form.new_thread.category.lock()
-                    form.new_thread.forum.synchronize()
+                    form.new_thread.category.synchronize()
-                    form.new_thread.forum.save()
+                    form.new_thread.category.save()
 
 
                 changed_posts = len(posts)
                 changed_posts = len(posts)
                 message = ungettext(
                 message = ungettext(
@@ -228,9 +228,9 @@ class PostsActions(ActionsBase):
 
 
         return render(request, template, {
         return render(request, template, {
             'form': form,
             'form': form,
-            'forum': self.forum,
+            'category': self.category,
             'thread': self.thread,
             'thread': self.thread,
-            'path': get_forum_path(self.forum),
+            'path': get_category_path(self.category),
 
 
             'posts': posts
             'posts': posts
         })
         })
@@ -250,7 +250,7 @@ class PostsActions(ActionsBase):
             form = SplitThreadForm(request.POST, acl=request.user.acl)
             form = SplitThreadForm(request.POST, acl=request.user.acl)
             if form.is_valid():
             if form.is_valid():
                 split_thread = Thread()
                 split_thread = Thread()
-                split_thread.forum = form.cleaned_data['forum']
+                split_thread.category = form.cleaned_data['category']
                 split_thread.set_title(
                 split_thread.set_title(
                     form.cleaned_data['thread_title'])
                     form.cleaned_data['thread_title'])
                 split_thread.starter_name = "-"
                 split_thread.starter_name = "-"
@@ -268,10 +268,10 @@ class PostsActions(ActionsBase):
                 split_thread.synchronize()
                 split_thread.synchronize()
                 split_thread.save()
                 split_thread.save()
 
 
-                if split_thread.forum != self.forum:
+                if split_thread.category != self.category:
-                    split_thread.forum.lock()
+                    split_thread.category.lock()
-                    split_thread.forum.synchronize()
+                    split_thread.category.synchronize()
-                    split_thread.forum.save()
+                    split_thread.category.save()
 
 
                 changed_posts = len(posts)
                 changed_posts = len(posts)
                 message = ungettext(
                 message = ungettext(
@@ -295,9 +295,9 @@ class PostsActions(ActionsBase):
 
 
         return render(request, template, {
         return render(request, template, {
             'form': form,
             'form': form,
-            'forum': self.forum,
+            'category': self.category,
             'thread': self.thread,
             'thread': self.thread,
-            'path': get_forum_path(self.forum),
+            'path': get_category_path(self.category),
 
 
             'posts': posts
             'posts': posts
         })
         })

+ 29 - 29
misago/threads/views/generic/thread/threadactions.py

@@ -3,7 +3,7 @@ from django.db.transaction import atomic
 from django.shortcuts import redirect, render
 from django.shortcuts import redirect, render
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
 
 
-from misago.forums.lists import get_forum_path
+from misago.categories.lists import get_category_path
 
 
 from misago.threads import moderation
 from misago.threads import moderation
 from misago.threads.forms.moderation import MoveThreadForm
 from misago.threads.forms.moderation import MoveThreadForm
@@ -20,13 +20,13 @@ class ThreadActions(ActionsBase):
 
 
     def get_available_actions(self, kwargs):
     def get_available_actions(self, kwargs):
         self.thread = kwargs['thread']
         self.thread = kwargs['thread']
-        self.forum = self.thread.forum
+        self.category = self.thread.category
 
 
         actions = []
         actions = []
 
 
         if self.thread.acl['can_change_label']:
         if self.thread.acl['can_change_label']:
-            self.forum.labels = Label.objects.get_forum_labels(self.forum)
+            self.category.labels = Label.objects.get_category_labels(self.category)
-            for label in self.forum.labels:
+            for label in self.category.labels:
                 if label.pk != self.thread.label_id:
                 if label.pk != self.thread.label_id:
                     name = _('Label as "%(label)s"') % {'label': label.name}
                     name = _('Label as "%(label)s"') % {'label': label.name}
                     actions.append({
                     actions.append({
@@ -35,7 +35,7 @@ class ThreadActions(ActionsBase):
                         'name': name
                         'name': name
                     })
                     })
 
 
-            if self.forum.labels and self.thread.label_id:
+            if self.category.labels and self.thread.label_id:
                 actions.append({
                 actions.append({
                     'action': 'unlabel',
                     'action': 'unlabel',
                     'icon': 'times-circle',
                     'icon': 'times-circle',
@@ -111,7 +111,7 @@ class ThreadActions(ActionsBase):
         return actions
         return actions
 
 
     def action_label(self, request, thread, label_slug):
     def action_label(self, request, thread, label_slug):
-        for label in self.forum.labels:
+        for label in self.category.labels:
             if label.slug == label_slug:
             if label.slug == label_slug:
                 break
                 break
         else:
         else:
@@ -141,28 +141,28 @@ class ThreadActions(ActionsBase):
     move_thread_modal_template = 'misago/thread/move/modal.html'
     move_thread_modal_template = 'misago/thread/move/modal.html'
 
 
     def action_move(self, request, thread):
     def action_move(self, request, thread):
-        form = MoveThreadForm(acl=request.user.acl, forum=self.forum)
+        form = MoveThreadForm(acl=request.user.acl, category=self.category)
 
 
         if 'submit' in request.POST:
         if 'submit' in request.POST:
             form = MoveThreadForm(
             form = MoveThreadForm(
-                request.POST, acl=request.user.acl, forum=self.forum)
+                request.POST, acl=request.user.acl, category=self.category)
             if form.is_valid():
             if form.is_valid():
-                new_forum = form.cleaned_data['new_forum']
+                new_category = form.cleaned_data['new_category']
 
 
                 with atomic():
                 with atomic():
-                    moderation.move_thread(request.user, thread, new_forum)
+                    moderation.move_thread(request.user, thread, new_category)
 
 
-                    self.forum.lock()
+                    self.category.lock()
-                    new_forum.lock()
+                    new_category.lock()
 
 
-                    self.forum.synchronize()
+                    self.category.synchronize()
-                    self.forum.save()
+                    self.category.save()
-                    new_forum.synchronize()
+                    new_category.synchronize()
-                    new_forum.save()
+                    new_category.save()
 
 
-                message = _('Thread was moved to "%(forum)s".')
+                message = _('Thread was moved to "%(category)s".')
                 messages.success(request, message % {
                 messages.success(request, message % {
-                    'forum': new_forum.name
+                    'category': new_category.name
                 })
                 })
 
 
                 return None # trigger thread refresh
                 return None # trigger thread refresh
@@ -174,8 +174,8 @@ class ThreadActions(ActionsBase):
 
 
         return render(request, template, {
         return render(request, template, {
             'form': form,
             'form': form,
-            'forum': self.forum,
+            'category': self.category,
-            'path': get_forum_path(self.forum),
+            'path': get_category_path(self.category),
             'thread': thread
             'thread': thread
         })
         })
 
 
@@ -189,27 +189,27 @@ class ThreadActions(ActionsBase):
 
 
     def action_unhide(self, request, thread):
     def action_unhide(self, request, thread):
         moderation.unhide_thread(request.user, thread)
         moderation.unhide_thread(request.user, thread)
-        self.forum.synchronize()
+        self.category.synchronize()
-        self.forum.save()
+        self.category.save()
         messages.success(request, _("Thread was made visible."))
         messages.success(request, _("Thread was made visible."))
 
 
     def action_hide(self, request, thread):
     def action_hide(self, request, thread):
         with atomic():
         with atomic():
-            self.forum.lock()
+            self.category.lock()
             moderation.hide_thread(request.user, thread)
             moderation.hide_thread(request.user, thread)
-            self.forum.synchronize()
+            self.category.synchronize()
-            self.forum.save()
+            self.category.save()
 
 
         messages.success(request, _("Thread was hid."))
         messages.success(request, _("Thread was hid."))
 
 
     def action_delete(self, request, thread):
     def action_delete(self, request, thread):
         with atomic():
         with atomic():
-            self.forum.lock()
+            self.category.lock()
             moderation.delete_thread(request.user, thread)
             moderation.delete_thread(request.user, thread)
-            self.forum.synchronize()
+            self.category.synchronize()
-            self.forum.save()
+            self.category.save()
 
 
         message = _('Thread "%(thread)s" was deleted.')
         message = _('Thread "%(thread)s" was deleted.')
         messages.success(request, message % {'thread': thread.title})
         messages.success(request, message % {'thread': thread.title})
 
 
-        return redirect(self.forum.get_absolute_url())
+        return redirect(self.category.get_absolute_url())

+ 13 - 13
misago/threads/views/generic/thread/view.py

@@ -3,8 +3,8 @@ from django.core.exceptions import PermissionDenied
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
 
 
 from misago.acl import add_acl
 from misago.acl import add_acl
+from misago.categories.lists import get_category_path
 from misago.core.shortcuts import validate_slug
 from misago.core.shortcuts import validate_slug
-from misago.forums.lists import get_forum_path
 from misago.readtracker import threadstracker
 from misago.readtracker import threadstracker
 
 
 from misago.threads.events import add_events_to_posts
 from misago.threads.events import add_events_to_posts
@@ -27,16 +27,16 @@ class ThreadView(ViewBase):
     PostsActions = PostsActions
     PostsActions = PostsActions
     template = 'misago/thread/replies.html'
     template = 'misago/thread/replies.html'
 
 
-    def get_posts(self, user, forum, thread, kwargs):
+    def get_posts(self, user, category, thread, kwargs):
-        queryset = self.get_posts_queryset(user, forum, thread)
+        queryset = self.get_posts_queryset(user, category, thread)
-        queryset = self.exclude_invisible_posts(queryset, user, forum, thread)
+        queryset = self.exclude_invisible_posts(queryset, user, category, thread)
         page = paginate(queryset, kwargs.get('page', 0),
         page = paginate(queryset, kwargs.get('page', 0),
                         settings.MISAGO_POSTS_PER_PAGE,
                         settings.MISAGO_POSTS_PER_PAGE,
                         settings.MISAGO_THREAD_TAIL)
                         settings.MISAGO_THREAD_TAIL)
 
 
         posts = []
         posts = []
         for post in page.object_list:
         for post in page.object_list:
-            post.forum = forum
+            post.category = category
             post.thread = thread
             post.thread = thread
 
 
             add_acl(user, post)
             add_acl(user, post)
@@ -50,7 +50,7 @@ class ThreadView(ViewBase):
 
 
         return page, posts
         return page, posts
 
 
-    def get_posts_queryset(self, user, forum, thread):
+    def get_posts_queryset(self, user, category, thread):
         return thread.post_set.select_related(
         return thread.post_set.select_related(
             'poster',
             'poster',
             'poster__rank',
             'poster__rank',
@@ -62,11 +62,11 @@ class ThreadView(ViewBase):
         allow_reply_thread(user, thread)
         allow_reply_thread(user, thread)
 
 
     def dispatch(self, request, *args, **kwargs):
     def dispatch(self, request, *args, **kwargs):
-        relations = ['forum', 'starter', 'last_poster', 'first_post']
+        relations = ['category', 'starter', 'last_poster', 'first_post']
         thread = self.fetch_thread(request, select_related=relations, **kwargs)
         thread = self.fetch_thread(request, select_related=relations, **kwargs)
-        forum = thread.forum
+        category = thread.category
 
 
-        self.check_forum_permissions(request, forum)
+        self.check_category_permissions(request, category)
         self.check_thread_permissions(request, thread)
         self.check_thread_permissions(request, thread)
 
 
         validate_slug(thread, kwargs['thread_slug'])
         validate_slug(thread, kwargs['thread_slug'])
@@ -82,12 +82,12 @@ class ThreadView(ViewBase):
                 if response:
                 if response:
                     return response
                     return response
             if posts_actions.query_key in request.POST:
             if posts_actions.query_key in request.POST:
-                queryset = self.get_posts_queryset(request.user, forum, thread)
+                queryset = self.get_posts_queryset(request.user, category, thread)
                 response = posts_actions.handle_post(request, queryset)
                 response = posts_actions.handle_post(request, queryset)
                 if response:
                 if response:
                     return response
                     return response
 
 
-        page, posts = self.get_posts(request.user, forum, thread, kwargs)
+        page, posts = self.get_posts(request.user, category, thread, kwargs)
         make_posts_reports_aware(request.user, thread, posts)
         make_posts_reports_aware(request.user, thread, posts)
 
 
         threadstracker.make_posts_read_aware(request.user, thread, posts)
         threadstracker.make_posts_read_aware(request.user, thread, posts)
@@ -105,8 +105,8 @@ class ThreadView(ViewBase):
                 'thread_id': thread.id, 'thread_slug': thread.slug
                 'thread_id': thread.id, 'thread_slug': thread.slug
             },
             },
 
 
-            'forum': forum,
+            'category': category,
-            'path': get_forum_path(forum),
+            'path': get_category_path(category),
 
 
             'thread': thread,
             'thread': thread,
             'thread_actions': thread_actions,
             'thread_actions': thread_actions,

+ 4 - 4
misago/threads/views/labelsadmin.py

@@ -9,7 +9,7 @@ from misago.threads.forms.admin import LabelForm
 
 
 
 
 class LabelsAdmin(generic.AdminBaseMixin):
 class LabelsAdmin(generic.AdminBaseMixin):
-    root_link = 'misago:admin:forums:labels:index'
+    root_link = 'misago:admin:categories:labels:index'
     Model = Label
     Model = Label
     Form = LabelForm
     Form = LabelForm
     templates_dir = 'misago/admin/labels'
     templates_dir = 'misago/admin/labels'
@@ -17,9 +17,9 @@ class LabelsAdmin(generic.AdminBaseMixin):
 
 
     def handle_form(self, form, request, target):
     def handle_form(self, form, request, target):
         target.save()
         target.save()
-        target.forums.clear()
+        target.categories.clear()
-        if form.cleaned_data.get('forums'):
+        if form.cleaned_data.get('categories'):
-            target.forums.add(*[f for f in form.cleaned_data.get('forums')])
+            target.categories.add(*[f for f in form.cleaned_data.get('categories')])
         Label.objects.clear_cache()
         Label.objects.clear_cache()
 
 
         if self.message_submit:
         if self.message_submit:

+ 1 - 1
misago/threads/views/moderatedcontent.py

@@ -12,7 +12,7 @@ from misago.threads.permissions import exclude_invisible_threads
 class ModeratedContent(Threads):
 class ModeratedContent(Threads):
     def get_queryset(self):
     def get_queryset(self):
         queryset = Thread.objects.filter(has_moderated_posts=True)
         queryset = Thread.objects.filter(has_moderated_posts=True)
-        queryset = queryset.select_related('forum')
+        queryset = queryset.select_related('category')
         queryset = exclude_invisible_threads(queryset, self.user)
         queryset = exclude_invisible_threads(queryset, self.user)
         return queryset
         return queryset
 
 

+ 1 - 1
misago/threads/views/newthreads.py

@@ -29,7 +29,7 @@ class NewThreads(Threads):
             cutoff_date = self.user.new_threads_cutoff
             cutoff_date = self.user.new_threads_cutoff
 
 
         queryset = Thread.objects.filter(started_on__gte=cutoff_date)
         queryset = Thread.objects.filter(started_on__gte=cutoff_date)
-        queryset = queryset.select_related('forum')
+        queryset = queryset.select_related('category')
 
 
         tracked_threads = self.user.threadread_set.all()
         tracked_threads = self.user.threadread_set.all()
         queryset = queryset.exclude(id__in=tracked_threads.values('thread_id'))
         queryset = queryset.exclude(id__in=tracked_threads.values('thread_id'))

+ 16 - 16
misago/threads/views/privatethreads.py

@@ -7,10 +7,10 @@ from django.shortcuts import get_object_or_404, redirect
 from django.utils.translation import ugettext as _, ungettext
 from django.utils.translation import ugettext as _, ungettext
 
 
 from misago.acl import add_acl
 from misago.acl import add_acl
+from misago.categories.models import Category
 from misago.core.errorpages import not_allowed
 from misago.core.errorpages import not_allowed
 from misago.core.exceptions import AjaxError
 from misago.core.exceptions import AjaxError
 from misago.core.uiviews import uiview
 from misago.core.uiviews import uiview
-from misago.forums.models import Forum
 from misago.users.decorators import deny_guests
 from misago.users.decorators import deny_guests
 
 
 from misago.threads import participants
 from misago.threads import participants
@@ -41,13 +41,13 @@ class PrivateThreadsMixin(object):
     """
     """
     Mixin is used to make views use different permission tests
     Mixin is used to make views use different permission tests
     """
     """
-    def get_forum(self, request, lock=False, **kwargs):
+    def get_category(self, request, lock=False, **kwargs):
-        forum = Forum.objects.private_threads()
+        category = Category.objects.private_threads()
-        add_acl(request.user, forum)
+        add_acl(request.user, category)
-        return forum
+        return category
 
 
-    def check_forum_permissions(self, request, forum):
+    def check_category_permissions(self, request, category):
-        add_acl(request.user, forum)
+        add_acl(request.user, category)
         allow_use_private_threads(request.user)
         allow_use_private_threads(request.user)
 
 
     def fetch_thread(self, request, lock=False, select_related=None,
     def fetch_thread(self, request, lock=False, select_related=None,
@@ -57,18 +57,18 @@ class PrivateThreadsMixin(object):
             queryset = queryset.select_for_update()
             queryset = queryset.select_for_update()
 
 
         select_related = select_related or []
         select_related = select_related or []
-        if not 'forum' in select_related:
+        if not 'category' in select_related:
-            select_related.append('forum')
+            select_related.append('category')
         queryset = queryset.select_related(*select_related)
         queryset = queryset.select_related(*select_related)
 
 
         where = {'id': kwargs.get('thread_id')}
         where = {'id': kwargs.get('thread_id')}
         thread = get_object_or_404(queryset, **where)
         thread = get_object_or_404(queryset, **where)
-        if thread.forum.special_role != 'private_threads':
+        if thread.category.special_role != 'private_threads':
             raise Http404()
             raise Http404()
         return thread
         return thread
 
 
     def check_thread_permissions(self, request, thread):
     def check_thread_permissions(self, request, thread):
-        add_acl(request.user, thread.forum)
+        add_acl(request.user, thread.category)
         add_acl(request.user, thread)
         add_acl(request.user, thread)
 
 
         participants.make_thread_participants_aware(request.user, thread)
         participants.make_thread_participants_aware(request.user, thread)
@@ -77,7 +77,7 @@ class PrivateThreadsMixin(object):
         allow_use_private_threads(request.user)
         allow_use_private_threads(request.user)
 
 
     def check_post_permissions(self, request, post):
     def check_post_permissions(self, request, post):
-        add_acl(request.user, post.forum)
+        add_acl(request.user, post.category)
         add_acl(request.user, post.thread)
         add_acl(request.user, post.thread)
         add_acl(request.user, post)
         add_acl(request.user, post)
 
 
@@ -87,7 +87,7 @@ class PrivateThreadsMixin(object):
         allow_see_private_thread(request.user, post.thread)
         allow_see_private_thread(request.user, post.thread)
         allow_use_private_threads(request.user)
         allow_use_private_threads(request.user)
 
 
-    def exclude_invisible_posts(self, queryset, user, forum, thread):
+    def exclude_invisible_posts(self, queryset, user, category, thread):
         return queryset
         return queryset
 
 
 
 
@@ -95,7 +95,7 @@ class PrivateThreads(generic.Threads):
     fetch_pinned_threads = False
     fetch_pinned_threads = False
 
 
     def get_queryset(self):
     def get_queryset(self):
-        threads_qs = Forum.objects.private_threads().thread_set
+        threads_qs = Category.objects.private_threads().thread_set
         return exclude_invisible_private_threads(threads_qs, self.user)
         return exclude_invisible_private_threads(threads_qs, self.user)
 
 
 
 
@@ -254,7 +254,7 @@ class ThreadParticipantsView(PrivateThreadsMixin, generic.ViewBase):
         participants_qs = participants_qs.select_related('user', 'user__rank')
         participants_qs = participants_qs.select_related('user', 'user__rank')
 
 
         return self.render(request, {
         return self.render(request, {
-            'forum': thread.forum,
+            'category': thread.category,
             'thread': thread,
             'thread': thread,
             'participants': participants_qs.order_by('-is_owner', 'user__slug')
             'participants': participants_qs.order_by('-is_owner', 'user__slug')
         })
         })
@@ -318,7 +318,7 @@ class AddThreadParticipantsView(BaseEditThreadParticipantView):
         participants_list = [p for p in participants_qs]
         participants_list = [p for p in participants_qs]
 
 
         participants_list_html = self.render(request, {
         participants_list_html = self.render(request, {
-            'forum': thread.forum,
+            'category': thread.category,
             'thread': thread,
             'thread': thread,
             'participants': participants_list,
             'participants': participants_list,
         }).content
         }).content

+ 1 - 1
misago/threads/views/threads.py

@@ -1,7 +1,7 @@
 from misago.threads.views import generic
 from misago.threads.views import generic
 
 
 
 
-class ForumView(generic.ForumView):
+class CategoryView(generic.CategoryView):
     pass
     pass
 
 
 
 

+ 1 - 1
misago/threads/views/unreadthreads.py

@@ -30,7 +30,7 @@ class UnreadThreads(Threads):
             cutoff_date = self.user.unread_threads_cutoff
             cutoff_date = self.user.unread_threads_cutoff
 
 
         queryset = Thread.objects.filter(last_post_on__gte=cutoff_date)
         queryset = Thread.objects.filter(last_post_on__gte=cutoff_date)
-        queryset = queryset.select_related('forum')
+        queryset = queryset.select_related('category')
         queryset = queryset.filter(threadread__user=self.user)
         queryset = queryset.filter(threadread__user=self.user)
         queryset = queryset.filter(
         queryset = queryset.filter(
             threadread__last_read_on__lt=F('last_post_on'))
             threadread__last_read_on__lt=F('last_post_on'))

+ 1 - 3
misago/urls.py

@@ -14,10 +14,8 @@ urlpatterns = patterns('misago.core.views',
 urlpatterns += patterns('',
 urlpatterns += patterns('',
     url(r'^', include('misago.legal.urls')),
     url(r'^', include('misago.legal.urls')),
     url(r'^', include('misago.users.urls')),
     url(r'^', include('misago.users.urls')),
-    url(r'^', include('misago.notifications.urls')),
+    url(r'^', include('misago.categories.urls')),
-    url(r'^', include('misago.forums.urls')),
     url(r'^', include('misago.threads.urls')),
     url(r'^', include('misago.threads.urls')),
-    url(r'^', include('misago.readtracker.urls')),
     # UI Server view that handles realtime updates of Misago UI
     # UI Server view that handles realtime updates of Misago UI
     url(r'^ui-server/$', 'misago.core.uiviews.uiserver', name="ui_server"),
     url(r'^ui-server/$', 'misago.core.uiviews.uiserver', name="ui_server"),
 )
 )

+ 5 - 3
misago/users/activepostersranking.py

@@ -5,7 +5,7 @@ from django.contrib.auth import get_user_model
 from django.db.models import Count
 from django.db.models import Count
 from django.utils import timezone
 from django.utils import timezone
 
 
-from misago.forums.models import Forum
+from misago.categories.models import Category
 from misago.core.cache import cache
 from misago.core.cache import cache
 
 
 
 
@@ -24,12 +24,14 @@ def get_real_active_posts_ranking():
     tracked_period = settings.MISAGO_RANKING_LENGTH
     tracked_period = settings.MISAGO_RANKING_LENGTH
     tracked_since = timezone.now() - timedelta(days=tracked_period)
     tracked_since = timezone.now() - timedelta(days=tracked_period)
 
 
-    ranked_forums = [forum.pk for forum in Forum.objects.all_forums()]
+    ranked_categories = []
+    for category in Category.objects.all_categories():
+        ranked_categories.append(category.pk)
 
 
     User = get_user_model()
     User = get_user_model()
     queryset = User.objects.filter(posts__gt=0)
     queryset = User.objects.filter(posts__gt=0)
     queryset = queryset.filter(post__posted_on__gte=tracked_since,
     queryset = queryset.filter(post__posted_on__gte=tracked_since,
-                               post__forum__in=ranked_forums)
+                               post__category__in=ranked_categories)
     queryset = queryset.annotate(score=Count('post'))
     queryset = queryset.annotate(score=Count('post'))
     queryset = queryset.select_related('user__rank')
     queryset = queryset.select_related('user__rank')
     queryset = queryset.order_by('-score')
     queryset = queryset.order_by('-score')

+ 4 - 4
misago/users/api/auth.py

@@ -71,10 +71,10 @@ def send_activation(request):
     if form.is_valid():
     if form.is_valid():
         requesting_user = form.user_cache
         requesting_user = form.user_cache
 
 
-        mail_subject = _("Activate %(user)s account on %(forum_title)s forums")
+        mail_subject = _("Activate %(user)s account on %(forum_name)s forums")
         subject_formats = {
         subject_formats = {
             'user': requesting_user.username,
             'user': requesting_user.username,
-            'forum_title': settings.forum_name,
+            'forum_name': settings.forum_name,
         }
         }
         mail_subject = mail_subject % subject_formats
         mail_subject = mail_subject % subject_formats
 
 
@@ -103,10 +103,10 @@ def send_password_form(request):
     if form.is_valid():
     if form.is_valid():
         requesting_user = form.user_cache
         requesting_user = form.user_cache
 
 
-        mail_subject = _("Change %(user)s password on %(forum_title)s forums")
+        mail_subject = _("Change %(user)s password on %(forum_name)s forums")
         subject_formats = {
         subject_formats = {
             'user': requesting_user.username,
             'user': requesting_user.username,
-            'forum_title': settings.forum_name,
+            'forum_name': settings.forum_name,
         }
         }
         mail_subject = mail_subject % subject_formats
         mail_subject = mail_subject % subject_formats
 
 

+ 2 - 2
misago/users/api/userendpoints/changeemail.py

@@ -16,8 +16,8 @@ def change_email_endpoint(request, pk=None):
         token = store_new_credential(
         token = store_new_credential(
             request, 'email', form.cleaned_data['new_email'])
             request, 'email', form.cleaned_data['new_email'])
 
 
-        mail_subject = _("Confirm e-mail change on %(forum_title)s forums")
+        mail_subject = _("Confirm e-mail change on %(forum_name)s forums")
-        mail_subject = mail_subject % {'forum_title': settings.forum_name}
+        mail_subject = mail_subject % {'forum_name': settings.forum_name}
 
 
         # swap address with new one so email is sent to new address
         # swap address with new one so email is sent to new address
         request.user.email = form.cleaned_data['new_email']
         request.user.email = form.cleaned_data['new_email']

+ 2 - 2
misago/users/api/userendpoints/changepassword.py

@@ -16,8 +16,8 @@ def change_password_endpoint(request, pk=None):
         token = store_new_credential(
         token = store_new_credential(
             request, 'password', form.cleaned_data['new_password'])
             request, 'password', form.cleaned_data['new_password'])
 
 
-        mail_subject = _("Confirm password change on %(forum_title)s forums")
+        mail_subject = _("Confirm password change on %(forum_name)s forums")
-        mail_subject = mail_subject % {'forum_title': settings.forum_name}
+        mail_subject = mail_subject % {'forum_name': settings.forum_name}
 
 
         mail_user(request, request.user, mail_subject,
         mail_user(request, request.user, mail_subject,
                   'misago/emails/change_password',
                   'misago/emails/change_password',

+ 2 - 2
misago/users/api/userendpoints/create.py

@@ -74,8 +74,8 @@ def create_endpoint(request):
                                         set_default_avatar=True,
                                         set_default_avatar=True,
                                         **activation_kwargs)
                                         **activation_kwargs)
 
 
-    mail_subject = _("Welcome on %(forum_title)s forums!")
+    mail_subject = _("Welcome on %(forum_name)s forums!")
-    mail_subject = mail_subject % {'forum_title': settings.forum_name}
+    mail_subject = mail_subject % {'forum_name': settings.forum_name}
 
 
     if settings.account_activation == 'none':
     if settings.account_activation == 'none':
         authenticated_user = authenticate(
         authenticated_user = authenticate(

+ 0 - 1
misago/users/api/userendpoints/list.py

@@ -12,7 +12,6 @@ from misago.conf import settings
 from misago.core.apipaginator import ApiPaginator
 from misago.core.apipaginator import ApiPaginator
 from misago.core.cache import cache
 from misago.core.cache import cache
 from misago.core.shortcuts import get_int_or_404, get_object_or_404
 from misago.core.shortcuts import get_int_or_404, get_object_or_404
-from misago.forums.models import Forum
 
 
 from misago.users.activepostersranking import get_active_posters_ranking
 from misago.users.activepostersranking import get_active_posters_ranking
 from misago.users.models import Rank
 from misago.users.models import Rank

+ 10 - 8
misago/users/api/users.py

@@ -12,8 +12,8 @@ from rest_framework.parsers import JSONParser, MultiPartParser
 from rest_framework.response import Response
 from rest_framework.response import Response
 
 
 from misago.acl import add_acl
 from misago.acl import add_acl
+from misago.categories.models import Category
 from misago.core.cache import cache
 from misago.core.cache import cache
-from misago.forums.models import Forum
 from misago.threads.moderation.posts import hide_post
 from misago.threads.moderation.posts import hide_post
 from misago.threads.moderation.threads import hide_thread
 from misago.threads.moderation.threads import hide_thread
 
 
@@ -204,24 +204,26 @@ class UserViewSet(viewsets.GenericViewSet):
                 if request.data.get('with_content'):
                 if request.data.get('with_content'):
                     profile.delete_content()
                     profile.delete_content()
                 else:
                 else:
-                    forums_to_sync = set()
+                    categories_to_sync = set()
 
 
                     threads = profile.thread_set.select_related('first_post')
                     threads = profile.thread_set.select_related('first_post')
                     for thread in threads.filter(is_hidden=False).iterator():
                     for thread in threads.filter(is_hidden=False).iterator():
-                        forums_to_sync.add(thread.forum_id)
+                        categories_to_sync.add(thread.category_id)
                         hide_thread(request.user, thread)
                         hide_thread(request.user, thread)
 
 
                     posts = profile.post_set.select_related('thread')
                     posts = profile.post_set.select_related('thread')
                     for post in posts.filter(is_hidden=False).iterator():
                     for post in posts.filter(is_hidden=False).iterator():
-                        forums_to_sync.add(post.forum_id)
+                        categories_to_sync.add(post.category_id)
                         hide_post(request.user, post)
                         hide_post(request.user, post)
                         post.thread.synchronize()
                         post.thread.synchronize()
                         post.thread.save()
                         post.thread.save()
 
 
-                    forums = Forum.objects.filter(id__in=forums_to_sync)
+                    categories = Category.objects.filter(
-                    for forum in forums.iterator():
+                        id__in=categories_to_sync).iterator()
-                        forum.synchronize()
+
-                        forum.save()
+                    for category in categories:
+                        category.synchronize()
+                        category.save()
 
 
                 profile.delete()
                 profile.delete()
 
 

+ 7 - 7
misago/users/tests/test_activepostersranking.py

@@ -1,8 +1,8 @@
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model
 
 
+from misago.categories.models import Category
 from misago.core import threadstore
 from misago.core import threadstore
 from misago.core.cache import cache
 from misago.core.cache import cache
-from misago.forums.models import Forum
 from misago.threads.testutils import post_thread
 from misago.threads.testutils import post_thread
 
 
 from misago.users.testutils import AuthenticatedUserTestCase
 from misago.users.testutils import AuthenticatedUserTestCase
@@ -18,7 +18,7 @@ class TestActivePostersRanking(AuthenticatedUserTestCase):
         cache.clear()
         cache.clear()
         threadstore.clear()
         threadstore.clear()
 
 
-        self.forum = Forum.objects.all_forums().filter(role="forum")[:1][0]
+        self.category = Category.objects.all_categories(role='forum').filter()[:1][0]
 
 
     def tearDown(self):
     def tearDown(self):
         super(TestActivePostersRanking, self).tearDown()
         super(TestActivePostersRanking, self).tearDown()
@@ -42,7 +42,7 @@ class TestActivePostersRanking(AuthenticatedUserTestCase):
         other_user.posts = 1
         other_user.posts = 1
         other_user.save()
         other_user.save()
 
 
-        post_thread(self.forum, poster=other_user)
+        post_thread(self.category, poster=other_user)
 
 
         ranking = get_real_active_posts_ranking()
         ranking = get_real_active_posts_ranking()
 
 
@@ -50,8 +50,8 @@ class TestActivePostersRanking(AuthenticatedUserTestCase):
         self.assertEqual(ranking['users_count'], 1)
         self.assertEqual(ranking['users_count'], 1)
 
 
         # two users in ranking
         # two users in ranking
-        post_thread(self.forum, poster=self.user)
+        post_thread(self.category, poster=self.user)
-        post_thread(self.forum, poster=self.user)
+        post_thread(self.category, poster=self.user)
 
 
         self.user.posts = 2
         self.user.posts = 2
         self.user.save()
         self.user.save()
@@ -72,8 +72,8 @@ class TestActivePostersRanking(AuthenticatedUserTestCase):
         self.assertEqual(ranking['users_count'], 0)
         self.assertEqual(ranking['users_count'], 0)
 
 
         # post something
         # post something
-        post_thread(self.forum, poster=self.user)
+        post_thread(self.category, poster=self.user)
-        post_thread(self.forum, poster=self.user)
+        post_thread(self.category, poster=self.user)
 
 
         self.user.posts = 2
         self.user.posts = 2
         self.user.save()
         self.user.save()

+ 5 - 5
misago/users/tests/test_useradmin_views.py

@@ -6,7 +6,7 @@ from django.core.urlresolvers import reverse
 
 
 from misago.acl.models import Role
 from misago.acl.models import Role
 from misago.admin.testutils import AdminTestCase
 from misago.admin.testutils import AdminTestCase
-from misago.forums.models import Forum
+from misago.categories.models import Category
 from misago.threads.testutils import post_thread, reply_thread
 from misago.threads.testutils import post_thread, reply_thread
 
 
 from misago.users.models import Ban, Rank
 from misago.users.models import Ban, Rank
@@ -215,8 +215,8 @@ class UserAdminViewsTests(AdminTestCase):
         test_link = reverse('misago:admin:users:accounts:delete_threads',
         test_link = reverse('misago:admin:users:accounts:delete_threads',
                             kwargs={'user_id': test_user.pk})
                             kwargs={'user_id': test_user.pk})
 
 
-        forum = Forum.objects.all_forums().filter(role="forum")[:1][0]
+        category = Category.objects.all_categories().filter(role='forum')[:1][0]
-        [post_thread(forum, poster=test_user) for i in xrange(10)]
+        [post_thread(category, poster=test_user) for i in xrange(10)]
 
 
         response = self.client.post(test_link, **self.ajax_header)
         response = self.client.post(test_link, **self.ajax_header)
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
@@ -239,8 +239,8 @@ class UserAdminViewsTests(AdminTestCase):
         test_link = reverse('misago:admin:users:accounts:delete_posts',
         test_link = reverse('misago:admin:users:accounts:delete_posts',
                             kwargs={'user_id': test_user.pk})
                             kwargs={'user_id': test_user.pk})
 
 
-        forum = Forum.objects.all_forums().filter(role="forum")[:1][0]
+        category = Category.objects.all_categories().filter(role='forum')[:1][0]
-        thread = post_thread(forum)
+        thread = post_thread(category)
         [reply_thread(thread, poster=test_user) for i in xrange(10)]
         [reply_thread(thread, poster=test_user) for i in xrange(10)]
 
 
         response = self.client.post(test_link, **self.ajax_header)
         response = self.client.post(test_link, **self.ajax_header)

+ 8 - 8
misago/users/tests/test_users_api.py

@@ -4,10 +4,10 @@ import json
 from django.contrib.auth import get_user_model
 from django.contrib.auth import get_user_model
 
 
 from misago.acl.testutils import override_acl
 from misago.acl.testutils import override_acl
+from misago.categories.models import Category
 from misago.conf import settings
 from misago.conf import settings
 from misago.core import threadstore
 from misago.core import threadstore
 from misago.core.cache import cache
 from misago.core.cache import cache
-from misago.forums.models import Forum
 from misago.threads.models import Thread, Post
 from misago.threads.models import Thread, Post
 from misago.threads.testutils import post_thread
 from misago.threads.testutils import post_thread
 
 
@@ -26,8 +26,8 @@ class ActivePostersListTests(AuthenticatedUserTestCase):
         cache.clear()
         cache.clear()
         threadstore.clear()
         threadstore.clear()
 
 
-        self.forum = Forum.objects.all_forums().filter(role="forum")[:1][0]
+        self.category = Category.objects.all_categories().filter(role='forum')[:1][0]
-        self.forum.labels = []
+        self.category.labels = []
 
 
     def test_empty_list(self):
     def test_empty_list(self):
         """empty list is served"""
         """empty list is served"""
@@ -41,7 +41,7 @@ class ActivePostersListTests(AuthenticatedUserTestCase):
 
 
     def test_filled_list(self):
     def test_filled_list(self):
         """filled list is served"""
         """filled list is served"""
-        post_thread(self.forum, poster=self.user)
+        post_thread(self.category, poster=self.user)
         self.user.posts = 1
         self.user.posts = 1
         self.user.save()
         self.user.save()
 
 
@@ -186,12 +186,12 @@ class SearchNamesListTests(AuthenticatedUserTestCase):
         self.assertIn(self.user.username, response.content)
         self.assertIn(self.user.username, response.content)
 
 
 
 
-class UserForumOptionsTests(AuthenticatedUserTestCase):
+class UserCategoriesOptionsTests(AuthenticatedUserTestCase):
     """
     """
     tests for user forum options RPC (POST to /api/users/1/forum-options/)
     tests for user forum options RPC (POST to /api/users/1/forum-options/)
     """
     """
     def setUp(self):
     def setUp(self):
-        super(UserForumOptionsTests, self).setUp()
+        super(UserCategoriesOptionsTests, self).setUp()
         self.link = '/api/users/%s/forum-options/' % self.user.pk
         self.link = '/api/users/%s/forum-options/' % self.user.pk
 
 
     def test_empty_request(self):
     def test_empty_request(self):
@@ -364,9 +364,9 @@ class UserDeleteTests(AuthenticatedUserTestCase):
         self.threads = Thread.objects.count()
         self.threads = Thread.objects.count()
         self.posts = Post.objects.count()
         self.posts = Post.objects.count()
 
 
-        self.forum = Forum.objects.all_forums().filter(role="forum")[:1][0]
+        self.category = Categories.objects.all_categories().filter(role='forum')[:1][0]
 
 
-        post_thread(self.forum, poster=self.other_user)
+        post_thread(self.category, poster=self.other_user)
         self.other_user.posts = 1
         self.other_user.posts = 1
         self.other_user.threads = 1
         self.other_user.threads = 1
         self.other_user.save()
         self.other_user.save()

+ 15 - 15
misago/users/views/admin/users.py

@@ -7,10 +7,10 @@ from django.utils.translation import ugettext_lazy as _
 
 
 from misago.admin.auth import start_admin_session
 from misago.admin.auth import start_admin_session
 from misago.admin.views import generic
 from misago.admin.views import generic
+from misago.categories.models import Category
 from misago.conf import settings
 from misago.conf import settings
 from misago.core.mail import mail_users
 from misago.core.mail import mail_users
 from misago.core.pgutils import batch_update
 from misago.core.pgutils import batch_update
-from misago.forums.models import Forum
 from misago.threads.models import Thread
 from misago.threads.models import Thread
 
 
 from misago.users.avatars.dynamic import set_avatar as set_dynamic_avatar
 from misago.users.avatars.dynamic import set_avatar as set_dynamic_avatar
@@ -101,9 +101,9 @@ class UsersList(UserAdmin, generic.ListView):
             queryset = User.objects.filter(pk__in=activated_users_pks)
             queryset = User.objects.filter(pk__in=activated_users_pks)
             queryset.update(requires_activation=ACTIVATION_REQUIRED_NONE)
             queryset.update(requires_activation=ACTIVATION_REQUIRED_NONE)
 
 
-            mail_subject = _("Your account on %(forum_title)s "
+            mail_subject = _("Your account on %(forum_name)s "
                              "forums has been activated")
                              "forums has been activated")
-            subject_formats = {'forum_title': settings.forum_name}
+            subject_formats = {'forum_name': settings.forum_name}
             mail_subject = mail_subject % subject_formats
             mail_subject = mail_subject % subject_formats
 
 
             mail_subject = mail_subject
             mail_subject = mail_subject
@@ -326,21 +326,21 @@ class DeletionStep(UserAdmin, generic.ButtonView):
 
 
 class DeleteThreadsStep(DeletionStep):
 class DeleteThreadsStep(DeletionStep):
     def execute_step(self, user):
     def execute_step(self, user):
-        recount_forums = set()
+        recount_categories = set()
 
 
         deleted_threads = 0
         deleted_threads = 0
         is_completed = False
         is_completed = False
 
 
         for thread in user.thread_set.order_by('-id')[:50]:
         for thread in user.thread_set.order_by('-id')[:50]:
-            recount_forums.add(thread.forum_id)
+            recount_categories.add(thread.category_id)
             with transaction.atomic():
             with transaction.atomic():
                 thread.delete()
                 thread.delete()
                 deleted_threads += 1
                 deleted_threads += 1
 
 
-        if recount_forums:
+        if recount_categories:
-            for forum in Forum.objects.filter(id__in=recount_forums):
+            for category in Category.objects.filter(id__in=recount_categories):
-                forum.synchronize()
+                category.synchronize()
-                forum.save()
+                category.save()
         else:
         else:
             is_completed = True
             is_completed = True
 
 
@@ -352,28 +352,28 @@ class DeleteThreadsStep(DeletionStep):
 
 
 class DeletePostsStep(DeletionStep):
 class DeletePostsStep(DeletionStep):
     def execute_step(self, user):
     def execute_step(self, user):
-        recount_forums = set()
+        recount_categories = set()
         recount_threads = set()
         recount_threads = set()
 
 
         deleted_posts = 0
         deleted_posts = 0
         is_completed = False
         is_completed = False
 
 
         for post in user.post_set.order_by('-id')[:50]:
         for post in user.post_set.order_by('-id')[:50]:
-            recount_forums.add(post.forum_id)
+            recount_categories.add(post.category_id)
             recount_threads.add(post.thread_id)
             recount_threads.add(post.thread_id)
             with transaction.atomic():
             with transaction.atomic():
                 post.delete()
                 post.delete()
                 deleted_posts += 1
                 deleted_posts += 1
 
 
-        if recount_forums:
+        if recount_categories:
             changed_threads_qs = Thread.objects.filter(id__in=recount_threads)
             changed_threads_qs = Thread.objects.filter(id__in=recount_threads)
             for thread in batch_update(changed_threads_qs, 50):
             for thread in batch_update(changed_threads_qs, 50):
                 thread.synchronize()
                 thread.synchronize()
                 thread.save()
                 thread.save()
 
 
-            for forum in Forum.objects.filter(id__in=recount_forums):
+            for category in Category.objects.filter(id__in=recount_categories):
-                forum.synchronize()
+                category.synchronize()
-                forum.save()
+                category.save()
         else:
         else:
             is_completed = True
             is_completed = True