Browse Source

Finalize misago.conf module

rafalp 6 years ago
parent
commit
c7ff046a12

+ 1 - 2
misago/conf/__init__.py

@@ -1,8 +1,7 @@
-from .gateway import db_settings  # noqa
 from .staticsettings import StaticSettings
 
 default_app_config = 'misago.conf.apps.MisagoConfConfig'
 
 SETTINGS_CACHE = "settings"
 
-settings = StaticSettings()
+settings = StaticSettings()

+ 23 - 0
misago/conf/cache.py

@@ -0,0 +1,23 @@
+from django.core.cache import cache
+
+from misago.cache.versions import invalidate_cache
+
+from . import SETTINGS_CACHE
+
+
+def get_settings_cache(cache_versions):
+    key = get_cache_key(cache_versions)
+    return cache.get(key)
+
+
+def set_settings_cache(cache_versions, user_settings):
+    key = get_cache_key(cache_versions)
+    cache.set(key, user_settings)
+
+
+def get_cache_key(cache_versions):
+    return "%s_%s" % (SETTINGS_CACHE, cache_versions[SETTINGS_CACHE])
+
+
+def clear_settings_cache():
+    invalidate_cache(SETTINGS_CACHE)

+ 1 - 1
misago/conf/context_processors.py

@@ -4,7 +4,7 @@ from django.utils.translation import get_language
 
 from misago.users.social.utils import get_enabled_social_auth_sites_list
 
-from .gateway import settings
+from . import settings
 
 BLANK_AVATAR_URL = static(settings.MISAGO_BLANK_AVATAR)
 

+ 0 - 105
misago/conf/dbsettings.py

@@ -1,105 +0,0 @@
-from django.core.cache import cache
-
-from misago.core import threadstore
-
-CACHE_KEY = 'misago_db_settings'
-
-
-class DBSettingsDeprecated(object):
-    def __init__(self):
-        self._settings = self._read_cache()
-        self._overrides = {}
-
-    def _read_cache(self):
-        from misago.core.cache import cache
-
-        data = cache.get(CACHE_KEY, 'nada')
-        if data == 'nada':
-            data = self._read_db()
-            cache.set(CACHE_KEY, data)
-        return data
-
-    def _read_db(self):
-        from .models import Setting
-
-        data = {}
-        for setting in Setting.objects.iterator():
-            if setting.is_lazy:
-                data[setting.setting] = {
-                    'value': True if setting.value else None,
-                    'is_lazy': setting.is_lazy,
-                    'is_public': setting.is_public,
-                }
-            else:
-                data[setting.setting] = {
-                    'value': setting.value,
-                    'is_lazy': setting.is_lazy,
-                    'is_public': setting.is_public,
-                }
-        return data
-
-    def get_public_settings(self):
-        public_settings = {}
-        for name, setting in self._settings.items():
-            if setting['is_public']:
-                public_settings[name] = setting['value']
-        return public_settings
-
-    def get_lazy_setting(self, setting):
-        from . import ENABLE_GLOBAL_STATE
-        if not ENABLE_GLOBAL_STATE:
-            raise Exception("Trying to access lazy dynamic setting: %s" % name)
-
-        from .models import Setting
-
-        try:
-            if self._settings[setting]['is_lazy']:
-                if not self._settings[setting].get('real_value'):
-                    real_value = Setting.objects.get(setting=setting).value
-                    self._settings[setting]['real_value'] = real_value
-                return self._settings[setting]['real_value']
-            else:
-                raise ValueError("Setting %s is not lazy" % setting)
-        except (KeyError, Setting.DoesNotExist):
-            raise AttributeError("Setting %s is undefined" % setting)
-
-    def flush_cache(self):
-        from misago.core.cache import cache
-        cache.delete(CACHE_KEY)
-
-    def __getattr__(self, attr):
-        from . import ENABLE_GLOBAL_STATE
-        if not ENABLE_GLOBAL_STATE:
-            raise Exception("Trying to access dynamic setting: %s" % name)
-
-        try:
-            return self._settings[attr]['value']
-        except KeyError:
-            raise AttributeError("Setting %s is undefined" % attr)
-
-    def override_setting(self, setting, new_value):
-        if not setting in self._overrides:
-            self._overrides[setting] = self._settings[setting]['value']
-        self._settings[setting]['value'] = new_value
-        self._settings[setting]['real_value'] = new_value
-        return new_value
-
-    def reset_settings(self):
-        for setting, original_value in self._overrides.items():
-            self._settings[setting]['value'] = original_value
-            self._settings[setting].pop('real_value', None)
-
-
-class _DBSettingsGateway(object):
-    def get_db_settings(self):
-        dbsettings = threadstore.get(CACHE_KEY)
-        if not dbsettings:
-            dbsettings = DBSettingsDeprecated()
-            threadstore.set(CACHE_KEY, dbsettings)
-        return dbsettings
-
-    def __getattr__(self, attr):
-        return getattr(self.get_db_settings(), attr)
-
-
-db_settings = _DBSettingsGateway()

