Browse Source

Merge pull request #729 from rafalp/constants-cleanup

fix #648
Rafał Pitoń 8 years ago
parent
commit
60871b4c9a
60 changed files with 318 additions and 292 deletions
  1. 0 1
      misago/acl/__init__.py
  2. 1 0
      misago/acl/constants.py
  3. 3 1
      misago/acl/migrations/0002_acl_version_tracker.py
  4. 8 2
      misago/acl/tests/test_api.py
  5. 6 6
      misago/acl/tests/test_providers.py
  6. 5 6
      misago/acl/version.py
  7. 7 5
      misago/admin/views/index.py
  8. 3 0
      misago/categories/__init__.py
  9. 2 0
      misago/categories/constants.py
  10. 2 1
      misago/categories/forms.py
  11. 2 2
      misago/categories/models.py
  12. 2 1
      misago/categories/tests/test_category_model.py
  13. 3 2
      misago/categories/views/categoriesadmin.py
  14. 1 1
      misago/core/errorpages.py
  15. 9 9
      misago/core/tests/test_apipatch.py
  16. 5 5
      misago/faker/management/commands/createfakebans.py
  17. 1 1
      misago/readtracker/signals.py
  18. 2 1
      misago/threads/api/postingendpoint/category.py
  19. 1 1
      misago/threads/api/postingendpoint/participants.py
  20. 2 1
      misago/threads/api/postingendpoint/privatethread.py
  21. 8 5
      misago/threads/api/postingendpoint/subscribe.py
  22. 1 1
      misago/threads/api/postingendpoint/syncprivatethreads.py
  23. 1 1
      misago/threads/api/postingendpoint/updatestats.py
  24. 2 1
      misago/threads/api/threadendpoints/editor.py
  25. 2 1
      misago/threads/api/threadendpoints/merge.py
  26. 2 1
      misago/threads/api/threadendpoints/read.py
  27. 1 1
      misago/threads/api/threads.py
  28. 2 19
      misago/threads/permissions/privatethreads.py
  29. 8 3
      misago/threads/tests/test_privatethread_start_api.py
  30. 13 10
      misago/threads/tests/test_subscription_middleware.py
  31. 5 2
      misago/threads/tests/test_subscriptions.py
  32. 2 1
      misago/threads/tests/test_thread_start_api.py
  33. 2 1
      misago/threads/tests/test_threads_api.py
  34. 1 1
      misago/threads/threadtypes/privatethread.py
  35. 1 1
      misago/threads/threadtypes/thread.py
  36. 2 1
      misago/threads/validators.py
  37. 2 1
      misago/threads/viewmodels/thread.py
  38. 2 2
      misago/users/api/rest_permissions.py
  39. 6 5
      misago/users/api/userendpoints/create.py
  40. 12 12
      misago/users/bans.py
  41. 1 0
      misago/users/constants.py
  42. 4 3
      misago/users/decorators.py
  43. 19 20
      misago/users/forms/admin.py
  44. 14 8
      misago/users/forms/options.py
  45. 3 1
      misago/users/migrations/0003_bans_version_tracker.py
  46. 22 32
      misago/users/models/ban.py
  47. 36 54
      misago/users/models/user.py
  48. 3 3
      misago/users/permissions/profiles.py
  49. 2 2
      misago/users/serializers/ban.py
  50. 2 2
      misago/users/tests/test_activation_views.py
  51. 6 6
      misago/users/tests/test_auth_api.py
  52. 13 4
      misago/users/tests/test_ban_model.py
  53. 14 12
      misago/users/tests/test_bans.py
  54. 7 5
      misago/users/tests/test_decorators.py
  55. 2 2
      misago/users/tests/test_forgottenpassword_views.py
  56. 7 5
      misago/users/tests/test_rest_permissions.py
  57. 6 4
      misago/users/tests/test_users_api.py
  58. 9 3
      misago/users/tests/test_validators.py
  59. 1 2
      misago/users/views/activation.py
  60. 7 8
      misago/users/views/admin/users.py

+ 0 - 1
misago/acl/__init__.py

@@ -1,4 +1,3 @@
 from .api import get_user_acl, add_acl, serialize_acl
 from .api import get_user_acl, add_acl, serialize_acl
 
 
-
 default_app_config = 'misago.acl.apps.MisagoACLsConfig'
 default_app_config = 'misago.acl.apps.MisagoACLsConfig'

+ 1 - 0
misago/acl/constants.py

@@ -0,0 +1 @@
+ACL_CACHEBUSTER = 'misago_acl'

+ 3 - 1
misago/acl/migrations/0002_acl_version_tracker.py

@@ -6,9 +6,11 @@ from django.db import migrations, models
 
 
 from misago.core.migrationutils import cachebuster_register_cache
 from misago.core.migrationutils import cachebuster_register_cache
 
 
+from misago.acl.constants import ACL_CACHEBUSTER
+
 
 
 def register_acl_version_tracker(apps, schema_editor):
 def register_acl_version_tracker(apps, schema_editor):
-    cachebuster_register_cache(apps, 'misago_acl')
+    cachebuster_register_cache(apps, ACL_CACHEBUSTER)
 
 
 
 
 class Migration(migrations.Migration):
 class Migration(migrations.Migration):

+ 8 - 2
misago/acl/tests/test_api.py

@@ -1,14 +1,20 @@
+from django.contrib.auth import get_user_model
 from django.test import TestCase
 from django.test import TestCase
 
 
-from misago.users.models import AnonymousUser, User
+from misago.users.models import AnonymousUser
 
 
 from ..api import get_user_acl
 from ..api import get_user_acl
 
 
 
 
+UserModel = get_user_model()
+
+
 class GetUserACLTests(TestCase):
 class GetUserACLTests(TestCase):
     def test_get_authenticated_acl(self):
     def test_get_authenticated_acl(self):
         """get ACL for authenticated user"""
         """get ACL for authenticated user"""
-        test_user = User.objects.create_user('Bob', 'bob@bob.com', 'pass123')
+        test_user = UserModel.objects.create_user(
+            'Bob', 'bob@bob.com', 'pass123')
+
         acl = get_user_acl(test_user)
         acl = get_user_acl(test_user)
 
 
         self.assertTrue(acl)
         self.assertTrue(acl)

+ 6 - 6
misago/acl/tests/test_providers.py

@@ -67,20 +67,20 @@ class PermissionProvidersTests(TestCase):
         """its possible to register and get annotators"""
         """its possible to register and get annotators"""
         providers = PermissionProviders()
         providers = PermissionProviders()
 
 
-        def test_annotator(*args):
+        def mock_annotator(*args):
             pass
             pass
 
 
-        providers.acl_annotator(TestType, test_annotator)
+        providers.acl_annotator(TestType, mock_annotator)
         annotators_list = providers.get_type_annotators(TestType())
         annotators_list = providers.get_type_annotators(TestType())
-        self.assertEqual(annotators_list[0], test_annotator)
+        self.assertEqual(annotators_list[0], mock_annotator)
 
 
     def test_serializers(self):
     def test_serializers(self):
         """its possible to register and get annotators"""
         """its possible to register and get annotators"""
         providers = PermissionProviders()
         providers = PermissionProviders()
 
 
-        def test_serializer(*args):
+        def mock_serializer(*args):
             pass
             pass
 
 
-        providers.acl_serializer(TestType, test_serializer)
+        providers.acl_serializer(TestType, mock_serializer)
         serializers_list = providers.get_type_serializers(TestType())
         serializers_list = providers.get_type_serializers(TestType())
-        self.assertEqual(serializers_list[0], test_serializer)
+        self.assertEqual(serializers_list[0], mock_serializer)

+ 5 - 6
misago/acl/version.py

@@ -1,16 +1,15 @@
-from misago.core import cachebuster as cb
+from misago.core import cachebuster
 
 
-
-ACL_CACHE_NAME = 'misago_acl'
+from .constants import ACL_CACHEBUSTER
 
 
 
 
 def get_version():
 def get_version():
-    return cb.get_version(ACL_CACHE_NAME)
+    return cachebuster.get_version(ACL_CACHEBUSTER)
 
 
 
 
 def is_valid(version):
 def is_valid(version):
-    return cb.is_valid(ACL_CACHE_NAME, version)
+    return cachebuster.is_valid(ACL_CACHEBUSTER, version)
 
 
 
 
 def invalidate():
 def invalidate():
-    cb.invalidate(ACL_CACHE_NAME)
+    cachebuster.invalidate(ACL_CACHEBUSTER)

+ 7 - 5
misago/admin/views/index.py

@@ -3,6 +3,7 @@ import json
 import requests
 import requests
 from requests.exceptions import RequestException
 from requests.exceptions import RequestException
 
 
+from django.contrib.auth import get_user_model
 from django.http import Http404, JsonResponse
 from django.http import Http404, JsonResponse
 from django.utils.six.moves import range
 from django.utils.six.moves import range
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
@@ -10,21 +11,22 @@ from django.utils.translation import ugettext as _
 from misago import __version__
 from misago import __version__
 from misago.core.cache import cache
 from misago.core.cache import cache
 from misago.threads.models import Post, Thread
 from misago.threads.models import Post, Thread
-from misago.users.models import ACTIVATION_REQUIRED_NONE, User
 
 
 from . import render
 from . import render
 
 
-
 VERSION_CHECK_CACHE_KEY = "misago_version_check"
 VERSION_CHECK_CACHE_KEY = "misago_version_check"
 
 
+UserModel = get_user_model()
+
 
 
 def admin_index(request):
 def admin_index(request):
-    inactive_users = {'requires_activation__gt': ACTIVATION_REQUIRED_NONE}
     db_stats = {
     db_stats = {
         'threads': Thread.objects.count(),
         'threads': Thread.objects.count(),
         'posts': Post.objects.count(),
         'posts': Post.objects.count(),
-        'users': User.objects.count(),
-        'inactive_users': User.objects.filter(**inactive_users).count()
+        'users': UserModel.objects.count(),
+        'inactive_users': UserModel.objects.exclude(
+            requires_activation=UserModel.ACTIVATION_NONE
+        ).count()
     }
     }
 
 
     return render(request, 'misago/admin/index.html', {
     return render(request, 'misago/admin/index.html', {

+ 3 - 0
misago/categories/__init__.py

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

+ 2 - 0
misago/categories/constants.py

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

+ 2 - 1
misago/categories/forms.py

@@ -9,7 +9,8 @@ from misago.core.forms import YesNoSwitch
 from misago.core.validators import validate_sluggable
 from misago.core.validators import validate_sluggable
 from misago.threads.threadtypes import trees_map
 from misago.threads.threadtypes import trees_map
 
 
-from .models import THREADS_ROOT_NAME, Category, CategoryRole
+from . import THREADS_ROOT_NAME
+from .models import Category, CategoryRole
 
 
 
 
 """
 """

+ 2 - 2
misago/categories/models.py

@@ -12,10 +12,10 @@ from misago.core.cache import cache
 from misago.core.utils import slugify
 from misago.core.utils import slugify
 from misago.threads.threadtypes import trees_map
 from misago.threads.threadtypes import trees_map
 
 
+from . import PRIVATE_THREADS_ROOT_NAME, THREADS_ROOT_NAME
+
 
 
 CACHE_NAME = 'misago_categories_tree'
 CACHE_NAME = 'misago_categories_tree'
-PRIVATE_THREADS_ROOT_NAME = 'private_threads'
-THREADS_ROOT_NAME = 'root_category'
 
 
 
 
 class CategoryManager(TreeManager):
 class CategoryManager(TreeManager):

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

@@ -4,7 +4,8 @@ from misago.core.testutils import MisagoTestCase
 from misago.threads import testutils
 from misago.threads import testutils
 from misago.threads.threadtypes import trees_map
 from misago.threads.threadtypes import trees_map
 
 
-from ..models import THREADS_ROOT_NAME, Category
+from misago.categories import THREADS_ROOT_NAME
+from misago.categories.models import Category
 
 
 
 
 class CategoryManagerTests(MisagoTestCase):
 class CategoryManagerTests(MisagoTestCase):

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

@@ -6,8 +6,9 @@ from misago.acl import version as acl_version
 from misago.admin.views import generic
 from misago.admin.views import generic
 from misago.threads.threadtypes import trees_map
 from misago.threads.threadtypes import trees_map
 
 
-from ..forms import CategoryFormFactory, DeleteFormFactory
-from ..models import THREADS_ROOT_NAME, Category, RoleCategoryACL
+from misago.categories import THREADS_ROOT_NAME
+from misago.categories.forms import CategoryFormFactory, DeleteFormFactory
+from misago.categories.models import Category, RoleCategoryACL
 
 
 
 
 class CategoryAdmin(generic.AdminBaseMixin):
 class CategoryAdmin(generic.AdminBaseMixin):

+ 1 - 1
misago/core/errorpages.py

@@ -24,7 +24,7 @@ def _error_page(request, code, message=None):
 
 
 def banned(request, ban):
 def banned(request, ban):
     request.frontend_context.update({
     request.frontend_context.update({
-        'BAN_MESSAGE': ban.get_serialized_message(),
+        'MESSAGE': ban.get_serialized_message(),
         'CURRENT_LINK': 'misago:error-banned'
         'CURRENT_LINK': 'misago:error-banned'
     })
     })
 
 

+ 9 - 9
misago/core/tests/test_apipatch.py

@@ -21,40 +21,40 @@ class ApiPatchTests(TestCase):
         """add method adds function to patch object"""
         """add method adds function to patch object"""
         patch = ApiPatch()
         patch = ApiPatch()
 
 
-        def test_function():
+        def mock_function():
             pass
             pass
-        patch.add('test-add', test_function)
+        patch.add('test-add', mock_function)
 
 
         self.assertEqual(len(patch._actions), 1)
         self.assertEqual(len(patch._actions), 1)
         self.assertEqual(patch._actions[0]['op'], 'add')
         self.assertEqual(patch._actions[0]['op'], 'add')
         self.assertEqual(patch._actions[0]['path'], 'test-add')
         self.assertEqual(patch._actions[0]['path'], 'test-add')
-        self.assertEqual(patch._actions[0]['handler'], test_function)
+        self.assertEqual(patch._actions[0]['handler'], mock_function)
 
 
     def test_remove(self):
     def test_remove(self):
         """remove method adds function to patch object"""
         """remove method adds function to patch object"""
         patch = ApiPatch()
         patch = ApiPatch()
 
 
-        def test_function():
+        def mock_function():
             pass
             pass
-        patch.remove('test-remove', test_function)
+        patch.remove('test-remove', mock_function)
 
 
         self.assertEqual(len(patch._actions), 1)
         self.assertEqual(len(patch._actions), 1)
         self.assertEqual(patch._actions[0]['op'], 'remove')
         self.assertEqual(patch._actions[0]['op'], 'remove')
         self.assertEqual(patch._actions[0]['path'], 'test-remove')
         self.assertEqual(patch._actions[0]['path'], 'test-remove')
-        self.assertEqual(patch._actions[0]['handler'], test_function)
+        self.assertEqual(patch._actions[0]['handler'], mock_function)
 
 
     def test_replace(self):
     def test_replace(self):
         """replace method adds function to patch object"""
         """replace method adds function to patch object"""
         patch = ApiPatch()
         patch = ApiPatch()
 
 
-        def test_function():
+        def mock_function():
             pass
             pass
-        patch.replace('test-replace', test_function)
+        patch.replace('test-replace', mock_function)
 
 
         self.assertEqual(len(patch._actions), 1)
         self.assertEqual(len(patch._actions), 1)
         self.assertEqual(patch._actions[0]['op'], 'replace')
         self.assertEqual(patch._actions[0]['op'], 'replace')
         self.assertEqual(patch._actions[0]['path'], 'test-replace')
         self.assertEqual(patch._actions[0]['path'], 'test-replace')
-        self.assertEqual(patch._actions[0]['handler'], test_function)
+        self.assertEqual(patch._actions[0]['handler'], mock_function)
 
 
     def test_validate_action(self):
     def test_validate_action(self):
         """validate_action method validates action dict"""
         """validate_action method validates action dict"""

+ 5 - 5
misago/faker/management/commands/createfakebans.py

@@ -9,7 +9,7 @@ from django.utils import timezone
 from django.utils.six.moves import range
 from django.utils.six.moves import range
 
 
 from misago.core.management.progressbar import show_progress
 from misago.core.management.progressbar import show_progress
-from misago.users.models import BAN_EMAIL, BAN_IP, BAN_USERNAME, Ban
+from misago.users.models import Ban
 
 
 
 
 def fake_username_ban(fake):
 def fake_username_ban(fake):
@@ -69,11 +69,11 @@ def fake_ip_ban(fake):
 
 
 
 
 def create_fake_test(fake, test_type):
 def create_fake_test(fake, test_type):
-    if test_type == BAN_USERNAME:
+    if test_type == Ban.USERNAME:
         return fake_username_ban(fake)
         return fake_username_ban(fake)
-    elif test_type == BAN_EMAIL:
+    elif test_type == Ban.EMAIL:
         return fake_email_ban(fake)
         return fake_email_ban(fake)
-    elif test_type == BAN_IP:
+    elif test_type == Ban.IP:
         return fake_ip_ban(fake)
         return fake_ip_ban(fake)
 
 
 
 
@@ -99,7 +99,7 @@ class Command(BaseCommand):
         created_count = 0
         created_count = 0
         show_progress(self, created_count, fake_bans_to_create)
         show_progress(self, created_count, fake_bans_to_create)
         for i in range(fake_bans_to_create):
         for i in range(fake_bans_to_create):
-            ban = Ban(check_type=random.randint(BAN_USERNAME, BAN_IP))
+            ban = Ban(check_type=random.randint(Ban.USERNAME, Ban.IP))
             ban.banned_value = create_fake_test(fake, ban.check_type)
             ban.banned_value = create_fake_test(fake, ban.check_type)
 
 
             if random.randint(0, 10) == 0:
             if random.randint(0, 10) == 0:

+ 1 - 1
misago/readtracker/signals.py

@@ -1,6 +1,6 @@
 from django.dispatch import Signal, receiver
 from django.dispatch import Signal, receiver
 
 
-from misago.categories.models import PRIVATE_THREADS_ROOT_NAME
+from misago.categories import PRIVATE_THREADS_ROOT_NAME
 from misago.categories.signals import delete_category_content, move_category_content
 from misago.categories.signals import delete_category_content, move_category_content
 from misago.threads.signals import move_thread
 from misago.threads.signals import move_thread
 
 

+ 2 - 1
misago/threads/api/postingendpoint/category.py

@@ -5,7 +5,8 @@ from django.utils.translation import ugettext_lazy
 from rest_framework import serializers
 from rest_framework import serializers
 
 
 from misago.acl import add_acl
 from misago.acl import add_acl
-from misago.categories.models import THREADS_ROOT_NAME, Category
+from misago.categories import THREADS_ROOT_NAME
+from misago.categories.models import Category
 from misago.categories.permissions import can_browse_category, can_see_category
 from misago.categories.permissions import can_browse_category, can_see_category
 
 
 from . import PostingEndpoint, PostingMiddleware
 from . import PostingEndpoint, PostingMiddleware

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

@@ -6,7 +6,7 @@ from django.utils.translation import ungettext
 
 
 from rest_framework import serializers
 from rest_framework import serializers
 
 
-from misago.categories.models import PRIVATE_THREADS_ROOT_NAME
+from misago.categories import PRIVATE_THREADS_ROOT_NAME
 
 
 from . import PostingEndpoint, PostingMiddleware
 from . import PostingEndpoint, PostingMiddleware
 from ...participants import add_participants, set_owner
 from ...participants import add_participants, set_owner

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

@@ -1,5 +1,6 @@
 from misago.acl import add_acl
 from misago.acl import add_acl
-from misago.categories.models import PRIVATE_THREADS_ROOT_NAME, Category
+from misago.categories import PRIVATE_THREADS_ROOT_NAME
+from misago.categories.models import Category
 
 
 from . import PostingEndpoint, PostingMiddleware
 from . import PostingEndpoint, PostingMiddleware
 
 

+ 8 - 5
misago/threads/api/postingendpoint/subscribe.py

@@ -1,9 +1,12 @@
-from misago.users.models import AUTO_SUBSCRIBE_NONE, AUTO_SUBSCRIBE_NOTIFY, AUTO_SUBSCRIBE_NOTIFY_AND_EMAIL
+from django.contrib.auth import get_user_model
 
 
 from . import PostingEndpoint, PostingMiddleware
 from . import PostingEndpoint, PostingMiddleware
 from ...models import Subscription
 from ...models import Subscription
 
 
 
 
+UserModel = get_user_model()
+
+
 class SubscribeMiddleware(PostingMiddleware):
 class SubscribeMiddleware(PostingMiddleware):
     def use_this_middleware(self):
     def use_this_middleware(self):
         return self.mode != PostingEndpoint.EDIT
         return self.mode != PostingEndpoint.EDIT
@@ -16,20 +19,20 @@ class SubscribeMiddleware(PostingMiddleware):
         if self.mode != PostingEndpoint.START:
         if self.mode != PostingEndpoint.START:
             return
             return
 
 
-        if self.user.subscribe_to_started_threads == AUTO_SUBSCRIBE_NONE:
+        if self.user.subscribe_to_started_threads == UserModel.SUBSCRIBE_NONE:
             return
             return
 
 
         self.user.subscription_set.create(
         self.user.subscription_set.create(
             category=self.thread.category,
             category=self.thread.category,
             thread=self.thread,
             thread=self.thread,
-            send_email=self.user.subscribe_to_started_threads == AUTO_SUBSCRIBE_NOTIFY_AND_EMAIL
+            send_email=self.user.subscribe_to_started_threads == UserModel.SUBSCRIBE_ALL
         )
         )
 
 
     def subscribe_replied_thread(self):
     def subscribe_replied_thread(self):
         if self.mode != PostingEndpoint.REPLY:
         if self.mode != PostingEndpoint.REPLY:
             return
             return
 
 
-        if self.user.subscribe_to_replied_threads == AUTO_SUBSCRIBE_NONE:
+        if self.user.subscribe_to_replied_threads == UserModel.SUBSCRIBE_NONE:
             return
             return
 
 
         try:
         try:
@@ -45,5 +48,5 @@ class SubscribeMiddleware(PostingMiddleware):
         self.user.subscription_set.create(
         self.user.subscription_set.create(
             category=self.thread.category,
             category=self.thread.category,
             thread=self.thread,
             thread=self.thread,
-            send_email=self.user.subscribe_to_replied_threads == AUTO_SUBSCRIBE_NOTIFY_AND_EMAIL
+            send_email=self.user.subscribe_to_replied_threads == UserModel.SUBSCRIBE_ALL
         )
         )

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

@@ -1,4 +1,4 @@
-from misago.categories.models import PRIVATE_THREADS_ROOT_NAME
+from misago.categories import PRIVATE_THREADS_ROOT_NAME
 
 
 from ...participants import set_users_unread_private_threads_sync
 from ...participants import set_users_unread_private_threads_sync
 from . import PostingEndpoint, PostingMiddleware
 from . import PostingEndpoint, PostingMiddleware

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

@@ -1,6 +1,6 @@
 from django.db.models import F
 from django.db.models import F
 
 
-from misago.categories.models import THREADS_ROOT_NAME
+from misago.categories import THREADS_ROOT_NAME
 
 
 from . import PostingEndpoint, PostingMiddleware
 from . import PostingEndpoint, PostingMiddleware
 
 

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

@@ -4,7 +4,8 @@ from django.utils.translation import gettext as _
 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 THREADS_ROOT_NAME, Category
+from misago.categories import THREADS_ROOT_NAME
+from misago.categories.models import Category
 
 
 from ...permissions.threads import can_start_thread
 from ...permissions.threads import can_start_thread
 from ...threadtypes import trees_map
 from ...threadtypes import trees_map

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

@@ -6,7 +6,8 @@ from django.utils.translation import ungettext
 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 THREADS_ROOT_NAME, Category
+from misago.categories import THREADS_ROOT_NAME
+from misago.categories.models import Category
 
 
 from ...events import record_event
 from ...events import record_event
 from ...models import THREAD_WEIGHT_GLOBAL, Thread
 from ...models import THREAD_WEIGHT_GLOBAL, Thread

+ 2 - 1
misago/threads/api/threadendpoints/read.py

@@ -1,4 +1,5 @@
-from misago.categories.models import THREADS_ROOT_NAME, Category
+from misago.categories import THREADS_ROOT_NAME
+from misago.categories.models import Category
 from misago.categories.permissions import allow_browse_category, allow_see_category
 from misago.categories.permissions import allow_browse_category, allow_see_category
 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.readtracker.categoriestracker import read_category
 from misago.readtracker.categoriestracker import read_category

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

@@ -6,7 +6,7 @@ from rest_framework import viewsets
 from rest_framework.decorators import detail_route, list_route
 from rest_framework.decorators import detail_route, list_route
 from rest_framework.response import Response
 from rest_framework.response import Response
 
 
-from misago.categories.models import PRIVATE_THREADS_ROOT_NAME, THREADS_ROOT_NAME
+from misago.categories import PRIVATE_THREADS_ROOT_NAME, THREADS_ROOT_NAME
 from misago.core.shortcuts import get_int_or_404
 from misago.core.shortcuts import get_int_or_404
 
 
 from ..models import Post, Thread
 from ..models import Post, Thread

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

@@ -8,30 +8,13 @@ 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 PRIVATE_THREADS_ROOT_NAME, Category
+from misago.categories import PRIVATE_THREADS_ROOT_NAME
+from misago.categories.models import Category
 from misago.core.forms import YesNoSwitch
 from misago.core.forms import YesNoSwitch
 
 
 from ..models import Thread
 from ..models import Thread
 
 
 
 
-__all__ = [
-    'allow_use_private_threads',
-    'can_use_private_threads',
-    'allow_see_private_thread',
-    'can_see_private_thread',
-    'allow_change_owner',
-    'can_change_owner',
-    'allow_add_participants',
-    'can_add_participants',
-    'allow_remove_participant',
-    'can_remove_participant',
-    'allow_add_participant',
-    'can_add_participant',
-    'allow_message_user',
-    'can_message_user',
-]
-
-
 """
 """
 Admin Permissions Form
 Admin Permissions Form
 """
 """

+ 8 - 3
misago/threads/tests/test_privatethread_start_api.py

@@ -10,7 +10,6 @@ from django.utils.encoding import smart_str
 
 
 from misago.acl.testutils import override_acl
 from misago.acl.testutils import override_acl
 from misago.categories.models import Category
 from misago.categories.models import Category
-from misago.users.models import LIMITS_PRIVATE_THREAD_INVITES_TO_FOLLOWED, LIMITS_PRIVATE_THREAD_INVITES_TO_NOBODY
 from misago.users.testutils import AuthenticatedUserTestCase
 from misago.users.testutils import AuthenticatedUserTestCase
 
 
 from ..models import Thread, ThreadParticipant
 from ..models import Thread, ThreadParticipant
@@ -194,7 +193,10 @@ class StartPrivateThreadTests(AuthenticatedUserTestCase):
 
 
     def test_cant_invite_followers_only(self):
     def test_cant_invite_followers_only(self):
         """api validates that you cant invite followers-only user to thread"""
         """api validates that you cant invite followers-only user to thread"""
-        self.other_user.limits_private_thread_invites_to = LIMITS_PRIVATE_THREAD_INVITES_TO_FOLLOWED
+        User = get_user_model()
+
+        user_constant = User.LIMIT_INVITES_TO_FOLLOWED
+        self.other_user.limits_private_thread_invites_to = user_constant
         self.other_user.save()
         self.other_user.save()
 
 
         response = self.client.post(self.api_link, data={
         response = self.client.post(self.api_link, data={
@@ -245,7 +247,10 @@ class StartPrivateThreadTests(AuthenticatedUserTestCase):
 
 
     def test_cant_invite_anyone(self):
     def test_cant_invite_anyone(self):
         """api validates that you cant invite nobody user to thread"""
         """api validates that you cant invite nobody user to thread"""
-        self.other_user.limits_private_thread_invites_to = LIMITS_PRIVATE_THREAD_INVITES_TO_NOBODY
+        User = get_user_model()
+
+        user_constant = User.LIMIT_INVITES_TO_NOBODY
+        self.other_user.limits_private_thread_invites_to = user_constant
         self.other_user.save()
         self.other_user.save()
 
 
         response = self.client.post(self.api_link, data={
         response = self.client.post(self.api_link, data={

+ 13 - 10
misago/threads/tests/test_subscription_middleware.py

@@ -1,16 +1,19 @@
 # -*- coding: utf-8 -*-
 # -*- coding: utf-8 -*-
 from __future__ import unicode_literals
 from __future__ import unicode_literals
 
 
+from django.contrib.auth import get_user_model
 from django.urls import reverse
 from django.urls import reverse
 
 
 from misago.acl.testutils import override_acl
 from misago.acl.testutils import override_acl
 from misago.categories.models import Category
 from misago.categories.models import Category
-from misago.users.models import AUTO_SUBSCRIBE_NONE, AUTO_SUBSCRIBE_NOTIFY, AUTO_SUBSCRIBE_NOTIFY_AND_EMAIL
 from misago.users.testutils import AuthenticatedUserTestCase
 from misago.users.testutils import AuthenticatedUserTestCase
 
 
 from .. import testutils
 from .. import testutils
 
 
 
 
+UserModel = get_user_model()
+
+
 class SubscriptionMiddlewareTestCase(AuthenticatedUserTestCase):
 class SubscriptionMiddlewareTestCase(AuthenticatedUserTestCase):
     def setUp(self):
     def setUp(self):
         super(SubscriptionMiddlewareTestCase, self).setUp()
         super(SubscriptionMiddlewareTestCase, self).setUp()
@@ -37,8 +40,8 @@ class SubscribeStartedThreadTests(SubscriptionMiddlewareTestCase):
 
 
     def test_dont_subscribe(self):
     def test_dont_subscribe(self):
         """middleware makes no subscription to thread"""
         """middleware makes no subscription to thread"""
-        self.user.subscribe_to_started_threads = AUTO_SUBSCRIBE_NONE
-        self.user.subscribe_to_replied_threads = AUTO_SUBSCRIBE_NOTIFY
+        self.user.subscribe_to_started_threads = UserModel.SUBSCRIBE_NONE
+        self.user.subscribe_to_replied_threads = UserModel.SUBSCRIBE_NOTIFY
         self.user.save()
         self.user.save()
 
 
         response = self.client.post(self.api_link, data={
         response = self.client.post(self.api_link, data={
@@ -53,7 +56,7 @@ class SubscribeStartedThreadTests(SubscriptionMiddlewareTestCase):
 
 
     def test_subscribe(self):
     def test_subscribe(self):
         """middleware subscribes thread"""
         """middleware subscribes thread"""
-        self.user.subscribe_to_started_threads = AUTO_SUBSCRIBE_NOTIFY
+        self.user.subscribe_to_started_threads = UserModel.SUBSCRIBE_NOTIFY
         self.user.save()
         self.user.save()
 
 
         response = self.client.post(self.api_link, data={
         response = self.client.post(self.api_link, data={
@@ -72,7 +75,7 @@ class SubscribeStartedThreadTests(SubscriptionMiddlewareTestCase):
 
 
     def test_email_subscribe(self):
     def test_email_subscribe(self):
         """middleware subscribes thread with an email"""
         """middleware subscribes thread with an email"""
-        self.user.subscribe_to_started_threads = AUTO_SUBSCRIBE_NOTIFY_AND_EMAIL
+        self.user.subscribe_to_started_threads = UserModel.SUBSCRIBE_ALL
         self.user.save()
         self.user.save()
 
 
         response = self.client.post(self.api_link, data={
         response = self.client.post(self.api_link, data={
@@ -100,8 +103,8 @@ class SubscribeRepliedThreadTests(SubscriptionMiddlewareTestCase):
 
 
     def test_dont_subscribe(self):
     def test_dont_subscribe(self):
         """middleware makes no subscription to thread"""
         """middleware makes no subscription to thread"""
-        self.user.subscribe_to_started_threads = AUTO_SUBSCRIBE_NOTIFY
-        self.user.subscribe_to_replied_threads = AUTO_SUBSCRIBE_NONE
+        self.user.subscribe_to_started_threads = UserModel.SUBSCRIBE_NOTIFY
+        self.user.subscribe_to_replied_threads = UserModel.SUBSCRIBE_NONE
         self.user.save()
         self.user.save()
 
 
         response = self.client.post(self.api_link, data={
         response = self.client.post(self.api_link, data={
@@ -114,7 +117,7 @@ class SubscribeRepliedThreadTests(SubscriptionMiddlewareTestCase):
 
 
     def test_subscribe(self):
     def test_subscribe(self):
         """middleware subscribes thread"""
         """middleware subscribes thread"""
-        self.user.subscribe_to_replied_threads = AUTO_SUBSCRIBE_NOTIFY
+        self.user.subscribe_to_replied_threads = UserModel.SUBSCRIBE_NOTIFY
         self.user.save()
         self.user.save()
 
 
         response = self.client.post(self.api_link, data={
         response = self.client.post(self.api_link, data={
@@ -130,7 +133,7 @@ class SubscribeRepliedThreadTests(SubscriptionMiddlewareTestCase):
 
 
     def test_email_subscribe(self):
     def test_email_subscribe(self):
         """middleware subscribes thread with an email"""
         """middleware subscribes thread with an email"""
-        self.user.subscribe_to_replied_threads = AUTO_SUBSCRIBE_NOTIFY_AND_EMAIL
+        self.user.subscribe_to_replied_threads = UserModel.SUBSCRIBE_ALL
         self.user.save()
         self.user.save()
 
 
         response = self.client.post(self.api_link, data={
         response = self.client.post(self.api_link, data={
@@ -146,7 +149,7 @@ class SubscribeRepliedThreadTests(SubscriptionMiddlewareTestCase):
 
 
     def test_dont_subscribe_replied(self):
     def test_dont_subscribe_replied(self):
         """middleware omits threads user already replied"""
         """middleware omits threads user already replied"""
-        self.user.subscribe_to_replied_threads = AUTO_SUBSCRIBE_NOTIFY_AND_EMAIL
+        self.user.subscribe_to_replied_threads = UserModel.SUBSCRIBE_ALL
         self.user.save()
         self.user.save()
 
 
         response = self.client.post(self.api_link, data={
         response = self.client.post(self.api_link, data={

+ 5 - 2
misago/threads/tests/test_subscriptions.py

@@ -12,13 +12,16 @@ from .. import testutils
 from ..subscriptions import make_subscription_aware
 from ..subscriptions import make_subscription_aware
 
 
 
 
+UserModel = get_user_model()
+
+
 class SubscriptionsTests(TestCase):
 class SubscriptionsTests(TestCase):
     def setUp(self):
     def setUp(self):
         self.category = list(Category.objects.all_categories()[:1])[0]
         self.category = list(Category.objects.all_categories()[:1])[0]
         self.thread = self.post_thread(timezone.now() - timedelta(days=10))
         self.thread = self.post_thread(timezone.now() - timedelta(days=10))
 
 
-        User = get_user_model()
-        self.user = User.objects.create_user("Bob", "bob@test.com", "Pass.123")
+        self.user = UserModel.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):

+ 2 - 1
misago/threads/tests/test_thread_start_api.py

@@ -4,7 +4,8 @@ from __future__ import unicode_literals
 from django.urls import reverse
 from django.urls import reverse
 
 
 from misago.acl.testutils import override_acl
 from misago.acl.testutils import override_acl
-from misago.categories.models import THREADS_ROOT_NAME, Category
+from misago.categories import THREADS_ROOT_NAME
+from misago.categories.models import Category
 from misago.users.testutils import AuthenticatedUserTestCase
 from misago.users.testutils import AuthenticatedUserTestCase
 
 
 from ..models import Thread
 from ..models import Thread

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

@@ -1,7 +1,8 @@
 from django.urls import reverse
 from django.urls import reverse
 
 
 from misago.acl.testutils import override_acl
 from misago.acl.testutils import override_acl
-from misago.categories.models import THREADS_ROOT_NAME, Category
+from misago.categories import THREADS_ROOT_NAME
+from misago.categories.models import Category
 from misago.users.testutils import AuthenticatedUserTestCase
 from misago.users.testutils import AuthenticatedUserTestCase
 
 
 from .. import testutils
 from .. import testutils

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

@@ -1,7 +1,7 @@
 from django.urls import reverse
 from django.urls import reverse
 from django.utils.translation import ugettext_lazy as _
 from django.utils.translation import ugettext_lazy as _
 
 
-from misago.categories.models import PRIVATE_THREADS_ROOT_NAME
+from misago.categories import PRIVATE_THREADS_ROOT_NAME
 
 
 from . import ThreadType
 from . import ThreadType
 
 

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

@@ -1,7 +1,7 @@
 from django.urls import reverse
 from django.urls import reverse
 from django.utils.translation import ugettext_lazy as _
 from django.utils.translation import ugettext_lazy as _
 
 
-from misago.categories.models import THREADS_ROOT_NAME
+from misago.categories import THREADS_ROOT_NAME
 
 
 from . import ThreadType
 from . import ThreadType
 
 

+ 2 - 1
misago/threads/validators.py

@@ -2,7 +2,8 @@ from django.core.exceptions import ValidationError
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
 from django.utils.translation import ungettext
 from django.utils.translation import ungettext
 
 
-from misago.categories.models import THREADS_ROOT_NAME, Category
+from misago.categories import THREADS_ROOT_NAME
+from misago.categories.models import Category
 from misago.categories.permissions import can_browse_category, can_see_category
 from misago.categories.permissions import can_browse_category, can_see_category
 from misago.conf import settings
 from misago.conf import settings
 from misago.core.validators import validate_sluggable
 from misago.core.validators import validate_sluggable

+ 2 - 1
misago/threads/viewmodels/thread.py

@@ -2,7 +2,8 @@ from django.shortcuts import get_object_or_404
 from django.utils.translation import gettext as _
 from django.utils.translation import gettext as _
 
 
 from misago.acl import add_acl
 from misago.acl import add_acl
-from misago.categories.models import PRIVATE_THREADS_ROOT_NAME, THREADS_ROOT_NAME, Category
+from misago.categories import PRIVATE_THREADS_ROOT_NAME, THREADS_ROOT_NAME
+from misago.categories.models import Category
 from misago.core.shortcuts import validate_slug
 from misago.core.shortcuts import validate_slug
 from misago.core.viewmodel import ViewModel as BaseViewModel
 from misago.core.viewmodel import ViewModel as BaseViewModel
 from misago.readtracker.threadstracker import make_read_aware
 from misago.readtracker.threadstracker import make_read_aware

+ 2 - 2
misago/users/api/rest_permissions.py

@@ -6,7 +6,7 @@ from rest_framework.permissions import BasePermission
 from misago.core.exceptions import Banned
 from misago.core.exceptions import Banned
 
 
 from ..bans import get_request_ip_ban
 from ..bans import get_request_ip_ban
-from ..models import BAN_IP, Ban
+from ..models import Ban
 
 
 
 
 __all__ = [
 __all__ = [
@@ -20,7 +20,7 @@ class UnbannedOnly(BasePermission):
         ban = get_request_ip_ban(request)
         ban = get_request_ip_ban(request)
         if ban:
         if ban:
             hydrated_ban = Ban(
             hydrated_ban = Ban(
-                check_type=BAN_IP,
+                check_type=Ban.IP,
                 user_message=ban['message'],
                 user_message=ban['message'],
                 expires_on=ban['expires_on'])
                 expires_on=ban['expires_on'])
             raise Banned(hydrated_ban)
             raise Banned(hydrated_ban)

+ 6 - 5
misago/users/api/userendpoints/create.py

@@ -11,11 +11,13 @@ from misago.core.mail import mail_user
 
 
 from ... import captcha
 from ... import captcha
 from ...forms.register import RegisterForm
 from ...forms.register import RegisterForm
-from ...models import ACTIVATION_REQUIRED_ADMIN, ACTIVATION_REQUIRED_USER
 from ...serializers import AuthenticatedUserSerializer
 from ...serializers import AuthenticatedUserSerializer
 from ...tokens import make_activation_token
 from ...tokens import make_activation_token
 
 
 
 
+UserModel = get_user_model()
+
+
 @csrf_protect
 @csrf_protect
 def create_endpoint(request):
 def create_endpoint(request):
     if settings.account_activation == 'closed':
     if settings.account_activation == 'closed':
@@ -34,15 +36,14 @@ def create_endpoint(request):
     activation_kwargs = {}
     activation_kwargs = {}
     if settings.account_activation == 'user':
     if settings.account_activation == 'user':
         activation_kwargs = {
         activation_kwargs = {
-            'requires_activation': ACTIVATION_REQUIRED_USER
+            'requires_activation': UserModel.ACTIVATION_USER
         }
         }
     elif settings.account_activation == 'admin':
     elif settings.account_activation == 'admin':
         activation_kwargs = {
         activation_kwargs = {
-            'requires_activation': ACTIVATION_REQUIRED_ADMIN
+            'requires_activation': UserModel.ACTIVATION_ADMIN
         }
         }
 
 
-    User = get_user_model()
-    new_user = User.objects.create_user(
+    new_user = UserModel.objects.create_user(
         form.cleaned_data['username'],
         form.cleaned_data['username'],
         form.cleaned_data['email'],
         form.cleaned_data['email'],
         form.cleaned_data['password'],
         form.cleaned_data['password'],

+ 12 - 12
misago/users/bans.py

@@ -12,11 +12,11 @@ from django.utils.dateparse import parse_datetime
 
 
 from misago.core import cachebuster
 from misago.core import cachebuster
 
 
-from .models import BAN_IP, Ban, BanCache
+from .models import Ban, BanCache
 
 
 
 
-BAN_CACHE_SESSION_KEY = 'misago_ip_check'
-BAN_VERSION_KEY = 'misago_bans'
+CACHE_SESSION_KEY = 'misago_ip_check'
+VERSION_KEY = 'misago_bans'
 
 
 
 
 def get_username_ban(username):
 def get_username_ban(username):
@@ -64,7 +64,7 @@ def get_user_ban(user):
 
 
 def _set_user_ban_cache(user):
 def _set_user_ban_cache(user):
     ban_cache = user.ban_cache
     ban_cache = user.ban_cache
-    ban_cache.bans_version = cachebuster.get_version(BAN_VERSION_KEY)
+    ban_cache.bans_version = cachebuster.get_version(VERSION_KEY)
 
 
     try:
     try:
         user_ban = Ban.objects.get_ban(
         user_ban = Ban.objects.get_ban(
@@ -102,8 +102,8 @@ def get_request_ip_ban(request):
 
 
     found_ban = get_ip_ban(request.user_ip)
     found_ban = get_ip_ban(request.user_ip)
 
 
-    ban_cache = request.session[BAN_CACHE_SESSION_KEY] = {
-        'version': cachebuster.get_version(BAN_VERSION_KEY),
+    ban_cache = request.session[CACHE_SESSION_KEY] = {
+        'version': cachebuster.get_version(VERSION_KEY),
         'ip': request.user_ip,
         'ip': request.user_ip,
     }
     }
 
 
@@ -117,21 +117,21 @@ def get_request_ip_ban(request):
                 'is_banned': True,
                 'is_banned': True,
                 'message': found_ban.user_message
                 'message': found_ban.user_message
             })
             })
-        request.session[BAN_CACHE_SESSION_KEY] = ban_cache
-        return _hydrate_session_cache(request.session[BAN_CACHE_SESSION_KEY])
+        request.session[CACHE_SESSION_KEY] = ban_cache
+        return _hydrate_session_cache(request.session[CACHE_SESSION_KEY])
     else:
     else:
         ban_cache['is_banned'] = False
         ban_cache['is_banned'] = False
-        request.session[BAN_CACHE_SESSION_KEY] = ban_cache
+        request.session[CACHE_SESSION_KEY] = ban_cache
         return None
         return None
 
 
 
 
 def _get_session_bancache(request):
 def _get_session_bancache(request):
     try:
     try:
-        ban_cache = request.session[BAN_CACHE_SESSION_KEY]
+        ban_cache = request.session[CACHE_SESSION_KEY]
         ban_cache = _hydrate_session_cache(ban_cache)
         ban_cache = _hydrate_session_cache(ban_cache)
         if ban_cache['ip'] != request.user_ip:
         if ban_cache['ip'] != request.user_ip:
             return None
             return None
-        if not cachebuster.is_valid(BAN_VERSION_KEY, ban_cache['version']):
+        if not cachebuster.is_valid(VERSION_KEY, ban_cache['version']):
             return None
             return None
         if ban_cache.get('expires_on'):
         if ban_cache.get('expires_on'):
             """
             """
@@ -177,7 +177,7 @@ def ban_ip(ip, user_message=None, staff_message=None, length=None,
         expires_on = timezone.now() + timedelta(**length)
         expires_on = timezone.now() + timedelta(**length)
 
 
     ban = Ban.objects.create(
     ban = Ban.objects.create(
-        check_type=BAN_IP,
+        check_type=Ban.IP,
         banned_value=ip,
         banned_value=ip,
         user_message=user_message,
         user_message=user_message,
         staff_message=staff_message,
         staff_message=staff_message,

+ 1 - 0
misago/users/constants.py

@@ -0,0 +1 @@
+BANS_CACHEBUSTER = 'misago_bans'

+ 4 - 3
misago/users/decorators.py

@@ -5,7 +5,7 @@ from django.utils.translation import gettext as _
 from misago.core.exceptions import Banned
 from misago.core.exceptions import Banned
 
 
 from .bans import get_request_ip_ban
 from .bans import get_request_ip_ban
-from .models import BAN_IP, Ban
+from .models import Ban
 
 
 
 
 def deny_authenticated(f):
 def deny_authenticated(f):
@@ -33,9 +33,10 @@ def deny_banned_ips(f):
         ban = get_request_ip_ban(request)
         ban = get_request_ip_ban(request)
         if ban:
         if ban:
             hydrated_ban = Ban(
             hydrated_ban = Ban(
-                check_type=BAN_IP,
+                check_type=Ban.IP,
                 user_message=ban['message'],
                 user_message=ban['message'],
-                expires_on=ban['expires_on'])
+                expires_on=ban['expires_on']
+            )
             raise Banned(hydrated_ban)
             raise Banned(hydrated_ban)
         else:
         else:
             return f(request, *args, **kwargs)
             return f(request, *args, **kwargs)

+ 19 - 20
misago/users/forms/admin.py

@@ -10,13 +10,13 @@ from misago.core import threadstore
 from misago.core.forms import IsoDateTimeField, YesNoSwitch
 from misago.core.forms import IsoDateTimeField, YesNoSwitch
 from misago.core.validators import validate_sluggable
 from misago.core.validators import validate_sluggable
 
 
-from ..models import (
-    AUTO_SUBSCRIBE_CHOICES, BANS_CHOICES, PRIVATE_THREAD_INVITES_LIMITS_CHOICES,
-    Ban, Rank
-)
+from ..models import Ban, Rank
 from ..validators import validate_email, validate_username
 from ..validators import validate_email, validate_username
 
 
 
 
+UserModel = get_user_model()
+
+
 """
 """
 Users
 Users
 """
 """
@@ -26,7 +26,7 @@ class UserBaseForm(forms.ModelForm):
     email = forms.EmailField(label=_("E-mail address"))
     email = forms.EmailField(label=_("E-mail address"))
 
 
     class Meta:
     class Meta:
-        model = get_user_model()
+        model = UserModel
         fields = ['username', 'email', 'title']
         fields = ['username', 'email', 'title']
 
 
     def clean_username(self):
     def clean_username(self):
@@ -65,7 +65,7 @@ class NewUserForm(UserBaseForm):
     )
     )
 
 
     class Meta:
     class Meta:
-        model = get_user_model()
+        model = UserModel
         fields = ['username', 'email', 'title']
         fields = ['username', 'email', 'title']
 
 
 
 
@@ -164,22 +164,22 @@ class EditUserForm(UserBaseForm):
     limits_private_thread_invites_to = forms.TypedChoiceField(
     limits_private_thread_invites_to = forms.TypedChoiceField(
         label=_("Who can add user to private threads"),
         label=_("Who can add user to private threads"),
         coerce=int,
         coerce=int,
-        choices=PRIVATE_THREAD_INVITES_LIMITS_CHOICES
+        choices=UserModel.LIMIT_INVITES_TO_CHOICES
     )
     )
 
 
     subscribe_to_started_threads = forms.TypedChoiceField(
     subscribe_to_started_threads = forms.TypedChoiceField(
         label=_("Started threads"),
         label=_("Started threads"),
         coerce=int,
         coerce=int,
-        choices=AUTO_SUBSCRIBE_CHOICES
+        choices=UserModel.SUBSCRIBE_CHOICES
     )
     )
     subscribe_to_replied_threads = forms.TypedChoiceField(
     subscribe_to_replied_threads = forms.TypedChoiceField(
         label=_("Replid threads"),
         label=_("Replid threads"),
         coerce=int,
         coerce=int,
-        choices=AUTO_SUBSCRIBE_CHOICES
+        choices=UserModel.SUBSCRIBE_CHOICES
     )
     )
 
 
     class Meta:
     class Meta:
-        model = get_user_model()
+        model = UserModel
         fields = [
         fields = [
             'username',
             'username',
             'email',
             'email',
@@ -489,7 +489,7 @@ class BanForm(forms.ModelForm):
     check_type = forms.TypedChoiceField(
     check_type = forms.TypedChoiceField(
         label=_("Check type"),
         label=_("Check type"),
         coerce=int,
         coerce=int,
-        choices=BANS_CHOICES
+        choices=Ban.CHOICES
     )
     )
     banned_value = forms.CharField(
     banned_value = forms.CharField(
         label=_("Banned value"),
         label=_("Banned value"),
@@ -551,19 +551,18 @@ class BanForm(forms.ModelForm):
         return data
         return data
 
 
 
 
-SARCH_BANS_CHOICES = (
-    ('', _('All bans')),
-    ('names', _('Usernames')),
-    ('emails', _('E-mails')),
-    ('ips', _('IPs')),
-)
-
-
 class SearchBansForm(forms.Form):
 class SearchBansForm(forms.Form):
+    SARCH_CHOICES = (
+        ('', _('All bans')),
+        ('names', _('Usernames')),
+        ('emails', _('E-mails')),
+        ('ips', _('IPs')),
+    )
+
     check_type = forms.ChoiceField(
     check_type = forms.ChoiceField(
         label=_("Type"),
         label=_("Type"),
         required=False,
         required=False,
-        choices=SARCH_BANS_CHOICES
+        choices=SARCH_CHOICES
     )
     )
     value = forms.CharField(
     value = forms.CharField(
         label=_("Banned value begins with"),
         label=_("Banned value begins with"),

+ 14 - 8
misago/users/forms/options.py

@@ -7,24 +7,30 @@ from django.utils.translation import ungettext
 from misago.conf import settings
 from misago.conf import settings
 from misago.core.forms import YesNoSwitch
 from misago.core.forms import YesNoSwitch
 
 
-from ..models import AUTO_SUBSCRIBE_CHOICES, PRIVATE_THREAD_INVITES_LIMITS_CHOICES
 from ..validators import validate_email
 from ..validators import validate_email
 
 
 
 
+UserModel = get_user_model()
+
+
 class ForumOptionsForm(forms.ModelForm):
 class ForumOptionsForm(forms.ModelForm):
     is_hiding_presence = YesNoSwitch()
     is_hiding_presence = YesNoSwitch()
 
 
     limits_private_thread_invites_to = forms.TypedChoiceField(
     limits_private_thread_invites_to = forms.TypedChoiceField(
-        coerce=int, choices=PRIVATE_THREAD_INVITES_LIMITS_CHOICES)
-
+        choices=UserModel.LIMIT_INVITES_TO_CHOICES,
+        coerce=int,
+    )
     subscribe_to_started_threads = forms.TypedChoiceField(
     subscribe_to_started_threads = forms.TypedChoiceField(
-        coerce=int, choices=AUTO_SUBSCRIBE_CHOICES)
-
+        choices=UserModel.SUBSCRIBE_CHOICES,
+        coerce=int,
+    )
     subscribe_to_replied_threads = forms.TypedChoiceField(
     subscribe_to_replied_threads = forms.TypedChoiceField(
-        coerce=int, choices=AUTO_SUBSCRIBE_CHOICES)
+        choices=UserModel.SUBSCRIBE_CHOICES,
+        coerce=int,
+    )
 
 
     class Meta:
     class Meta:
-        model = get_user_model()
+        model = UserModel
         fields = [
         fields = [
             'is_hiding_presence',
             'is_hiding_presence',
             'limits_private_thread_invites_to',
             'limits_private_thread_invites_to',
@@ -37,7 +43,7 @@ class EditSignatureForm(forms.ModelForm):
     signature = forms.CharField(required=False)
     signature = forms.CharField(required=False)
 
 
     class Meta:
     class Meta:
-        model = get_user_model()
+        model = UserModel
         fields = ['signature']
         fields = ['signature']
 
 
     def clean(self):
     def clean(self):

+ 3 - 1
misago/users/migrations/0003_bans_version_tracker.py

@@ -5,9 +5,11 @@ from django.db import migrations, models
 
 
 from misago.core.migrationutils import cachebuster_register_cache
 from misago.core.migrationutils import cachebuster_register_cache
 
 
+from misago.users.constants import BANS_CACHEBUSTER
+
 
 
 def register_bans_version_tracker(apps, schema_editor):
 def register_bans_version_tracker(apps, schema_editor):
-    cachebuster_register_cache(apps, 'misago_bans')
+    cachebuster_register_cache(apps, BANS_CACHEBUSTER)
 
 
 
 
 class Migration(migrations.Migration):
 class Migration(migrations.Migration):

+ 22 - 32
misago/users/models/ban.py

@@ -7,26 +7,7 @@ from django.utils.translation import ugettext_lazy as _
 
 
 from misago.core import cachebuster
 from misago.core import cachebuster
 
 
-
-__all__ = [
-    'BAN_USERNAME', 'BAN_EMAIL', 'BAN_IP', 'BANS_CHOICES',
-    'Ban', 'BanCache'
-]
-
-
-BAN_CACHEBUSTER = 'misago_bans'
-
-
-BAN_USERNAME = 0
-BAN_EMAIL = 1
-BAN_IP = 2
-
-
-BANS_CHOICES = (
-    (BAN_USERNAME, _('Username')),
-    (BAN_EMAIL, _('E-mail address')),
-    (BAN_IP, _('IP address')),
-)
+from misago.users.constants import BANS_CACHEBUSTER
 
 
 
 
 class BansManager(models.Manager):
 class BansManager(models.Manager):
@@ -40,19 +21,19 @@ class BansManager(models.Manager):
         return self.get_ban(email=email)
         return self.get_ban(email=email)
 
 
     def invalidate_cache(self):
     def invalidate_cache(self):
-        cachebuster.invalidate(BAN_CACHEBUSTER)
+        cachebuster.invalidate(BANS_CACHEBUSTER)
 
 
     def get_ban(self, username=None, email=None, ip=None):
     def get_ban(self, username=None, email=None, ip=None):
         checks = []
         checks = []
 
 
         if username:
         if username:
             username = username.lower()
             username = username.lower()
-            checks.append(BAN_USERNAME)
+            checks.append(self.model.USERNAME)
         if email:
         if email:
             email = email.lower()
             email = email.lower()
-            checks.append(BAN_EMAIL)
+            checks.append(self.model.EMAIL)
         if ip:
         if ip:
-            checks.append(BAN_IP)
+            checks.append(self.model.IP)
 
 
         queryset = self.filter(is_checked=True)
         queryset = self.filter(is_checked=True)
         if len(checks) == 1:
         if len(checks) == 1:
@@ -63,20 +44,30 @@ class BansManager(models.Manager):
         for ban in queryset.order_by('-id').iterator():
         for ban in queryset.order_by('-id').iterator():
             if ban.is_expired:
             if ban.is_expired:
                 continue
                 continue
-            elif (ban.check_type == BAN_USERNAME and username and
+            elif (ban.check_type == self.model.USERNAME and username and
                     ban.check_value(username)):
                     ban.check_value(username)):
                 return ban
                 return ban
-            elif (ban.check_type == BAN_EMAIL and email and
+            elif (ban.check_type == self.model.EMAIL and email and
                     ban.check_value(email)):
                     ban.check_value(email)):
                 return ban
                 return ban
-            elif ban.check_type == BAN_IP and ip and ban.check_value(ip):
+            elif ban.check_type == self.model.IP and ip and ban.check_value(ip):
                 return ban
                 return ban
         else:
         else:
             raise Ban.DoesNotExist('specified values are not banned')
             raise Ban.DoesNotExist('specified values are not banned')
 
 
 
 
 class Ban(models.Model):
 class Ban(models.Model):
-    check_type = models.PositiveIntegerField(default=BAN_USERNAME, db_index=True)
+    USERNAME = 0
+    EMAIL = 1
+    IP = 2
+
+    CHOICES = (
+        (USERNAME, _('Username')),
+        (EMAIL, _('E-mail address')),
+        (IP, _('IP address')),
+    )
+
+    check_type = models.PositiveIntegerField(default=USERNAME, db_index=True)
     banned_value = models.CharField(max_length=255, db_index=True)
     banned_value = models.CharField(max_length=255, db_index=True)
     user_message = models.TextField(null=True, blank=True)
     user_message = models.TextField(null=True, blank=True)
     staff_message = models.TextField(null=True, blank=True)
     staff_message = models.TextField(null=True, blank=True)
@@ -97,7 +88,7 @@ class Ban(models.Model):
 
 
     @property
     @property
     def check_name(self):
     def check_name(self):
-        return BANS_CHOICES[self.check_type][1]
+        return self.CHOICES[self.check_type][1]
 
 
     @property
     @property
     def name(self):
     def name(self):
@@ -139,7 +130,7 @@ class BanCache(models.Model):
         from ..serializers import BanMessageSerializer
         from ..serializers import BanMessageSerializer
         temp_ban = Ban(
         temp_ban = Ban(
             id=1,
             id=1,
-            check_type=BAN_USERNAME,
+            check_type=Ban.USERNAME,
             user_message=self.user_message,
             user_message=self.user_message,
             staff_message=self.staff_message,
             staff_message=self.staff_message,
             expires_on=self.expires_on
             expires_on=self.expires_on
@@ -152,8 +143,7 @@ class BanCache(models.Model):
 
 
     @property
     @property
     def is_valid(self):
     def is_valid(self):
-        version_is_valid = cachebuster.is_valid(BAN_CACHEBUSTER,
-                                                self.bans_version)
+        version_is_valid = cachebuster.is_valid(BANS_CACHEBUSTER, self.bans_version)
         expired = self.expires_on and self.expires_on < timezone.now()
         expired = self.expires_on and self.expires_on < timezone.now()
 
 
         return version_is_valid and not expired
         return version_is_valid and not expired

+ 36 - 54
misago/users/models/user.py

@@ -24,20 +24,6 @@ from .rank import Rank
 
 
 
 
 __all__ = [
 __all__ = [
-    'ACTIVATION_REQUIRED_NONE',
-    'ACTIVATION_REQUIRED_USER',
-    'ACTIVATION_REQUIRED_ADMIN',
-
-    'AUTO_SUBSCRIBE_NONE',
-    'AUTO_SUBSCRIBE_NOTIFY',
-    'AUTO_SUBSCRIBE_NOTIFY_AND_EMAIL',
-    'AUTO_SUBSCRIBE_CHOICES',
-
-    'LIMITS_PRIVATE_THREAD_INVITES_TO_NONE',
-    'LIMITS_PRIVATE_THREAD_INVITES_TO_FOLLOWED',
-    'LIMITS_PRIVATE_THREAD_INVITES_TO_NOBODY',
-    'PRIVATE_THREAD_INVITES_LIMITS_CHOICES',
-
     'AnonymousUser',
     'AnonymousUser',
     'User',
     'User',
     'UsernameChange',
     'UsernameChange',
@@ -45,34 +31,6 @@ __all__ = [
 ]
 ]
 
 
 
 
-ACTIVATION_REQUIRED_NONE = 0
-ACTIVATION_REQUIRED_USER = 1
-ACTIVATION_REQUIRED_ADMIN = 2
-
-
-AUTO_SUBSCRIBE_NONE = 0
-AUTO_SUBSCRIBE_NOTIFY = 1
-AUTO_SUBSCRIBE_NOTIFY_AND_EMAIL = 2
-
-AUTO_SUBSCRIBE_CHOICES = (
-    (AUTO_SUBSCRIBE_NONE, _("No")),
-    (AUTO_SUBSCRIBE_NOTIFY, _("Notify")),
-    (AUTO_SUBSCRIBE_NOTIFY_AND_EMAIL,
-     _("Notify with e-mail"))
-)
-
-
-LIMITS_PRIVATE_THREAD_INVITES_TO_NONE = 0
-LIMITS_PRIVATE_THREAD_INVITES_TO_FOLLOWED = 1
-LIMITS_PRIVATE_THREAD_INVITES_TO_NOBODY = 2
-
-PRIVATE_THREAD_INVITES_LIMITS_CHOICES = (
-    (LIMITS_PRIVATE_THREAD_INVITES_TO_NONE, _("Everybody")),
-    (LIMITS_PRIVATE_THREAD_INVITES_TO_FOLLOWED, _("Users I follow")),
-    (LIMITS_PRIVATE_THREAD_INVITES_TO_NOBODY, _("Nobody")),
-)
-
-
 class UserManager(BaseUserManager):
 class UserManager(BaseUserManager):
     @transaction.atomic
     @transaction.atomic
     def create_user(self, username, email, password=None, set_default_avatar=False, **extra_fields):
     def create_user(self, username, email, password=None, set_default_avatar=False, **extra_fields):
@@ -90,9 +48,9 @@ class UserManager(BaseUserManager):
             extra_fields['joined_from_ip'] = '127.0.0.1'
             extra_fields['joined_from_ip'] = '127.0.0.1'
 
 
         WATCH_DICT = {
         WATCH_DICT = {
-            'no': AUTO_SUBSCRIBE_NONE,
-            'watch': AUTO_SUBSCRIBE_NOTIFY,
-            'watch_email': AUTO_SUBSCRIBE_NOTIFY_AND_EMAIL,
+            'no':  self.model.SUBSCRIBE_NONE,
+            'watch':  self.model.SUBSCRIBE_NOTIFY,
+            'watch_email':  self.model.SUBSCRIBE_ALL,
         }
         }
 
 
         if not 'subscribe_to_started_threads' in extra_fields:
         if not 'subscribe_to_started_threads' in extra_fields:
@@ -185,6 +143,30 @@ class UserManager(BaseUserManager):
 
 
 
 
 class User(AbstractBaseUser, PermissionsMixin):
 class User(AbstractBaseUser, PermissionsMixin):
+    ACTIVATION_NONE = 0
+    ACTIVATION_USER = 1
+    ACTIVATION_ADMIN = 2
+
+    SUBSCRIBE_NONE = 0
+    SUBSCRIBE_NOTIFY = 1
+    SUBSCRIBE_ALL = 2
+
+    SUBSCRIBE_CHOICES = (
+        (SUBSCRIBE_NONE, _("No")),
+        (SUBSCRIBE_NOTIFY, _("Notify")),
+        (SUBSCRIBE_ALL, _("Notify with e-mail"))
+    )
+
+    LIMIT_INVITES_TO_NONE = 0
+    LIMIT_INVITES_TO_FOLLOWED = 1
+    LIMIT_INVITES_TO_NOBODY = 2
+
+    LIMIT_INVITES_TO_CHOICES = (
+        (LIMIT_INVITES_TO_NONE, _("Everybody")),
+        (LIMIT_INVITES_TO_FOLLOWED, _("Users I follow")),
+        (LIMIT_INVITES_TO_NOBODY, _("Nobody")),
+    )
+
     """
     """
     Note that "username" field is purely for shows.
     Note that "username" field is purely for shows.
     When searching users by their names, always use lowercased string
     When searching users by their names, always use lowercased string
@@ -209,7 +191,7 @@ class User(AbstractBaseUser, PermissionsMixin):
 
 
     rank = models.ForeignKey('Rank', null=True, blank=True, on_delete=models.deletion.PROTECT)
     rank = models.ForeignKey('Rank', null=True, blank=True, on_delete=models.deletion.PROTECT)
     title = models.CharField(max_length=255, null=True, blank=True)
     title = models.CharField(max_length=255, null=True, blank=True)
-    requires_activation = models.PositiveIntegerField(default=ACTIVATION_REQUIRED_NONE)
+    requires_activation = models.PositiveIntegerField(default=ACTIVATION_NONE)
 
 
     is_staff = models.BooleanField(_('staff status'),
     is_staff = models.BooleanField(_('staff status'),
         default=False,
         default=False,
@@ -268,16 +250,16 @@ class User(AbstractBaseUser, PermissionsMixin):
     )
     )
 
 
     limits_private_thread_invites_to = models.PositiveIntegerField(
     limits_private_thread_invites_to = models.PositiveIntegerField(
-        default=LIMITS_PRIVATE_THREAD_INVITES_TO_NONE
+        default=LIMIT_INVITES_TO_NONE
     )
     )
     unread_private_threads = models.PositiveIntegerField(default=0)
     unread_private_threads = models.PositiveIntegerField(default=0)
     sync_unread_private_threads = models.BooleanField(default=False)
     sync_unread_private_threads = models.BooleanField(default=False)
 
 
     subscribe_to_started_threads = models.PositiveIntegerField(
     subscribe_to_started_threads = models.PositiveIntegerField(
-        default=AUTO_SUBSCRIBE_NONE
+        default=SUBSCRIBE_NONE
     )
     )
     subscribe_to_replied_threads = models.PositiveIntegerField(
     subscribe_to_replied_threads = models.PositiveIntegerField(
-        default=AUTO_SUBSCRIBE_NONE
+        default=SUBSCRIBE_NONE
     )
     )
 
 
     threads = models.PositiveIntegerField(default=0)
     threads = models.PositiveIntegerField(default=0)
@@ -332,26 +314,26 @@ class User(AbstractBaseUser, PermissionsMixin):
 
 
     @property
     @property
     def requires_activation_by_admin(self):
     def requires_activation_by_admin(self):
-        return self.requires_activation == ACTIVATION_REQUIRED_ADMIN
+        return self.requires_activation == self.ACTIVATION_ADMIN
 
 
     @property
     @property
     def requires_activation_by_user(self):
     def requires_activation_by_user(self):
-        return self.requires_activation == ACTIVATION_REQUIRED_USER
+        return self.requires_activation == self.ACTIVATION_USER
 
 
     @property
     @property
     def can_be_messaged_by_everyone(self):
     def can_be_messaged_by_everyone(self):
         preference = self.limits_private_thread_invites_to
         preference = self.limits_private_thread_invites_to
-        return preference == LIMITS_PRIVATE_THREAD_INVITES_TO_NONE
+        return preference == self.LIMIT_INVITES_TO_NONE
 
 
     @property
     @property
     def can_be_messaged_by_followed(self):
     def can_be_messaged_by_followed(self):
         preference = self.limits_private_thread_invites_to
         preference = self.limits_private_thread_invites_to
-        return preference == LIMITS_PRIVATE_THREAD_INVITES_TO_FOLLOWED
+        return preference == self.LIMIT_INVITES_TO_FOLLOWED
 
 
     @property
     @property
     def can_be_messaged_by_nobody(self):
     def can_be_messaged_by_nobody(self):
         preference = self.limits_private_thread_invites_to
         preference = self.limits_private_thread_invites_to
-        return preference == LIMITS_PRIVATE_THREAD_INVITES_TO_NOBODY
+        return preference == self.LIMIT_INVITES_TO_NOBODY
 
 
     @property
     @property
     def has_valid_signature(self):
     def has_valid_signature(self):

+ 3 - 3
misago/users/permissions/profiles.py

@@ -25,7 +25,7 @@ CAN_SEARCH_USERS = YesNoSwitch(
 CAN_SEE_USER_NAME_HISTORY = YesNoSwitch(
 CAN_SEE_USER_NAME_HISTORY = YesNoSwitch(
     label=_("Can see other members name history")
     label=_("Can see other members name history")
 )
 )
-CAN_SEE_BAN_DETAILS = YesNoSwitch(
+CAN_SEE_DETAILS = YesNoSwitch(
     label=_("Can see members bans details"),
     label=_("Can see members bans details"),
     help_text=_("Allows users with this permission to see user and staff ban messages.")
     help_text=_("Allows users with this permission to see user and staff ban messages.")
 )
 )
@@ -37,7 +37,7 @@ class LimitedPermissionsForm(forms.Form):
     can_browse_users_list = CAN_BROWSE_USERS_LIST
     can_browse_users_list = CAN_BROWSE_USERS_LIST
     can_search_users = CAN_SEARCH_USERS
     can_search_users = CAN_SEARCH_USERS
     can_see_users_name_history = CAN_SEE_USER_NAME_HISTORY
     can_see_users_name_history = CAN_SEE_USER_NAME_HISTORY
-    can_see_ban_details = CAN_SEE_BAN_DETAILS
+    can_see_ban_details = CAN_SEE_DETAILS
 
 
 
 
 class PermissionsForm(LimitedPermissionsForm):
 class PermissionsForm(LimitedPermissionsForm):
@@ -52,7 +52,7 @@ class PermissionsForm(LimitedPermissionsForm):
         initial=0
         initial=0
     )
     )
     can_see_users_name_history = CAN_SEE_USER_NAME_HISTORY
     can_see_users_name_history = CAN_SEE_USER_NAME_HISTORY
-    can_see_ban_details = CAN_SEE_BAN_DETAILS
+    can_see_ban_details = CAN_SEE_DETAILS
     can_see_users_emails = YesNoSwitch(
     can_see_users_emails = YesNoSwitch(
         label=_("Can see members e-mails")
         label=_("Can see members e-mails")
     )
     )

+ 2 - 2
misago/users/serializers/ban.py

@@ -4,7 +4,7 @@ from rest_framework import serializers
 
 
 from misago.core.utils import format_plaintext_for_html
 from misago.core.utils import format_plaintext_for_html
 
 
-from ..models import BAN_IP, Ban
+from ..models import Ban
 
 
 
 
 __all__ = [
 __all__ = [
@@ -33,7 +33,7 @@ class BanMessageSerializer(serializers.ModelSerializer):
     def get_message(self, obj):
     def get_message(self, obj):
         if obj.user_message:
         if obj.user_message:
             message = obj.user_message
             message = obj.user_message
-        elif obj.check_type == BAN_IP:
+        elif obj.check_type == Ban.IP:
             message = _("Your IP address is banned.")
             message = _("Your IP address is banned.")
         else:
         else:
             message = _("You are banned.")
             message = _("You are banned.")

+ 2 - 2
misago/users/tests/test_activation_views.py

@@ -4,7 +4,7 @@ from django.urls import reverse
 
 
 from misago.core.utils import encode_json_html
 from misago.core.utils import encode_json_html
 
 
-from ..models import BAN_USERNAME, Ban
+from ..models import Ban
 from ..tokens import make_activation_token
 from ..tokens import make_activation_token
 
 
 
 
@@ -20,7 +20,7 @@ class ActivationViewsTests(TestCase):
         test_user = User.objects.create_user('Bob', 'bob@test.com', 'Pass.123',
         test_user = User.objects.create_user('Bob', 'bob@test.com', 'Pass.123',
                                              requires_activation=1)
                                              requires_activation=1)
         Ban.objects.create(
         Ban.objects.create(
-            check_type=BAN_USERNAME,
+            check_type=Ban.USERNAME,
             banned_value='bob',
             banned_value='bob',
             user_message='Nope!',
             user_message='Nope!',
         )
         )

+ 6 - 6
misago/users/tests/test_auth_api.py

@@ -2,7 +2,7 @@ from django.contrib.auth import get_user_model
 from django.core import mail
 from django.core import mail
 from django.test import TestCase
 from django.test import TestCase
 
 
-from ..models import BAN_USERNAME, Ban
+from ..models import Ban
 from ..tokens import make_password_change_token
 from ..tokens import make_password_change_token
 
 
 
 
@@ -51,7 +51,7 @@ class GatewayTests(TestCase):
         User.objects.create_user('Bob', 'bob@test.com', 'Pass.123')
         User.objects.create_user('Bob', 'bob@test.com', 'Pass.123')
 
 
         ban = Ban.objects.create(
         ban = Ban.objects.create(
-            check_type=BAN_USERNAME,
+            check_type=Ban.USERNAME,
             banned_value='bob',
             banned_value='bob',
             user_message='You are tragically banned.',
             user_message='You are tragically banned.',
         )
         )
@@ -84,7 +84,7 @@ class GatewayTests(TestCase):
         user.save()
         user.save()
 
 
         ban = Ban.objects.create(
         ban = Ban.objects.create(
-            check_type=BAN_USERNAME,
+            check_type=Ban.USERNAME,
             banned_value='bob',
             banned_value='bob',
             user_message='You are tragically banned.',
             user_message='You are tragically banned.',
         )
         )
@@ -192,7 +192,7 @@ class SendActivationAPITests(TestCase):
     def test_submit_banned(self):
     def test_submit_banned(self):
         """request activation link api passes for banned users"""
         """request activation link api passes for banned users"""
         Ban.objects.create(
         Ban.objects.create(
-            check_type=BAN_USERNAME,
+            check_type=Ban.USERNAME,
             banned_value=self.user.username,
             banned_value=self.user.username,
             user_message='Nope!',
             user_message='Nope!',
         )
         )
@@ -271,7 +271,7 @@ class SendPasswordFormAPITests(TestCase):
     def test_submit_banned(self):
     def test_submit_banned(self):
         """request change password form link api sends reset link mail"""
         """request change password form link api sends reset link mail"""
         Ban.objects.create(
         Ban.objects.create(
-            check_type=BAN_USERNAME,
+            check_type=Ban.USERNAME,
             banned_value=self.user.username,
             banned_value=self.user.username,
             user_message='Nope!',
             user_message='Nope!',
         )
         )
@@ -352,7 +352,7 @@ class ChangePasswordAPITests(TestCase):
     def test_banned_user_link(self):
     def test_banned_user_link(self):
         """request errors because user is banned"""
         """request errors because user is banned"""
         Ban.objects.create(
         Ban.objects.create(
-            check_type=BAN_USERNAME,
+            check_type=Ban.USERNAME,
             banned_value=self.user.username,
             banned_value=self.user.username,
             user_message='Nope!',
             user_message='Nope!',
         )
         )

+ 13 - 4
misago/users/tests/test_ban_model.py

@@ -1,15 +1,24 @@
 #-*- coding: utf-8 -*-
 #-*- coding: utf-8 -*-
 from django.test import TestCase
 from django.test import TestCase
 
 
-from ..models import BAN_EMAIL, BAN_IP, BAN_USERNAME, Ban
+from ..models import Ban
 
 
 
 
 class BansManagerTests(TestCase):
 class BansManagerTests(TestCase):
     def setUp(self):
     def setUp(self):
         Ban.objects.bulk_create([
         Ban.objects.bulk_create([
-            Ban(check_type=BAN_USERNAME, banned_value='bob'),
-            Ban(check_type=BAN_EMAIL, banned_value='bob@test.com'),
-            Ban(check_type=BAN_IP, banned_value='127.0.0.1'),
+            Ban(
+                check_type=Ban.USERNAME,
+                banned_value='bob'
+            ),
+            Ban(
+                check_type=Ban.EMAIL,
+                banned_value='bob@test.com'
+            ),
+            Ban(
+                check_type=Ban.IP,
+                banned_value='127.0.0.1'
+            ),
         ])
         ])
 
 
     def test_get_ban_for_banned_name(self):
     def test_get_ban_for_banned_name(self):

+ 14 - 12
misago/users/tests/test_bans.py

@@ -4,8 +4,10 @@ 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 ..bans import ban_ip, ban_user, get_email_ban, get_ip_ban, get_request_ip_ban, get_user_ban, get_username_ban
-from ..models import BAN_EMAIL, BAN_IP, BAN_USERNAME, Ban
+from ..bans import (
+    ban_ip, ban_user, get_email_ban, get_ip_ban,
+    get_request_ip_ban, get_user_ban, get_username_ban)
+from ..models import Ban
 
 
 
 
 class GetBanTests(TestCase):
 class GetBanTests(TestCase):
@@ -24,7 +26,7 @@ class GetBanTests(TestCase):
 
 
         Ban.objects.create(
         Ban.objects.create(
             banned_value='wrongtype',
             banned_value='wrongtype',
-            check_type=BAN_EMAIL
+            check_type=Ban.EMAIL
         )
         )
 
 
         wrong_type_ban = get_username_ban('wrongtype')
         wrong_type_ban = get_username_ban('wrongtype')
@@ -43,7 +45,7 @@ class GetBanTests(TestCase):
 
 
         Ban.objects.create(
         Ban.objects.create(
             banned_value='ex@pired.com',
             banned_value='ex@pired.com',
-            check_type=BAN_EMAIL,
+            check_type=Ban.EMAIL,
             expires_on=timezone.now() - timedelta(days=7)
             expires_on=timezone.now() - timedelta(days=7)
         )
         )
 
 
@@ -52,7 +54,7 @@ class GetBanTests(TestCase):
 
 
         Ban.objects.create(
         Ban.objects.create(
             banned_value='wrong@type.com',
             banned_value='wrong@type.com',
-            check_type=BAN_IP
+            check_type=Ban.IP
         )
         )
 
 
         wrong_type_ban = get_email_ban('wrong@type.com')
         wrong_type_ban = get_email_ban('wrong@type.com')
@@ -60,7 +62,7 @@ class GetBanTests(TestCase):
 
 
         valid_ban = Ban.objects.create(
         valid_ban = Ban.objects.create(
             banned_value='*.ru',
             banned_value='*.ru',
-            check_type=BAN_EMAIL,
+            check_type=Ban.EMAIL,
             expires_on=timezone.now() + timedelta(days=7)
             expires_on=timezone.now() + timedelta(days=7)
         )
         )
         self.assertEqual(get_email_ban('banned@mail.ru').pk, valid_ban.pk)
         self.assertEqual(get_email_ban('banned@mail.ru').pk, valid_ban.pk)
@@ -72,7 +74,7 @@ class GetBanTests(TestCase):
 
 
         Ban.objects.create(
         Ban.objects.create(
             banned_value='124.0.0.1',
             banned_value='124.0.0.1',
-            check_type=BAN_IP,
+            check_type=Ban.IP,
             expires_on=timezone.now() - timedelta(days=7)
             expires_on=timezone.now() - timedelta(days=7)
         )
         )
 
 
@@ -81,7 +83,7 @@ class GetBanTests(TestCase):
 
 
         Ban.objects.create(
         Ban.objects.create(
             banned_value='wrongtype',
             banned_value='wrongtype',
-            check_type=BAN_EMAIL
+            check_type=Ban.EMAIL
         )
         )
 
 
         wrong_type_ban = get_ip_ban('wrongtype')
         wrong_type_ban = get_ip_ban('wrongtype')
@@ -89,7 +91,7 @@ class GetBanTests(TestCase):
 
 
         valid_ban = Ban.objects.create(
         valid_ban = Ban.objects.create(
             banned_value='125.0.0.*',
             banned_value='125.0.0.*',
-            check_type=BAN_IP,
+            check_type=Ban.IP,
             expires_on=timezone.now() + timedelta(days=7)
             expires_on=timezone.now() + timedelta(days=7)
         )
         )
         self.assertEqual(get_ip_ban('125.0.0.1').pk, valid_ban.pk)
         self.assertEqual(get_ip_ban('125.0.0.1').pk, valid_ban.pk)
@@ -173,7 +175,7 @@ class RequestIPBansTests(TestCase):
     def test_permanent_ban(self):
     def test_permanent_ban(self):
         """ip is caught by permanent ban"""
         """ip is caught by permanent ban"""
         Ban.objects.create(
         Ban.objects.create(
-            check_type=BAN_IP,
+            check_type=Ban.IP,
             banned_value='127.0.0.1',
             banned_value='127.0.0.1',
             user_message='User reason'
             user_message='User reason'
         )
         )
@@ -189,7 +191,7 @@ class RequestIPBansTests(TestCase):
     def test_temporary_ban(self):
     def test_temporary_ban(self):
         """ip is caught by temporary ban"""
         """ip is caught by temporary ban"""
         Ban.objects.create(
         Ban.objects.create(
-            check_type=BAN_IP,
+            check_type=Ban.IP,
             banned_value='127.0.0.1',
             banned_value='127.0.0.1',
             user_message='User reason',
             user_message='User reason',
             expires_on=timezone.now() + timedelta(days=7)
             expires_on=timezone.now() + timedelta(days=7)
@@ -206,7 +208,7 @@ class RequestIPBansTests(TestCase):
     def test_expired_ban(self):
     def test_expired_ban(self):
         """ip is not caught by expired ban"""
         """ip is not caught by expired ban"""
         Ban.objects.create(
         Ban.objects.create(
-            check_type=BAN_IP,
+            check_type=Ban.IP,
             banned_value='127.0.0.1',
             banned_value='127.0.0.1',
             user_message='User reason',
             user_message='User reason',
             expires_on=timezone.now() - timedelta(days=7)
             expires_on=timezone.now() - timedelta(days=7)

+ 7 - 5
misago/users/tests/test_decorators.py

@@ -2,7 +2,7 @@ from django.urls import reverse
 
 
 from misago.core.utils import encode_json_html
 from misago.core.utils import encode_json_html
 
 
-from ..models import BAN_IP, Ban
+from ..models import Ban
 from ..testutils import UserTestCase
 from ..testutils import UserTestCase
 
 
 
 
@@ -38,9 +38,10 @@ class DenyBannedIPTests(UserTestCase):
     def test_success(self):
     def test_success(self):
         """deny_banned_ips decorator allowed unbanned request"""
         """deny_banned_ips decorator allowed unbanned request"""
         Ban.objects.create(
         Ban.objects.create(
-            check_type=BAN_IP,
+            check_type=Ban.IP,
             banned_value='83.*',
             banned_value='83.*',
-            user_message="Ya got banned!")
+            user_message="Ya got banned!"
+        )
 
 
         response = self.client.post(reverse('misago:request-activation'))
         response = self.client.post(reverse('misago:request-activation'))
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)
@@ -48,9 +49,10 @@ class DenyBannedIPTests(UserTestCase):
     def test_fail(self):
     def test_fail(self):
         """deny_banned_ips decorator denied banned request"""
         """deny_banned_ips decorator denied banned request"""
         Ban.objects.create(
         Ban.objects.create(
-            check_type=BAN_IP,
+            check_type=Ban.IP,
             banned_value='127.*',
             banned_value='127.*',
-            user_message="Ya got banned!")
+            user_message="Ya got banned!"
+        )
 
 
         response = self.client.post(reverse('misago:request-activation'))
         response = self.client.post(reverse('misago:request-activation'))
         self.assertContains(
         self.assertContains(

+ 2 - 2
misago/users/tests/test_forgottenpassword_views.py

@@ -3,7 +3,7 @@ from django.urls import reverse
 
 
 from misago.core.utils import encode_json_html
 from misago.core.utils import encode_json_html
 
 
-from ..models import BAN_USERNAME, Ban
+from ..models import Ban
 from ..testutils import UserTestCase
 from ..testutils import UserTestCase
 from ..tokens import make_password_change_token
 from ..tokens import make_password_change_token
 
 
@@ -27,7 +27,7 @@ class ForgottenPasswordViewsTests(UserTestCase):
         test_user = User.objects.create_user('Bob', 'bob@test.com', 'Pass.123')
         test_user = User.objects.create_user('Bob', 'bob@test.com', 'Pass.123')
 
 
         Ban.objects.create(
         Ban.objects.create(
-            check_type=BAN_USERNAME,
+            check_type=Ban.USERNAME,
             banned_value='bob',
             banned_value='bob',
             user_message='Nope!',
             user_message='Nope!',
         )
         )

+ 7 - 5
misago/users/tests/test_rest_permissions.py

@@ -1,6 +1,6 @@
 from django.urls import reverse
 from django.urls import reverse
 
 
-from ..models import BAN_IP, Ban
+from ..models import Ban
 from ..testutils import UserTestCase
 from ..testutils import UserTestCase
 
 
 
 
@@ -29,9 +29,10 @@ class UnbannedOnlyTests(UserTestCase):
     def test_api_blocks_banned(self):
     def test_api_blocks_banned(self):
         """policy blocked banned ip"""
         """policy blocked banned ip"""
         Ban.objects.create(
         Ban.objects.create(
-            check_type=BAN_IP,
+            check_type=Ban.IP,
             banned_value='127.*',
             banned_value='127.*',
-            user_message='Ya got banned!')
+            user_message='Ya got banned!'
+        )
 
 
         response = self.client.post(
         response = self.client.post(
             reverse('misago:api:send-password-form'), data={
             reverse('misago:api:send-password-form'), data={
@@ -68,9 +69,10 @@ class UnbannedAnonOnlyTests(UserTestCase):
     def test_api_blocks_banned(self):
     def test_api_blocks_banned(self):
         """policy blocked banned ip"""
         """policy blocked banned ip"""
         Ban.objects.create(
         Ban.objects.create(
-            check_type=BAN_IP,
+            check_type=Ban.IP,
             banned_value='127.*',
             banned_value='127.*',
-            user_message='Ya got banned!')
+            user_message='Ya got banned!'
+        )
 
 
         response = self.client.post(
         response = self.client.post(
             reverse('misago:api:send-activation'), data={
             reverse('misago:api:send-activation'), data={

+ 6 - 4
misago/users/tests/test_users_api.py

@@ -14,7 +14,7 @@ from misago.threads.models import Post, Thread
 from misago.threads.testutils import post_thread
 from misago.threads.testutils import post_thread
 
 
 from ..activepostersranking import build_active_posters_ranking
 from ..activepostersranking import build_active_posters_ranking
-from ..models import BAN_USERNAME, Ban, Rank
+from ..models import Ban, Rank
 from ..testutils import AuthenticatedUserTestCase
 from ..testutils import AuthenticatedUserTestCase
 
 
 
 
@@ -439,9 +439,11 @@ class UserBanTests(AuthenticatedUserTestCase):
             'can_see_ban_details': 1
             'can_see_ban_details': 1
         })
         })
 
 
-        Ban.objects.create(check_type=BAN_USERNAME,
-                           banned_value=self.other_user.username,
-                           user_message='Nope!')
+        Ban.objects.create(
+            check_type=Ban.USERNAME,
+            banned_value=self.other_user.username,
+            user_message='Nope!'
+        )
 
 
         response = self.client.get(self.link)
         response = self.client.get(self.link)
         self.assertEqual(response.status_code, 200)
         self.assertEqual(response.status_code, 200)

+ 9 - 3
misago/users/tests/test_validators.py

@@ -5,7 +5,7 @@ from django.test import TestCase
 
 
 from misago.conf import settings
 from misago.conf import settings
 
 
-from ..models import BAN_EMAIL, BAN_USERNAME, Ban
+from ..models import Ban
 from ..validators import (
 from ..validators import (
     validate_email,
     validate_email,
     validate_email_available,
     validate_email_available,
@@ -39,7 +39,10 @@ class ValidateEmailAvailableTests(TestCase):
 
 
 class ValidateEmailBannedTests(TestCase):
 class ValidateEmailBannedTests(TestCase):
     def setUp(self):
     def setUp(self):
-        Ban.objects.create(check_type=BAN_EMAIL, banned_value="ban@test.com")
+        Ban.objects.create(
+            check_type=Ban.EMAIL,
+            banned_value="ban@test.com"
+        )
 
 
     def test_unbanned_name(self):
     def test_unbanned_name(self):
         """unbanned email passes validation"""
         """unbanned email passes validation"""
@@ -87,7 +90,10 @@ class ValidateUsernameAvailableTests(TestCase):
 
 
 class ValidateUsernameBannedTests(TestCase):
 class ValidateUsernameBannedTests(TestCase):
     def setUp(self):
     def setUp(self):
-        Ban.objects.create(check_type=BAN_USERNAME, banned_value="Bob")
+        Ban.objects.create(
+            check_type=Ban.USERNAME,
+            banned_value="Bob"
+        )
 
 
     def test_unbanned_name(self):
     def test_unbanned_name(self):
         """unbanned name passes validation"""
         """unbanned name passes validation"""

+ 1 - 2
misago/users/views/activation.py

@@ -9,7 +9,6 @@ from misago.core.mail import mail_user
 
 
 from ..bans import get_user_ban
 from ..bans import get_user_ban
 from ..decorators import deny_authenticated, deny_banned_ips
 from ..decorators import deny_authenticated, deny_banned_ips
-from ..models import ACTIVATION_REQUIRED_NONE
 from ..tokens import is_activation_token_valid
 from ..tokens import is_activation_token_valid
 
 
 
 
@@ -66,7 +65,7 @@ def activate_by_token(request, pk, token):
                 'message': e.args[0],
                 'message': e.args[0],
             }, status=400)
             }, status=400)
 
 
-    inactive_user.requires_activation = ACTIVATION_REQUIRED_NONE
+    inactive_user.requires_activation = User.ACTIVATION_NONE
     inactive_user.save(update_fields=['requires_activation'])
     inactive_user.save(update_fields=['requires_activation'])
 
 
     message = _("%(user)s, your account has been activated!")
     message = _("%(user)s, your account has been activated!")

+ 7 - 8
misago/users/views/admin/users.py

@@ -17,8 +17,7 @@ from ...avatars.dynamic import set_avatar as set_dynamic_avatar
 from ...forms.admin import (
 from ...forms.admin import (
     BanUsersForm, NewUserForm, SearchUsersForm,
     BanUsersForm, NewUserForm, SearchUsersForm,
     EditUserForm, EditUserFormFactory)
     EditUserForm, EditUserFormFactory)
-from ...models import ACTIVATION_REQUIRED_NONE, Ban, User
-from ...models.ban import BAN_EMAIL, BAN_IP, BAN_USERNAME
+from ...models import Ban, User
 from ...signatures import set_user_signature
 from ...signatures import set_user_signature
 
 
 
 
@@ -108,7 +107,7 @@ class UsersList(UserAdmin, generic.ListView):
         else:
         else:
             activated_users_pks = [u.pk for u in inactive_users]
             activated_users_pks = [u.pk for u in inactive_users]
             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=User.ACTIVATION_NONE)
 
 
             subject = _("Your account on %(forum_name)s forums has been activated")
             subject = _("Your account on %(forum_name)s forums has been activated")
             mail_subject = subject % {
             mail_subject = subject % {
@@ -145,25 +144,25 @@ class UsersList(UserAdmin, generic.ListView):
                 for user in users:
                 for user in users:
                     for ban in cleaned_data['ban_type']:
                     for ban in cleaned_data['ban_type']:
                         if ban == 'usernames':
                         if ban == 'usernames':
-                            check_type = BAN_USERNAME
+                            check_type = Ban.USERNAME
                             banned_value = user.username.lower()
                             banned_value = user.username.lower()
 
 
                         if ban == 'emails':
                         if ban == 'emails':
-                            check_type = BAN_EMAIL
+                            check_type = Ban.EMAIL
                             banned_value = user.email.lower()
                             banned_value = user.email.lower()
 
 
                         if ban == 'domains':
                         if ban == 'domains':
-                            check_type = BAN_EMAIL
+                            check_type = Ban.EMAIL
                             banned_value = user.email.lower()
                             banned_value = user.email.lower()
                             at_pos = banned_value.find('@')
                             at_pos = banned_value.find('@')
                             banned_value = '*%s' % banned_value[at_pos:]
                             banned_value = '*%s' % banned_value[at_pos:]
 
 
                         if ban == 'ip':
                         if ban == 'ip':
-                            check_type = BAN_IP
+                            check_type = Ban.IP
                             banned_value = user.joined_from_ip
                             banned_value = user.joined_from_ip
 
 
                         if ban in ('ip_first', 'ip_two'):
                         if ban in ('ip_first', 'ip_two'):
-                            check_type = BAN_IP
+                            check_type = Ban.IP
 
 
                             if ':' in user.joined_from_ip:
                             if ':' in user.joined_from_ip:
                                 ip_separator = ':'
                                 ip_separator = ':'