Browse Source

Remove some wildcard imports, make attribute definitions on in posting process explicit

Rafał Pitoń 7 years ago
parent
commit
f3800eade9
38 changed files with 153 additions and 116 deletions
  1. 0 2
      misago/categories/__init__.py
  2. 0 2
      misago/categories/constants.py
  3. 2 3
      misago/categories/forms.py
  4. 4 2
      misago/categories/migrations/0002_default_categories.py
  5. 5 5
      misago/categories/models.py
  6. 2 3
      misago/categories/tests/test_category_model.py
  7. 2 3
      misago/categories/views/categoriesadmin.py
  8. 1 1
      misago/core/testproject/urlswitherrorhandlers.py
  9. 2 2
      misago/readtracker/signals.py
  10. 1 1
      misago/threads/api/postendpoints/delete.py
  11. 1 1
      misago/threads/api/postendpoints/patch_event.py
  12. 1 1
      misago/threads/api/postendpoints/patch_post.py
  13. 1 1
      misago/threads/api/postendpoints/split.py
  14. 16 3
      misago/threads/api/postingendpoint/__init__.py
  15. 3 4
      misago/threads/api/postingendpoint/category.py
  16. 2 2
      misago/threads/api/postingendpoint/moderationqueue.py
  17. 2 2
      misago/threads/api/postingendpoint/participants.py
  18. 2 3
      misago/threads/api/postingendpoint/privatethread.py
  19. 2 2
      misago/threads/api/postingendpoint/syncprivatethreads.py
  20. 2 2
      misago/threads/api/postingendpoint/updatestats.py
  21. 1 1
      misago/threads/api/threadendpoints/delete.py
  22. 2 3
      misago/threads/api/threadendpoints/editor.py
  23. 1 1
      misago/threads/api/threadendpoints/merge.py
  24. 1 1
      misago/threads/api/threadendpoints/patch.py
  25. 4 4
      misago/threads/api/threads.py
  26. 2 3
      misago/threads/permissions/privatethreads.py
  27. 2 2
      misago/threads/serializers/moderation.py
  28. 29 10
      misago/threads/tests/test_attachments_middleware.py
  29. 2 2
      misago/threads/tests/test_floodprotection.py
  30. 23 4
      misago/threads/tests/test_floodprotection_middleware.py
  31. 2 3
      misago/threads/tests/test_threads_api.py
  32. 2 3
      misago/threads/tests/test_threads_bulkdelete_api.py
  33. 17 18
      misago/threads/tests/test_threadview.py
  34. 2 2
      misago/threads/threadtypes/privatethread.py
  35. 2 2
      misago/threads/threadtypes/thread.py
  36. 2 3
      misago/threads/validators.py
  37. 3 4
      misago/threads/viewmodels/thread.py
  38. 5 5
      misago/users/bans.py

+ 0 - 2
misago/categories/__init__.py

@@ -1,3 +1 @@
-from .constants import *
-
 default_app_config = 'misago.categories.apps.MisagoCategoriesConfig'

+ 0 - 2
misago/categories/constants.py

@@ -1,2 +0,0 @@
-PRIVATE_THREADS_ROOT_NAME = 'private_threads'
-THREADS_ROOT_NAME = 'root_category'

+ 2 - 3
misago/categories/forms.py

@@ -9,8 +9,7 @@ from misago.core.forms import YesNoSwitch
 from misago.core.validators import validate_sluggable
 from misago.threads.threadtypes import trees_map
 
-from . import THREADS_ROOT_NAME
-from .models import Category, CategoryRole
+from .models import THREADS_ROOT, Category, CategoryRole
 
 
 class AdminCategoryFieldMixin(object):
@@ -18,7 +17,7 @@ class AdminCategoryFieldMixin(object):
         self.base_level = kwargs.pop('base_level', 1)
         kwargs['level_indicator'] = kwargs.get('level_indicator', '- - ')
 
-        threads_tree_id = trees_map.get_tree_id_for_root(THREADS_ROOT_NAME)
+        threads_tree_id = trees_map.get_tree_id_for_root(THREADS_ROOT)
         queryset = Category.objects.filter(tree_id=threads_tree_id)
         if not kwargs.pop('include_root', False):
             queryset = queryset.exclude(special_role="root_category")

+ 4 - 2
misago/categories/migrations/0002_default_categories.py

@@ -5,6 +5,8 @@ from django.db import migrations
 
 from misago.core.utils import slugify
 
+from misago.categories.models import PRIVATE_THREADS_ROOT, THREADS_ROOT
+
 
 _ = lambda s: s
 