+ 3 - 6
misago/conf/dynamicsettings.py

@@ -1,6 +1,4 @@
-from django.core.cache import cache
-
-from . import SETTINGS_CACHE
+from .cache import get_settings_cache, set_settings_cache
 from .models import Setting
 
 
@@ -8,11 +6,10 @@ class DynamicSettings:
     _overrides = {}
 
     def __init__(self, cache_versions):
-        cache_name = get_cache_name(cache_versions)
-        self._settings = cache.get(cache_name)
+        self._settings = get_settings_cache(cache_versions)
         if self._settings is None:
             self._settings = get_settings_from_db()
-            cache.set(cache_name, self._settings)
+            set_settings_cache(cache_versions, self._settings)
 
     def get_public_settings(self):
         public_settings = {}

+ 1 - 2
misago/conf/forms.py

@@ -4,8 +4,7 @@ from django.utils.translation import ngettext
 
 from misago.admin.forms import YesNoSwitch
 
-
-__ALL__ = ['ChangeSettingsForm']
+__all__ = ['ChangeSettingsForm']
 
 
 class ValidateChoicesNum(object):

+ 0 - 26
misago/conf/gateway.py

@@ -1,26 +0,0 @@
-from django.conf import settings as dj_settings
-
-from . import defaults
-from .dbsettings import db_settings
-
-
-class SettingsGateway(object):
-    def __getattr__(self, name):
-        from . import ENABLE_GLOBAL_STATE
-        if not ENABLE_GLOBAL_STATE and name.lower() == name:
-            raise Exception("Trying to access dynamic setting: %s" % name)
-
-        try:
-            return getattr(dj_settings, name)
-        except AttributeError:
-            pass
-
-        try:
-            return getattr(defaults, name)
-        except AttributeError:
-            pass
-
-        return getattr(db_settings, name)
-
-
-settings = SettingsGateway()

+ 0 - 7
misago/conf/migrationutils.py

@@ -1,6 +1,3 @@
-from misago.core.cache import cache as default_cache
-
-from .dbsettings import CACHE_KEY
 from .hydrators import dehydrate_value
 from .utils import get_setting_value, has_custom_value
 
@@ -91,7 +88,3 @@ def migrate_setting(Setting, group, setting_fixture, order, old_value):
     setting.field_extra = field_extra or {}
 
     setting.save()
-
-
-def delete_settings_cache():
-    default_cache.delete(CACHE_KEY)

+ 1 - 1
misago/conf/staticsettings.py

@@ -18,4 +18,4 @@ class StaticSettings(object):
         except AttributeError:
             pass
 
-        raise AttributeError("Setting %s is undefined" % name)
+        raise AttributeError

+ 6 - 1
misago/conf/tests/test_getting_static_settings_values.py

@@ -25,4 +25,9 @@ class GettingSettingValueTests(TestCase):
     def test_undefined_setting_value_can_be_overridden_using_django_util(self):
         settings = StaticSettings()
         with override_settings(UNDEFINED_SETTING="test"):
-            assert settings.UNDEFINED_SETTING == "test"
+            assert settings.UNDEFINED_SETTING == "test"
+
+    def test_accessing_attr_for_undefined_setting_raises_attribute_error(self):
+        settings = StaticSettings()
+        with self.assertRaises(AttributeError):
+            assert settings.UNDEFINED_SETTING

+ 0 - 132
misago/conf/tests/test_settings.py

@@ -1,132 +0,0 @@
-from django.apps import apps
-from django.conf import settings as dj_settings
-from django.test import TestCase, override_settings
-
-from misago.conf import defaults
-from misago.conf.dbsettings import db_settings
-from misago.conf.gateway import settings as gateway
-from misago.conf.migrationutils import migrate_settings_group
-from misago.core import threadstore
-from misago.core.cache import cache
-
-
-class DBSettingsTests(TestCase):
-    def test_get_existing_setting(self):
-        """forum_name is defined"""
-        self.assertEqual(db_settings.forum_name, 'Misago')
-
-        with self.assertRaises(AttributeError):
-            db_settings.MISAGO_THREADS_PER_PAGE
-
-
-class GatewaySettingsTests(TestCase):
-    def tearDown(self):
-        cache.clear()
-        threadstore.clear()
-
-    def test_get_existing_setting(self):
-        """forum_name is defined"""
-        self.assertEqual(gateway.forum_name, db_settings.forum_name)
-        self.assertEqual(gateway.INSTALLED_APPS, dj_settings.INSTALLED_APPS)
-        self.assertEqual(gateway.MISAGO_THREADS_PER_PAGE, defaults.MISAGO_THREADS_PER_PAGE)
-
-        with self.assertRaises(AttributeError):
-            gateway.LoremIpsum
-
-    @override_settings(MISAGO_THREADS_PER_PAGE=1234)
-    def test_override_file_setting(self):
-        """file settings are overrideable"""
-        self.assertEqual(gateway.MISAGO_THREADS_PER_PAGE, 1234)
-
-    def test_setting_public(self):
-        """get_public_settings returns public settings"""
-        test_group = {
-            'key': 'test_group',
-            'name': "Test settings",
-            'description': "Those are test settings.",
-            'settings': [
-                {
-                    'setting': 'fish_name',
-                    'name': "Fish's name",
-                    'value': "Public Eric",
-                    'field_extra': {
-                        'min_length': 2,
-                        'max_length': 255,
-                    },
-                    'is_public': True,
-                },
-                {
-                    'setting': 'private_fish_name',
-                    'name': "Fish's name",
-                    'value': "Private Eric",
-                    'field_extra': {
-                        'min_length': 2,
-                        'max_length': 255,
-                    },
-                    'is_public': False,
-                },
-            ],
-        }
-
-        migrate_settings_group(apps, test_group)
-
-        self.assertEqual(gateway.fish_name, 'Public Eric')
-        self.assertEqual(gateway.private_fish_name, 'Private Eric')
-
-        public_settings = gateway.get_public_settings().keys()
-        self.assertIn('fish_name', public_settings)
-        self.assertNotIn('private_fish_name', public_settings)
-
-    def test_setting_lazy(self):
-        """lazy settings work"""
-        test_group = {
-            'key': 'test_group',
-            'name': "Test settings",
-            'description': "Those are test settings.",
-            'settings': [
-                {
-                    'setting': 'fish_name',
-                    'name': "Fish's name",
-                    'value': "Greedy Eric",
-                    'field_extra': {
-                        'min_length': 2,
-                        'max_length': 255,
-                    },
-                    'is_lazy': False,
-                },
-                {
-                    'setting': 'lazy_fish_name',
-                    'name': "Fish's name",
-                    'value': "Lazy Eric",
-                    'field_extra': {
-                        'min_length': 2,
-                        'max_length': 255,
-                    },
-                    'is_lazy': True,
-                },
-                {
-                    'setting': 'lazy_empty_setting',
-                    'name': "Fish's name",
-                    'field_extra': {
-                        'min_length': 2,
-                        'max_length': 255,
-                    },
-                    'is_lazy': True,
-                },
-            ],
-        }
-
-        migrate_settings_group(apps, test_group)
-
-        self.assertTrue(gateway.lazy_fish_name)
-        self.assertTrue(db_settings.lazy_fish_name)
-
-        self.assertTrue(gateway.lazy_fish_name)
-        self.assertEqual(gateway.get_lazy_setting('lazy_fish_name'), 'Lazy Eric')
-        self.assertTrue(db_settings.lazy_fish_name)
-        self.assertEqual(db_settings.get_lazy_setting('lazy_fish_name'), 'Lazy Eric')
-
-        self.assertTrue(gateway.lazy_empty_setting is None)
-        self.assertTrue(db_settings.lazy_empty_setting is None)
-        with self.assertRaises(ValueError):
-            db_settings.get_lazy_setting('fish_name')

+ 2 - 2
misago/conf/views.py

@@ -4,7 +4,7 @@ from django.utils.translation import gettext as _
 
 from misago.admin.views import render as mi_render
 
-from . import db_settings
+from .cache import clear_settings_cache
 from .forms import ChangeSettingsForm
 from .models import SettingsGroup
 
@@ -44,7 +44,7 @@ def group(request, key):
                 setting.value = new_values[setting.setting]
                 setting.save(update_fields=['dry_value'])
 
-            db_settings.flush_cache()
+            clear_settings_cache()
 
             messages.success(request, _("Changes in settings have been saved!"))
             return redirect('misago:admin:system:settings:group', key=key)

+ 1 - 4
misago/threads/migrations/0004_update_settings.py

@@ -1,7 +1,6 @@
 from django.db import migrations
 
-from misago.conf.migrationutils import delete_settings_cache, migrate_settings_group
-
+from misago.conf.migrationutils import migrate_settings_group
 
 _ = lambda s: s
 
@@ -68,8 +67,6 @@ def update_threads_settings(apps, schema_editor):
         }
     )
 
-    delete_settings_cache()
-
 
 class Migration(migrations.Migration):
 

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

@@ -21,7 +21,7 @@ UserModel = get_user_model()
 
 @csrf_protect
 def create_endpoint(request):
-    if settings.account_activation == 'closed':
+    if request.settings.account_activation == 'closed':
         raise PermissionDenied(_("New users registrations are currently closed."))
 
     form = RegisterForm(
@@ -40,9 +40,9 @@ def create_endpoint(request):
         return Response(form.errors, status=status.HTTP_400_BAD_REQUEST)
 
     activation_kwargs = {}
-    if settings.account_activation == 'user':
+    if request.settings.account_activation == 'user':
         activation_kwargs = {'requires_activation': UserModel.ACTIVATION_USER}
-    elif settings.account_activation == 'admin':
+    elif request.settings.account_activation == 'admin':
         activation_kwargs = {'requires_activation': UserModel.ACTIVATION_ADMIN}
 
     try:

+ 9 - 7
misago/users/api/userendpoints/username.py

@@ -4,7 +4,6 @@ from rest_framework.response import Response
 from django.db import IntegrityError
 from django.utils.translation import gettext as _
 
-from misago.conf import settings
 from misago.users.namechanges import get_username_options
 from misago.users.serializers import ChangeUsernameSerializer
 
@@ -19,7 +18,7 @@ def username_endpoint(request):
 
 def get_username_options_from_request(request):
     return get_username_options(
-        settings, request.user, request.user_acl
+        request.settings, request.user, request.user_acl
     )
 
 
@@ -41,9 +40,9 @@ def change_username(request):
         )
 
     serializer = ChangeUsernameSerializer(
-        data=request.data, context={'user': request.user}
+        data=request.data,
+        context={'settings': request.settings, 'user': request.user},
     )
-
     if serializer.is_valid():
         try:
             serializer.change_username(changed_by=request.user)
@@ -74,7 +73,10 @@ def change_username(request):
 
 def moderate_username_endpoint(request, profile):
     if request.method == 'POST':
-        serializer = ChangeUsernameSerializer(data=request.data, context={'user': profile})
+        serializer = ChangeUsernameSerializer(
+            data=request.data,
+            context={'settings': request.settings, 'user': profile},
+        )
 
         if serializer.is_valid():
             try:
@@ -99,6 +101,6 @@ def moderate_username_endpoint(request, profile):
             )
     else:
         return Response({
-            'length_min': settings.username_length_min,
-            'length_max': settings.username_length_max,
+            'length_min': request.settings.username_length_min,
+            'length_max': request.settings.username_length_max,
         })

+ 8 - 5
misago/users/forms/admin.py

@@ -28,9 +28,15 @@ class UserBaseForm(forms.ModelForm):
         model = UserModel
         fields = ['username', 'email', 'title']
 
+    def __init__(self, *args, **kwargs):
+        self.request = kwargs.pop('request')
+        self.settings = self.request.settings
+
+        super().__init__(*args, **kwargs)
+
     def clean_username(self):
         data = self.cleaned_data['username']
-        validate_username(data, exclude=self.instance)
+        validate_username(self.settings, data, exclude=self.instance)
         return data
 
     def clean_email(self):
@@ -191,10 +197,7 @@ class EditUserForm(UserBaseForm):
         ]
 
     def __init__(self, *args, **kwargs):
-        self.request = kwargs.pop('request')
-
         super().__init__(*args, **kwargs)
-
         profilefields.add_fields_to_admin_form(self.request, self.instance, self)
 
     def get_profile_fields_groups(self):
@@ -214,7 +217,7 @@ class EditUserForm(UserBaseForm):
     def clean_signature(self):
         data = self.cleaned_data['signature']
 
-        length_limit = settings.signature_length_max
+        length_limit = self.settings.signature_length_max
         if len(data) > length_limit:
             raise forms.ValidationError(
                 ngettext(

+ 8 - 5
misago/users/forms/register.py

@@ -4,16 +4,18 @@ from django.contrib.auth.password_validation import validate_password
 from django.core.exceptions import ValidationError
 from django.utils.translation import gettext as _
 
-from misago.users import validators
 from misago.users.bans import get_email_ban, get_ip_ban, get_username_ban
+from misago.users.validators import (
+    validate_email, validate_new_registration, validate_username
+)
 
 
 UserModel = get_user_model()
 
 
 class BaseRegisterForm(forms.Form):
-    username = forms.CharField(validators=[validators.validate_username])
-    email = forms.CharField(validators=[validators.validate_email])
+    username = forms.CharField()
+    email = forms.CharField(validators=[validate_email])
 
     terms_of_service = forms.IntegerField(required=False)
     privacy_policy = forms.IntegerField(required=False)
@@ -26,6 +28,7 @@ class BaseRegisterForm(forms.Form):
     def clean_username(self):
         data = self.cleaned_data['username']
 
+        validate_username(self.request.settings, data)
         ban = get_username_ban(data, registration_only=True)
         if ban:
             if ban.user_message:
@@ -67,7 +70,7 @@ class SocialAuthRegisterForm(BaseRegisterForm):
         self.clean_agreements(cleaned_data)
         self.raise_if_ip_banned()
 
-        validators.validate_new_registration(self.request, cleaned_data, self)
+        validate_new_registration(self.request, cleaned_data, self)
 
         return cleaned_data
 
@@ -99,6 +102,6 @@ class RegisterForm(BaseRegisterForm):
         except forms.ValidationError as e:
             self.add_error('password', e)
 
-        validators.validate_new_registration(self.request, cleaned_data, self.add_error)
+        validate_new_registration(self.request, cleaned_data, self.add_error)
 
         return cleaned_data

+ 1 - 2
misago/users/management/commands/createsuperuser.py

@@ -18,7 +18,6 @@ from misago.conf.dynamicsettings import DynamicSettings
 from misago.users.setupnewuser import setup_new_user
 from misago.users.validators import validate_email, validate_username
 
-
 User = get_user_model()
 
 
@@ -89,7 +88,7 @@ class Command(BaseCommand):
         if username is not None:
             try:
                 username = username.strip()
-                validate_username(username)
+                validate_username(settings, username)
             except ValidationError as e:
                 self.stderr.write('\n'.join(e.messages))
                 username = None

+ 1 - 4
misago/users/migrations/0006_update_settings.py

@@ -1,8 +1,7 @@
 # Generated by Django 1.10.5 on 2017-02-05 14:34
 from django.db import migrations
 
-from misago.conf.migrationutils import delete_settings_cache, migrate_settings_group
-
+from misago.conf.migrationutils import migrate_settings_group
 
 _ = lambda s: s
 
@@ -165,8 +164,6 @@ def update_users_settings(apps, schema_editor):
         }
     )
 
-    delete_settings_cache()
-
 
 class Migration(migrations.Migration):
 

+ 7 - 5
misago/users/serializers/options.py

@@ -59,20 +59,22 @@ class ChangeUsernameSerializer(serializers.Serializer):
 
     def validate(self, data):
         username = data.get('username')
-
         if not username:
             raise serializers.ValidationError(_("Enter new username."))
 
-        if username == self.context['user'].username:
+        user = self.context['user']
+        if username == user.username:
             raise serializers.ValidationError(_("New username is same as current one."))
 
-        validate_username(username)
+        settings = self.context['settings']
+        validate_username(settings, username)
 
         return data
 
     def change_username(self, changed_by):
-        self.context['user'].set_username(self.validated_data['username'], changed_by=changed_by)
-        self.context['user'].save(update_fields=['username', 'slug'])
+        user = self.context['user']
+        user.set_username(self.validated_data['username'], changed_by=changed_by)
+        user.save(update_fields=['username', 'slug'])
 
 
 class ChangePasswordSerializer(serializers.Serializer):

+ 8 - 4
misago/users/social/pipeline.py

@@ -8,7 +8,6 @@ from django.urls import reverse
 from django.utils.translation import gettext as _
 from social_core.pipeline.partial import partial
 
-from misago.conf import settings
 from misago.core.exceptions import SocialAuthFailed, SocialAuthBanned
 from misago.legal.models import Agreement
 
@@ -98,6 +97,8 @@ def get_username(strategy, details, backend, user=None, *args, **kwargs):
     if user:
         return None
 
+    settings = strategy.request.settings
+
     username = perpare_username(details.get('username', ''))
     full_name = perpare_username(details.get('full_name', ''))
     first_name = perpare_username(details.get('first_name', ''))
@@ -127,7 +128,7 @@ def get_username(strategy, details, backend, user=None, *args, **kwargs):
 
     for name in filter(bool, names_to_try):
         try:
-            validate_username(name)
+            validate_username(settings, name)
             return {'clean_username': name}
         except ValidationError:
             pass
@@ -139,6 +140,8 @@ def create_user(strategy, details, backend, user=None, *args, **kwargs):
         return None
     
     request = strategy.request
+    settings = request.settings
+
     email = details.get('email')
     username = kwargs.get('clean_username')
     
@@ -165,7 +168,7 @@ def create_user(strategy, details, backend, user=None, *args, **kwargs):
         **activation_kwargs
     )
 
-    setup_new_user(request.settings, new_user)
+    setup_new_user(settings, new_user)
     send_welcome_email(request, new_user)
 
     return {'user': new_user, 'is_new': True}
@@ -178,6 +181,7 @@ def create_user_with_form(strategy, details, backend, user=None, *args, **kwargs
         return None
 
     request = strategy.request
+    settings = request.settings
     backend_name = get_social_auth_backend_name(backend.name)
 
     if request.method == 'POST':
@@ -210,7 +214,7 @@ def create_user_with_form(strategy, details, backend, user=None, *args, **kwargs
                 joined_from_ip=request.user_ip,
                 **activation_kwargs
             )
-            setup_new_user(request.settings, new_user)
+            setup_new_user(settings, new_user)
         except IntegrityError:
             return JsonResponse({'__all__': _("Please try resubmitting the form.")}, status=400)
 

+ 1 - 1
misago/users/tests/test_new_user_setup.py

@@ -3,7 +3,7 @@ from django.test import TestCase
 
 from misago.cache.versions import get_cache_versions
 from misago.conf.dynamicsettings import DynamicSettings
-from misago.conf.tests import override_dynamic_settings
+from misago.conf.test import override_dynamic_settings
 
 from misago.users.setupnewuser import (
     set_default_subscription_options, setup_new_user

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

@@ -3,7 +3,7 @@ import json
 from django.contrib.auth import get_user_model
 
 from misago.acl.test import patch_user_acl
-from misago.conf import settings
+from misago.conf.test import override_dynamic_settings
 from misago.users.testutils import AuthenticatedUserTestCase
 
 
@@ -17,6 +17,7 @@ class UserUsernameTests(AuthenticatedUserTestCase):
         super().setUp()
         self.link = '/api/users/%s/username/' % self.user.pk
 
+    @override_dynamic_settings(username_length_min=2, username_length_max=4)
     def test_get_change_username_options(self):
         """get to API returns options"""
         response = self.client.get(self.link)
@@ -25,8 +26,8 @@ class UserUsernameTests(AuthenticatedUserTestCase):
         response_json = response.json()
 
         self.assertIsNotNone(response_json['changes_left'])
-        self.assertEqual(response_json['length_min'], settings.username_length_min)
-        self.assertEqual(response_json['length_max'], settings.username_length_max)
+        self.assertEqual(response_json['length_min'], 2)
+        self.assertEqual(response_json['length_max'], 4)
         self.assertIsNone(response_json['next_on'])
 
         for i in range(response_json['changes_left']):
@@ -133,14 +134,15 @@ class UserUsernameModerationTests(AuthenticatedUserTestCase):
         })
 
     @patch_user_acl({'can_rename_users': 1})
+    @override_dynamic_settings(username_length_min=3, username_length_max=12)
     def test_moderate_username(self):
         """moderate username"""
         response = self.client.get(self.link)
         self.assertEqual(response.status_code, 200)
 
         options = response.json()
-        self.assertEqual(options['length_min'], settings.username_length_min)
-        self.assertEqual(options['length_max'], settings.username_length_max)
+        self.assertEqual(options['length_min'], 3)
+        self.assertEqual(options['length_max'], 12)
 
         response = self.client.post(
             self.link,

+ 12 - 8
misago/users/tests/test_validators.py

@@ -1,8 +1,9 @@
+from unittest.mock import Mock
+
 from django.contrib.auth import get_user_model
 from django.core.exceptions import ValidationError
 from django.test import TestCase
 
-from misago.conf import settings
 from misago.users.models import Ban
 from misago.users.validators import (
     validate_email, validate_email_available, validate_email_banned, validate_gmail_email,
@@ -56,14 +57,15 @@ class ValidateEmailTests(TestCase):
 class ValidateUsernameTests(TestCase):
     def test_validate_username(self):
         """validate_username has no crashes"""
-        validate_username('LeBob')
+        settings = Mock(username_length_min=1, username_length_max=5)
+        validate_username(settings, 'LeBob')
         with self.assertRaises(ValidationError):
-            validate_username('*')
+            validate_username(settings, '*')
 
 
 class ValidateUsernameAvailableTests(TestCase):
     def setUp(self):
-        self.test_user = UserModel.objects.create_user('EricTheFish', 'eric@test.com', 'pass123')
+        self.test_user = UserModel.objects.create_user('EricTheFish', 'eric@test.com')
 
     def test_valid_name(self):
         """validate_username_available allows available names"""
@@ -117,15 +119,17 @@ class ValidateUsernameContentTests(TestCase):
 class ValidateUsernameLengthTests(TestCase):
     def test_valid_name(self):
         """validate_username_length allows valid names"""
-        validate_username_length('a' * settings.username_length_min)
-        validate_username_length('a' * settings.username_length_max)
+        settings = Mock(username_length_min=1, username_length_max=5)
+        validate_username_length(settings, 'a' * settings.username_length_min)
+        validate_username_length(settings, 'a' * settings.username_length_max)
 
     def test_invalid_name(self):
         """validate_username_length disallows invalid names"""
+        settings = Mock(username_length_min=1, username_length_max=5)
         with self.assertRaises(ValidationError):
-            validate_username_length('a' * (settings.username_length_min - 1))
+            validate_username_length(settings, 'a' * (settings.username_length_min - 1))
         with self.assertRaises(ValidationError):
-            validate_username_length('a' * (settings.username_length_max + 1))
+            validate_username_length(settings, 'a' * (settings.username_length_max + 1))
 
 
 class ValidateGmailEmailTests(TestCase):

+ 4 - 4
misago/users/validators.py

@@ -51,9 +51,9 @@ def validate_email_banned(value):
 
 
 # Username validators
-def validate_username(value, exclude=None):
+def validate_username(settings, value, exclude=None):
     """shortcut function that does complete validation of username"""
-    validate_username_length(value)
+    validate_username_length(settings, value)
     validate_username_content(value)
     validate_username_available(value, exclude)
     validate_username_banned(value)
@@ -83,7 +83,7 @@ def validate_username_content(value):
         raise ValidationError(_("Username can only contain latin alphabet letters and digits."))
 
 
-def validate_username_length(value):
+def validate_username_length(settings, value):
     if len(value) < settings.username_length_min:
         message = ngettext(
             "Username must be at least %(limit_value)s character long.",
@@ -143,7 +143,7 @@ validators_list = settings.MISAGO_NEW_REGISTRATIONS_VALIDATORS
 REGISTRATION_VALIDATORS = list(map(import_string, validators_list))
 
 
-def raise_validation_error(fieldname, validation_error):
+def raise_validation_error(*_):
     raise ValidationError()
 
 

+ 12 - 2
misago/users/views/admin/users.py

@@ -9,7 +9,6 @@ from misago.acl.useracl import get_user_acl
 from misago.admin.auth import start_admin_session
 from misago.admin.views import generic
 from misago.categories.models import Category
-from misago.conf import settings
 from misago.core.mail import mail_users
 from misago.core.pgutils import chunk_queryset
 from misago.threads.models import Thread
@@ -119,7 +118,7 @@ class UsersList(UserAdmin, generic.ListView):
             queryset.update(requires_activation=User.ACTIVATION_NONE)
 
             subject = _("Your account on %(forum_name)s forums has been activated")
-            mail_subject = subject % {'forum_name': settings.forum_name}
+            mail_subject = subject % {'forum_name': request.settings.forum_name}
 
             mail_users(
                 inactive_users,
@@ -252,6 +251,17 @@ class NewUser(UserAdmin, generic.ModelFormView):
     template = 'new.html'
     message_submit = _('New user "%(user)s" has been registered.')
 
+    def initialize_form(self, form, request, target):
+        if request.method == 'POST':
+            return form(
+                request.POST,
+                request.FILES,
+                instance=target,
+                request=request,
+            )
+        else:
+            return form(instance=target, request=request)
+            
     def handle_form(self, form, request, target):
         new_user = User.objects.create_user(
             form.cleaned_data['username'],