@@ -13,7 +15,7 @@ def create_default_categories_tree(apps, schema_editor):
     Category = apps.get_model('misago_categories', 'Category')
 
     Category.objects.create(
-        special_role='private_threads',
+        special_role=PRIVATE_THREADS_ROOT,
         name='Private',
         slug='private',
         lft=1,
@@ -23,7 +25,7 @@ def create_default_categories_tree(apps, schema_editor):
     )
 
     root = Category.objects.create(
-        special_role='root_category',
+        special_role=THREADS_ROOT,
         name='Root',
         slug='root',
         lft=3,

+ 5 - 5
misago/categories/models.py

@@ -12,18 +12,18 @@ from misago.core.cache import cache
 from misago.core.utils import slugify
 from misago.threads.threadtypes import trees_map
 
-from . import PRIVATE_THREADS_ROOT_NAME, THREADS_ROOT_NAME
-
 
 CACHE_NAME = 'misago_categories_tree'
+PRIVATE_THREADS_ROOT = 'private_threads'
+THREADS_ROOT = 'root_category'
 
 
 class CategoryManager(TreeManager):
     def private_threads(self):
-        return self.get_special(PRIVATE_THREADS_ROOT_NAME)
+        return self.get_special(PRIVATE_THREADS_ROOT)
 
     def root_category(self):
-        return self.get_special(THREADS_ROOT_NAME)
+        return self.get_special(THREADS_ROOT)
 
     def get_special(self, special_role):
         cache_name = '%s_%s' % (CACHE_NAME, special_role)
@@ -35,7 +35,7 @@ class CategoryManager(TreeManager):
         return special_category
 
     def all_categories(self, include_root=False):
-        tree_id = trees_map.get_tree_id_for_root(THREADS_ROOT_NAME)
+        tree_id = trees_map.get_tree_id_for_root(THREADS_ROOT)
         queryset = self.filter(tree_id=tree_id)
         if not include_root:
             queryset = queryset.filter(level__gt=0)

+ 2 - 3
misago/categories/tests/test_category_model.py

@@ -1,5 +1,4 @@
-from misago.categories import THREADS_ROOT_NAME
-from misago.categories.models import Category
+from misago.categories.models import THREADS_ROOT, Category
 from misago.core.testutils import MisagoTestCase
 from misago.threads import testutils
 from misago.threads.threadtypes import trees_map
@@ -38,7 +37,7 @@ class CategoryManagerTests(MisagoTestCase):
         test_dict = Category.objects.get_categories_dict_from_db()
 
         for category in Category.objects.all():
-            threads_tree_id = trees_map.get_tree_id_for_root(THREADS_ROOT_NAME)
+            threads_tree_id = trees_map.get_tree_id_for_root(THREADS_ROOT)
             if category.tree_id == threads_tree_id:
                 self.assertIn(category.id, test_dict)
             else:

+ 2 - 3
misago/categories/views/categoriesadmin.py

@@ -4,9 +4,8 @@ from django.utils.translation import ugettext_lazy as _
 
 from misago.acl import version as acl_version
 from misago.admin.views import generic
-from misago.categories import THREADS_ROOT_NAME
 from misago.categories.forms import CategoryFormFactory, DeleteFormFactory
-from misago.categories.models import Category, RoleCategoryACL
+from misago.categories.models import THREADS_ROOT, Category, RoleCategoryACL
 from misago.threads.threadtypes import trees_map
 
 
@@ -19,7 +18,7 @@ class CategoryAdmin(generic.AdminBaseMixin):
     def get_target(self, kwargs):
         target = super(CategoryAdmin, self).get_target(kwargs)
 
-        threads_tree_id = trees_map.get_tree_id_for_root(THREADS_ROOT_NAME)
+        threads_tree_id = trees_map.get_tree_id_for_root(THREADS_ROOT)
 
         target_is_special = bool(target.special_role)
         target_not_in_categories_tree = target.tree_id != threads_tree_id

+ 1 - 1
misago/core/testproject/urlswitherrorhandlers.py

@@ -1,4 +1,4 @@
-from .urls import *
+from .urls import *  # pylint: disable=unused-wildcard-import
 
 
 handler403 = 'misago.core.testproject.views.mock_custom_403_error_page'

+ 2 - 2
misago/readtracker/signals.py

@@ -1,6 +1,6 @@
 from django.dispatch import Signal, receiver
 
-from misago.categories import PRIVATE_THREADS_ROOT_NAME
+from misago.categories.models import PRIVATE_THREADS_ROOT
 from misago.categories.signals import delete_category_content, move_category_content
 from misago.threads.signals import merge_thread, move_thread, merge_post, move_post
 
@@ -50,7 +50,7 @@ def decrease_unread_private_count(sender, **kwargs):
     user = sender
     thread = kwargs['thread']
 
-    if thread.category.thread_type.root_name != PRIVATE_THREADS_ROOT_NAME:
+    if thread.category.thread_type.root_name != PRIVATE_THREADS_ROOT:
         return
 
     if user.unread_private_threads:

+ 1 - 1
misago/threads/api/postendpoints/delete.py

@@ -5,7 +5,7 @@ from django.utils.translation import ugettext as _
 from django.utils.translation import ungettext
 
 from misago.conf import settings
-from misago.threads.moderation import posts as moderation
+from misago.threads import moderation
 from misago.threads.permissions import (
     allow_delete_best_answer, allow_delete_event, allow_delete_post)
 from misago.threads.permissions import exclude_invisible_posts

+ 1 - 1
misago/threads/api/postendpoints/patch_event.py

@@ -3,7 +3,7 @@ from django.utils.translation import ugettext as _
 
 from misago.acl import add_acl
 from misago.api.patch import ApiPatch
-from misago.threads.moderation import posts as moderation
+from misago.threads import moderation
 from misago.threads.permissions import allow_hide_event, allow_unhide_event
 
 

+ 1 - 1
misago/threads/api/postendpoints/patch_post.py

@@ -8,7 +8,7 @@ from misago.acl import add_acl
 from misago.api.patch import ApiPatch
 from misago.conf import settings
 from misago.threads.models import PostLike
-from misago.threads.moderation import posts as moderation
+from misago.threads import moderation
 from misago.threads.permissions import (
     allow_approve_post, allow_hide_best_answer, allow_hide_post, allow_protect_post,
     allow_unhide_post)

+ 1 - 1
misago/threads/api/postendpoints/split.py

@@ -4,8 +4,8 @@ from django.core.exceptions import PermissionDenied
 from django.utils import six
 from django.utils.translation import ugettext as _
 
+from misago.threads import moderation
 from misago.threads.models import Thread
-from misago.threads.moderation import threads as moderation
 from misago.threads.serializers import SplitPostsSerializer
 
 

+ 16 - 3
misago/threads/api/postingendpoint/__init__.py

@@ -24,12 +24,13 @@ class PostingEndpoint(object):
         # we are using lock on user model to protect us from flood
         request.user.lock()
 
+        # store mode
+        self.mode = mode
+
         # build kwargs dict for passing to middlewares
         self.kwargs = kwargs
         self.kwargs.update({'mode': mode, 'request': request, 'user': request.user})
 
-        self.__dict__.update(kwargs)
-
         # some middlewares (eg. emailnotification) may call render()
         # which will crash if this isn't set to false
         request.include_frontend_context = False
@@ -134,7 +135,19 @@ class PostingMiddleware(object):
 
     def __init__(self, **kwargs):
         self.kwargs = kwargs
-        self.__dict__.update(kwargs)
+
+        self.prefix = kwargs['prefix']
+
+        self.mode = kwargs['mode']
+        self.request = kwargs['request']
+        self.user = kwargs['user']
+
+        self.datetime = kwargs['datetime']
+        self.parsing_result = kwargs['parsing_result']
+        self.tree_name = kwargs.get('tree_name')
+
+        self.thread = kwargs['thread']
+        self.post = kwargs['post']
 
     def use_this_middleware(self):
         return True

+ 3 - 4
misago/threads/api/postingendpoint/category.py

@@ -5,8 +5,7 @@ from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext_lazy
 
 from misago.acl import add_acl
-from misago.categories import THREADS_ROOT_NAME
-from misago.categories.models import Category
+from misago.categories.models import THREADS_ROOT, Category
 from misago.categories.permissions import can_browse_category, can_see_category
 from misago.threads.permissions import allow_start_thread
 from misago.threads.threadtypes import trees_map
@@ -19,7 +18,7 @@ class CategoryMiddleware(PostingMiddleware):
 
     def use_this_middleware(self):
         if self.mode == PostingEndpoint.START:
-            return self.tree_name == THREADS_ROOT_NAME
+            return self.tree_name == THREADS_ROOT
         return False
 
     def get_serializer(self):
@@ -56,7 +55,7 @@ class CategorySerializer(serializers.Serializer):
     def validate_category(self, value):
         try:
             self.category_cache = Category.objects.get(
-                pk=value, tree_id=trees_map.get_tree_id_for_root(THREADS_ROOT_NAME)
+                pk=value, tree_id=trees_map.get_tree_id_for_root(THREADS_ROOT)
             )
 
             can_see = can_see_category(self.user, self.category_cache)

+ 2 - 2
misago/threads/api/postingendpoint/moderationqueue.py

@@ -1,4 +1,4 @@
-from misago.categories import PRIVATE_THREADS_ROOT_NAME
+from misago.categories.models import PRIVATE_THREADS_ROOT
 
 from . import PostingEndpoint, PostingMiddleware
 
@@ -10,7 +10,7 @@ class ModerationQueueMiddleware(PostingMiddleware):
         except AttributeError:
             tree_name = self.thread.category.thread_type.root_name
 
-        return tree_name != PRIVATE_THREADS_ROOT_NAME
+        return tree_name != PRIVATE_THREADS_ROOT
 
     def save(self, serializer):
         if self.mode == PostingEndpoint.START:

+ 2 - 2
misago/threads/api/postingendpoint/participants.py

@@ -6,7 +6,7 @@ from django.utils import six
 from django.utils.translation import ugettext as _
 from django.utils.translation import ungettext
 
-from misago.categories import PRIVATE_THREADS_ROOT_NAME
+from misago.categories.models import PRIVATE_THREADS_ROOT
 from misago.threads.participants import add_participants, set_owner
 from misago.threads.permissions import allow_message_user
 
@@ -19,7 +19,7 @@ UserModel = get_user_model()
 class ParticipantsMiddleware(PostingMiddleware):
     def use_this_middleware(self):
         if self.mode == PostingEndpoint.START:
-            return self.tree_name == PRIVATE_THREADS_ROOT_NAME
+            return self.tree_name == PRIVATE_THREADS_ROOT
         return False
 
     def get_serializer(self):

+ 2 - 3
misago/threads/api/postingendpoint/privatethread.py

@@ -1,6 +1,5 @@
 from misago.acl import add_acl
-from misago.categories import PRIVATE_THREADS_ROOT_NAME
-from misago.categories.models import Category
+from misago.categories.models import PRIVATE_THREADS_ROOT, Category
 
 from . import PostingEndpoint, PostingMiddleware
 
@@ -10,7 +9,7 @@ class PrivateThreadMiddleware(PostingMiddleware):
 
     def use_this_middleware(self):
         if self.mode == PostingEndpoint.START:
-            return self.tree_name == PRIVATE_THREADS_ROOT_NAME
+            return self.tree_name == PRIVATE_THREADS_ROOT
         return False
 
     def pre_save(self, serializer):

+ 2 - 2
misago/threads/api/postingendpoint/syncprivatethreads.py

@@ -1,4 +1,4 @@
-from misago.categories import PRIVATE_THREADS_ROOT_NAME
+from misago.categories.models import PRIVATE_THREADS_ROOT
 from misago.threads.participants import set_users_unread_private_threads_sync
 
 from . import PostingEndpoint, PostingMiddleware
@@ -9,7 +9,7 @@ class SyncPrivateThreadsMiddleware(PostingMiddleware):
 
     def use_this_middleware(self):
         if self.mode == PostingEndpoint.REPLY:
-            return self.thread.thread_type.root_name == PRIVATE_THREADS_ROOT_NAME
+            return self.thread.thread_type.root_name == PRIVATE_THREADS_ROOT
         return False
 
     def post_save(self, serializer):

+ 2 - 2
misago/threads/api/postingendpoint/updatestats.py

@@ -1,6 +1,6 @@
 from django.db.models import F
 
-from misago.categories import THREADS_ROOT_NAME
+from misago.categories.models import THREADS_ROOT
 
 from . import PostingEndpoint, PostingMiddleware
 
@@ -41,7 +41,7 @@ class UpdateStatsMiddleware(PostingMiddleware):
         if post.is_unapproved:
             return  # don't update user on moderated post
 
-        if self.thread.thread_type.root_name == THREADS_ROOT_NAME:
+        if self.thread.thread_type.root_name == THREADS_ROOT:
             if self.mode == PostingEndpoint.START:
                 user.threads = F('threads') + 1
                 user.update_fields.append('threads')

+ 1 - 1
misago/threads/api/threadendpoints/delete.py

@@ -2,7 +2,7 @@ from rest_framework.response import Response
 
 from django.db import transaction
 
-from misago.threads.moderation import threads as moderation
+from misago.threads import moderation
 from misago.threads.permissions import allow_delete_thread
 from misago.threads.serializers import DeleteThreadsSerializer
 

+ 2 - 3
misago/threads/api/threadendpoints/editor.py

@@ -4,8 +4,7 @@ from django.core.exceptions import PermissionDenied
 from django.utils.translation import ugettext as _
 
 from misago.acl import add_acl
-from misago.categories import THREADS_ROOT_NAME
-from misago.categories.models import Category
+from misago.categories.models import THREADS_ROOT, Category
 from misago.threads.permissions import can_start_thread
 from misago.threads.threadtypes import trees_map
 
@@ -20,7 +19,7 @@ def thread_start_editor(request):
 
     queryset = Category.objects.filter(
         pk__in=request.user.acl_cache['browseable_categories'],
-        tree_id=trees_map.get_tree_id_for_root(THREADS_ROOT_NAME)
+        tree_id=trees_map.get_tree_id_for_root(THREADS_ROOT)
     ).order_by('-lft')
 
     for category in queryset:

+ 1 - 1
misago/threads/api/threadendpoints/merge.py

@@ -6,9 +6,9 @@ from django.utils.six import text_type
 from django.utils.translation import ugettext as _
 
 from misago.acl import add_acl
+from misago.threads import moderation
 from misago.threads.events import record_event
 from misago.threads.models import Thread
-from misago.threads.moderation import threads as moderation
 from misago.threads.permissions import allow_merge_thread
 from misago.threads.serializers import (
     MergeThreadSerializer, MergeThreadsSerializer, ThreadsListSerializer)

+ 1 - 1
misago/threads/api/threadendpoints/patch.py

@@ -15,7 +15,7 @@ from misago.categories.permissions import allow_browse_category, allow_see_categ
 from misago.categories.serializers import CategorySerializer
 from misago.conf import settings
 from misago.core.shortcuts import get_int_or_404
-from misago.threads.moderation import threads as moderation
+from misago.threads import moderation
 from misago.threads.participants import (
     add_participant, change_owner, make_participants_aware, remove_participant
 )

+ 4 - 4
misago/threads/api/threads.py

@@ -6,10 +6,10 @@ from django.core.exceptions import PermissionDenied
 from django.db import transaction
 from django.utils.translation import ugettext as _
 
-from misago.categories import PRIVATE_THREADS_ROOT_NAME, THREADS_ROOT_NAME
+from misago.categories.models import PRIVATE_THREADS_ROOT, THREADS_ROOT
 from misago.core.shortcuts import get_int_or_404
 from misago.threads.models import Post, Thread
-from misago.threads.moderation import threads as moderation
+from misago.threads import moderation
 from misago.threads.permissions import allow_use_private_threads
 from misago.threads.viewmodels import (ForumThread, PrivateThread,
     ThreadsRootCategory, PrivateThreadsCategory)
@@ -77,7 +77,7 @@ class ThreadViewSet(ViewSet):
         posting = PostingEndpoint(
             request,
             PostingEndpoint.START,
-            tree_name=THREADS_ROOT_NAME,
+            tree_name=THREADS_ROOT,
             thread=thread,
             post=post,
         )
@@ -132,7 +132,7 @@ class PrivateThreadViewSet(ViewSet):
         posting = PostingEndpoint(
             request,
             PostingEndpoint.START,
-            tree_name=PRIVATE_THREADS_ROOT_NAME,
+            tree_name=PRIVATE_THREADS_ROOT,
             thread=thread,
             post=post,
         )

+ 2 - 3
misago/threads/permissions/privatethreads.py

@@ -6,8 +6,7 @@ from django.utils.translation import ugettext_lazy as _
 from misago.acl import algebra
 from misago.acl.decorators import return_boolean
 from misago.acl.models import Role
-from misago.categories import PRIVATE_THREADS_ROOT_NAME
-from misago.categories.models import Category
+from misago.categories.models import PRIVATE_THREADS_ROOT, Category
 from misago.core.forms import YesNoSwitch
 from misago.threads.models import Thread
 
@@ -153,7 +152,7 @@ def build_acl(acl, roles, key_name):
 
 
 def add_acl_to_thread(user, thread):
-    if thread.thread_type.root_name != PRIVATE_THREADS_ROOT_NAME:
+    if thread.thread_type.root_name != PRIVATE_THREADS_ROOT:
         return
 
     if not hasattr(thread, 'participant'):

+ 2 - 2
misago/threads/serializers/moderation.py

@@ -6,7 +6,7 @@ from django.http import Http404
 from django.utils.translation import ugettext as _, ugettext_lazy, ungettext
 
 from misago.acl import add_acl
-from misago.categories import THREADS_ROOT_NAME
+from misago.categories.models import THREADS_ROOT
 from misago.conf import settings
 from misago.threads.mergeconflict import MergeConflict
 from misago.threads.models import Thread
@@ -508,7 +508,7 @@ class MergeThreadsSerializer(NewThreadSerializer):
     def get_valid_threads(self, threads_ids):
         user = self.context['user']
 
-        threads_tree_id = trees_map.get_tree_id_for_root(THREADS_ROOT_NAME)
+        threads_tree_id = trees_map.get_tree_id_for_root(THREADS_ROOT)
         threads_queryset = Thread.objects.filter(
             id__in=threads_ids,
             category__tree_id=threads_tree_id,

+ 29 - 10
misago/threads/tests/test_attachments_middleware.py

@@ -16,6 +16,25 @@ class RequestMock(object):
         self.data = data or {}
 
 
+def get_middleware_for_testing(**kwargs):
+    mock_kwargs = {
+        'prefix': 'test',
+        'mode': 0,
+
+        'request': None,
+        'user': None,
+
+        'datetime': None,
+        'parsing_result': None,
+
+        'thread': None,
+        'post': None,
+    }
+    mock_kwargs.update(kwargs)
+
+    return AttachmentsMiddleware(**mock_kwargs)
+
+
 class AttachmentsMiddlewareTests(AuthenticatedUserTestCase):
     def setUp(self):
         super(AttachmentsMiddlewareTests, self).setUp()
@@ -47,7 +66,7 @@ class AttachmentsMiddlewareTests(AuthenticatedUserTestCase):
 
     def test_use_this_middleware(self):
         """use_this_middleware returns False if we can't upload attachments"""
-        middleware = AttachmentsMiddleware(user=self.user)
+        middleware = get_middleware_for_testing(user=self.user)
 
         self.override_acl({'max_attachment_size': 0})
 
@@ -62,7 +81,7 @@ class AttachmentsMiddlewareTests(AuthenticatedUserTestCase):
         INPUTS = [{}, {'attachments': []}]
 
         for test_input in INPUTS:
-            middleware = AttachmentsMiddleware(
+            middleware = get_middleware_for_testing(
                 request=RequestMock(test_input),
                 mode=PostingEndpoint.START,
                 user=self.user,
@@ -77,7 +96,7 @@ class AttachmentsMiddlewareTests(AuthenticatedUserTestCase):
         INPUTS = ['none', ['a', 'b', 123], range(settings.MISAGO_POST_ATTACHMENTS_LIMIT + 1)]
 
         for test_input in INPUTS:
-            middleware = AttachmentsMiddleware(
+            middleware = get_middleware_for_testing(
                 request=RequestMock({
                     'attachments': test_input
                 }),
@@ -91,7 +110,7 @@ class AttachmentsMiddlewareTests(AuthenticatedUserTestCase):
 
     def test_get_initial_attachments(self):
         """get_initial_attachments returns list of attachments already existing on post"""
-        middleware = AttachmentsMiddleware(
+        middleware = get_middleware_for_testing(
             request=RequestMock(),
             mode=PostingEndpoint.EDIT,
             user=self.user,
@@ -113,7 +132,7 @@ class AttachmentsMiddlewareTests(AuthenticatedUserTestCase):
 
     def test_get_new_attachments(self):
         """get_initial_attachments returns list of attachments already existing on post"""
-        middleware = AttachmentsMiddleware(
+        middleware = get_middleware_for_testing(
             request=RequestMock(),
             mode=PostingEndpoint.EDIT,
             user=self.user,
@@ -144,7 +163,7 @@ class AttachmentsMiddlewareTests(AuthenticatedUserTestCase):
         attachment = self.mock_attachment(user=False, post=self.post)
         self.assertIsNone(attachment.uploader)
 
-        serializer = AttachmentsMiddleware(
+        serializer = get_middleware_for_testing(
             request=RequestMock({
                 'attachments': []
             }),
@@ -162,7 +181,7 @@ class AttachmentsMiddlewareTests(AuthenticatedUserTestCase):
             self.mock_attachment(),
         ]
 
-        middleware = AttachmentsMiddleware(
+        middleware = get_middleware_for_testing(
             request=RequestMock({
                 'attachments': [a.pk for a in attachments]
             }),
@@ -190,7 +209,7 @@ class AttachmentsMiddlewareTests(AuthenticatedUserTestCase):
             self.mock_attachment(post=self.post),
         ]
 
-        middleware = AttachmentsMiddleware(
+        middleware = get_middleware_for_testing(
             request=RequestMock({
                 'attachments': [attachments[0].pk]
             }),
@@ -222,7 +241,7 @@ class AttachmentsMiddlewareTests(AuthenticatedUserTestCase):
             self.mock_attachment(),
         ]
 
-        middleware = AttachmentsMiddleware(
+        middleware = get_middleware_for_testing(
             request=RequestMock({
                 'attachments': [attachments[0].pk, attachments[1].pk]
             }),
@@ -250,7 +269,7 @@ class AttachmentsMiddlewareTests(AuthenticatedUserTestCase):
             self.mock_attachment(),
         ]
 
-        middleware = AttachmentsMiddleware(
+        middleware = get_middleware_for_testing(
             request=RequestMock({
                 'attachments': [attachments[0].pk, attachments[2].pk]
             }),

+ 2 - 2
misago/threads/tests/test_floodprotection.py

@@ -9,9 +9,9 @@ from misago.threads import testutils
 from misago.users.testutils import AuthenticatedUserTestCase
 
 
-class PostMentionsTests(AuthenticatedUserTestCase):
+class FloodProtectionTests(AuthenticatedUserTestCase):
     def setUp(self):
-        super(PostMentionsTests, self).setUp()
+        super(FloodProtectionTests, self).setUp()
 
         self.category = Category.objects.get(slug='first-category')
         self.thread = testutils.post_thread(category=self.category)

+ 23 - 4
misago/threads/tests/test_floodprotection_middleware.py

@@ -8,13 +8,32 @@ from misago.threads.api.postingendpoint.floodprotection import FloodProtectionMi
 from misago.users.testutils import AuthenticatedUserTestCase
 
 
+def get_middleware_for_testing(**kwargs):
+    mock_kwargs = {
+        'prefix': 'test',
+        'mode': 0,
+
+        'request': None,
+        'user': None,
+
+        'datetime': None,
+        'parsing_result': None,
+
+        'thread': None,
+        'post': None,
+    }
+    mock_kwargs.update(kwargs)
+
+    return FloodProtectionMiddleware(**mock_kwargs)
+
+
 class FloodProtectionMiddlewareTests(AuthenticatedUserTestCase):
     def test_flood_protection_middleware_on_no_posts(self):
         """middleware sets last_posted_on on user"""
         self.user.update_fields = []
         self.assertIsNone(self.user.last_posted_on)
 
-        middleware = FloodProtectionMiddleware(user=self.user)
+        middleware = get_middleware_for_testing(user=self.user)
         middleware.interrupt_posting(None)
 
         self.assertIsNotNone(self.user.last_posted_on)
@@ -26,7 +45,7 @@ class FloodProtectionMiddlewareTests(AuthenticatedUserTestCase):
         original_last_posted_on = timezone.now() - timedelta(days=1)
         self.user.last_posted_on = original_last_posted_on
 
-        middleware = FloodProtectionMiddleware(user=self.user)
+        middleware = get_middleware_for_testing(user=self.user)
         middleware.interrupt_posting(None)
 
         self.assertTrue(self.user.last_posted_on > original_last_posted_on)
@@ -36,12 +55,12 @@ class FloodProtectionMiddlewareTests(AuthenticatedUserTestCase):
         self.user.last_posted_on = timezone.now()
 
         with self.assertRaises(PostingInterrupt):
-            middleware = FloodProtectionMiddleware(user=self.user)
+            middleware = get_middleware_for_testing(user=self.user)
             middleware.interrupt_posting(None)
 
     def test_flood_permission(self):
         """middleware is respects permission to flood for team members"""
         override_acl(self.user, {'can_omit_flood_protection': True})
 
-        middleware = FloodProtectionMiddleware(user=self.user)
+        middleware = get_middleware_for_testing(user=self.user)
         self.assertFalse(middleware.use_this_middleware())

+ 2 - 3
misago/threads/tests/test_threads_api.py

@@ -4,8 +4,7 @@ from django.utils import timezone
 from django.urls import reverse
 
 from misago.acl.testutils import override_acl
-from misago.categories import THREADS_ROOT_NAME
-from misago.categories.models import Category
+from misago.categories.models import THREADS_ROOT, Category
 from misago.threads import testutils
 from misago.threads.models import Thread
 from misago.threads.threadtypes import trees_map
@@ -16,7 +15,7 @@ class ThreadsApiTestCase(AuthenticatedUserTestCase):
     def setUp(self):
         super(ThreadsApiTestCase, self).setUp()
 
-        threads_tree_id = trees_map.get_tree_id_for_root(THREADS_ROOT_NAME)
+        threads_tree_id = trees_map.get_tree_id_for_root(THREADS_ROOT)
 
         self.root = Category.objects.get(tree_id=threads_tree_id, level=0)
         self.category = Category.objects.get(slug='first-category')

+ 2 - 3
misago/threads/tests/test_threads_bulkdelete_api.py

@@ -4,8 +4,7 @@ from django.urls import reverse
 from django.utils import six
 
 from misago.acl.testutils import override_acl
-from misago.categories import PRIVATE_THREADS_ROOT_NAME
-from misago.categories.models import Category
+from misago.categories.models import PRIVATE_THREADS_ROOT, Category
 from misago.threads import testutils
 from misago.threads.models import Thread
 from misago.threads.serializers.moderation import THREADS_LIMIT
@@ -223,7 +222,7 @@ class ThreadsBulkDeleteApiTests(ThreadsApiTestCase):
         private_thread = self.threads[0]
 
         private_thread.category = Category.objects.get(
-            tree_id=trees_map.get_tree_id_for_root(PRIVATE_THREADS_ROOT_NAME),
+            tree_id=trees_map.get_tree_id_for_root(PRIVATE_THREADS_ROOT),
         )
         private_thread.save()
 

+ 17 - 18
misago/threads/tests/test_threadview.py

@@ -2,11 +2,10 @@
 from misago.acl.testutils import override_acl
 from misago.categories.models import Category
 from misago.conf import settings
-from misago.threads import testutils
+from misago.threads import moderation, testutils
 from misago.threads.checksums import update_post_checksum
 from misago.threads.events import record_event
-from misago.threads.moderation import threads as threads_moderation
-from misago.threads.moderation import hide_post
+from misago.threads import moderation
 from misago.users.testutils import AuthenticatedUserTestCase
 
 
@@ -156,7 +155,7 @@ class ThreadPostsVisibilityTests(ThreadViewTestCase):
     def test_hidden_post_visibility(self):
         """hidden post renders correctly"""
         post = testutils.reply_thread(self.thread, message="Hello, I'm hidden post!")
-        hide_post(self.user, post)
+        moderation.hide_post(self.user, post)
 
         response = self.client.get(self.thread.get_absolute_url())
         self.assertContains(response, post.get_absolute_url())
@@ -223,14 +222,14 @@ class ThreadEventVisibilityTests(ThreadViewTestCase):
     def test_thread_events_render(self):
         """different thread events render"""
         TEST_ACTIONS = [
-            (threads_moderation.pin_thread_globally, "Thread has been pinned globally."),
-            (threads_moderation.pin_thread_locally, "Thread has been pinned locally."),
-            (threads_moderation.unpin_thread, "Thread has been unpinned."),
-            (threads_moderation.approve_thread, "Thread has been approved."),
-            (threads_moderation.close_thread, "Thread has been closed."),
-            (threads_moderation.open_thread, "Thread has been opened."),
-            (threads_moderation.hide_thread, "Thread has been made hidden."),
-            (threads_moderation.unhide_thread, "Thread has been revealed."),
+            (moderation.pin_thread_globally, "Thread has been pinned globally."),
+            (moderation.pin_thread_locally, "Thread has been pinned locally."),
+            (moderation.unpin_thread, "Thread has been unpinned."),
+            (moderation.approve_thread, "Thread has been approved."),
+            (moderation.close_thread, "Thread has been closed."),
+            (moderation.open_thread, "Thread has been opened."),
+            (moderation.hide_thread, "Thread has been made hidden."),
+            (moderation.unhide_thread, "Thread has been revealed."),
         ]
 
         self.thread.is_unapproved = True
@@ -250,7 +249,7 @@ class ThreadEventVisibilityTests(ThreadViewTestCase):
             self.assertContains(response, message)
 
             # hidden events don't render without permission
-            hide_post(self.user, event)
+            moderation.hide_post(self.user, event)
             self.override_acl({'can_approve_content': 1, 'can_hide_threads': 1})
 
             response = self.client.get(self.thread.get_absolute_url())
@@ -258,7 +257,7 @@ class ThreadEventVisibilityTests(ThreadViewTestCase):
             self.assertNotContains(response, message)
 
             # hidden event renders with permission
-            hide_post(self.user, event)
+            moderation.hide_post(self.user, event)
             self.override_acl({
                 'can_approve_content': 1,
                 'can_hide_threads': 1,
@@ -347,7 +346,7 @@ class ThreadEventVisibilityTests(ThreadViewTestCase):
 
     def test_changed_thread_title_event_renders(self):
         """changed thread title event renders"""
-        threads_moderation.change_thread_title(
+        moderation.change_thread_title(
             MockRequest(self.user), self.thread, "Lorem renamed ipsum!"
         )
 
@@ -365,7 +364,7 @@ class ThreadEventVisibilityTests(ThreadViewTestCase):
         self.thread.category = self.thread.category.parent
         self.thread.save()
 
-        threads_moderation.move_thread(MockRequest(self.user), self.thread, self.category)
+        moderation.move_thread(MockRequest(self.user), self.thread, self.category)
 
         event = self.thread.post_set.filter(is_event=True)[0]
         self.assertEqual(event.event_type, 'moved')
@@ -378,7 +377,7 @@ class ThreadEventVisibilityTests(ThreadViewTestCase):
     def test_thread_merged_event_renders(self):
         """merged thread event renders"""
         other_thread = testutils.post_thread(category=self.category)
-        threads_moderation.merge_thread(MockRequest(self.user), self.thread, other_thread)
+        moderation.merge_thread(MockRequest(self.user), self.thread, other_thread)
 
         event = self.thread.post_set.filter(is_event=True)[0]
         self.assertEqual(event.event_type, 'merged')
@@ -510,7 +509,7 @@ class ThreadAnonViewTests(ThreadViewTestCase):
         event = record_event(MockRequest(self.user), self.thread, 'closed')
 
         hidden_event = record_event(MockRequest(self.user), self.thread, 'opened')
-        hide_post(self.user, hidden_event)
+        moderation.hide_post(self.user, hidden_event)
 
         unapproved_post = testutils.reply_thread(self.thread, is_unapproved=True)
         post = testutils.reply_thread(self.thread)

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

@@ -1,13 +1,13 @@
 from django.urls import reverse
 from django.utils.translation import ugettext_lazy as _
 
-from misago.categories import PRIVATE_THREADS_ROOT_NAME
+from misago.categories.models import PRIVATE_THREADS_ROOT
 
 from . import ThreadType
 
 
 class PrivateThread(ThreadType):
-    root_name = PRIVATE_THREADS_ROOT_NAME
+    root_name = PRIVATE_THREADS_ROOT
 
     def get_category_name(self, category):
         return _('Private threads')

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

@@ -1,13 +1,13 @@
 from django.urls import reverse
 from django.utils.translation import ugettext_lazy as _
 
-from misago.categories import THREADS_ROOT_NAME
+from misago.categories.models import THREADS_ROOT
 
 from . import ThreadType
 
 
 class Thread(ThreadType):
-    root_name = THREADS_ROOT_NAME
+    root_name = THREADS_ROOT
 
     def get_category_name(self, category):
         if category.level:

+ 2 - 3
misago/threads/validators.py

@@ -3,8 +3,7 @@ from django.utils.module_loading import import_string
 from django.utils.translation import ugettext as _
 from django.utils.translation import ungettext
 
-from misago.categories import THREADS_ROOT_NAME
-from misago.categories.models import Category
+from misago.categories.models import THREADS_ROOT, Category
 from misago.categories.permissions import can_browse_category, can_see_category
 from misago.conf import settings
 from misago.core.validators import validate_sluggable
@@ -14,7 +13,7 @@ from .threadtypes import trees_map
 
 def validate_category(user, category_id, allow_root=False):
     try:
-        threads_tree_id = trees_map.get_tree_id_for_root(THREADS_ROOT_NAME)
+        threads_tree_id = trees_map.get_tree_id_for_root(THREADS_ROOT)
         category = Category.objects.get(
             tree_id=threads_tree_id,
             id=category_id,

+ 3 - 4
misago/threads/viewmodels/thread.py

@@ -2,8 +2,7 @@ from django.shortcuts import get_object_or_404
 from django.utils.translation import ugettext as _
 
 from misago.acl import add_acl
-from misago.categories import PRIVATE_THREADS_ROOT_NAME, THREADS_ROOT_NAME
-from misago.categories.models import Category
+from misago.categories.models import PRIVATE_THREADS_ROOT, THREADS_ROOT, Category
 from misago.core.shortcuts import validate_slug
 from misago.core.viewmodel import ViewModel as BaseViewModel
 from misago.readtracker.threadstracker import make_read_aware
@@ -106,7 +105,7 @@ class ForumThread(ViewModel):
         thread = get_object_or_404(
             Thread.objects.select_related(*BASE_RELATIONS),
             pk=pk,
-            category__tree_id=trees_map.get_tree_id_for_root(THREADS_ROOT_NAME),
+            category__tree_id=trees_map.get_tree_id_for_root(THREADS_ROOT),
         )
 
         allow_see_thread(request.user, thread)
@@ -128,7 +127,7 @@ class PrivateThread(ViewModel):
         thread = get_object_or_404(
             Thread.objects.select_related(*BASE_RELATIONS),
             pk=pk,
-            category__tree_id=trees_map.get_tree_id_for_root(PRIVATE_THREADS_ROOT_NAME),
+            category__tree_id=trees_map.get_tree_id_for_root(PRIVATE_THREADS_ROOT),
         )
 
         make_participants_aware(request.user, thread)

+ 5 - 5
misago/users/bans.py

@@ -11,11 +11,11 @@ from django.utils.dateparse import parse_datetime
 
 from misago.core import cachebuster
 
+from .constants import BANS_CACHEBUSTER
 from .models import Ban, BanCache
 
 
 CACHE_SESSION_KEY = 'misago_ip_check'
-VERSION_KEY = 'misago_bans'
 
 
 def get_username_ban(username, registration_only=False):
@@ -63,7 +63,7 @@ def get_user_ban(user):
 
 def _set_user_ban_cache(user):
     ban_cache = user.ban_cache
-    ban_cache.bans_version = cachebuster.get_version(VERSION_KEY)
+    ban_cache.bans_version = cachebuster.get_version(BANS_CACHEBUSTER)
 
     try:
         user_ban = Ban.objects.get_ban(username=user.username, email=user.email)
@@ -99,7 +99,7 @@ def get_request_ip_ban(request):
     found_ban = get_ip_ban(request.user_ip)
 
     ban_cache = request.session[CACHE_SESSION_KEY] = {
-        'version': cachebuster.get_version(VERSION_KEY),
+        'version': cachebuster.get_version(BANS_CACHEBUSTER),
         'ip': request.user_ip,
     }
 
@@ -124,10 +124,10 @@ def _get_session_bancache(request):
         ban_cache = _hydrate_session_cache(ban_cache)
         if ban_cache['ip'] != request.user_ip:
             return None
-        if not cachebuster.is_valid(VERSION_KEY, ban_cache['version']):
+        if not cachebuster.is_valid(BANS_CACHEBUSTER, ban_cache['version']):
             return None
         if ban_cache.get('expires_on'):
-            if ban_cache['expires_on'] < timezone.today():
+            if ban_cache['expires_on'] < timezone.now():
                 return None
         return ban_cache
     except KeyError: