Browse Source

Adding new user model to Misago

Rafał Pitoń 11 years ago
parent
commit
1e8da646a2

+ 6 - 1
misago/conf/defaults.py

@@ -78,6 +78,7 @@ INSTALLED_APPS = (
     'floppyforms',
     'misago.core',
     'misago.conf',
+    'misago.users',
 )
 
 MIDDLEWARE_CLASSES = (
@@ -129,10 +130,14 @@ USE_TZ = True
 TIME_ZONE = 'UTC'
 
 
-# Use Misago's CSRF Failure Page
+# Use Misago CSRF Failure Page
 CSRF_FAILURE_VIEW = 'misago.core.errorpages.csrf_failure'
 
 
+# Use Misago user model
+AUTH_USER_MODEL = 'users.User'
+
+
 # How many e-mails should be sent in single step.
 # This is used for conserving memory usage when mailing many users at same time
 

+ 1 - 0
misago/core/tests/test_utils.py

@@ -48,6 +48,7 @@ class SlugifyTests(TestCase):
             (u'J0n', u'j0n'),
             (u'An###ne', u'anne'),
             (u'S**t', u'st'),
+            (u'Łók', u'lok'),
         )
 
         for original, slug in test_cases:

+ 0 - 0
misago/users/__init__.py


+ 1 - 0
misago/users/models/__init__.py

@@ -0,0 +1 @@
+from misago.users.models.usermodel import User

+ 90 - 0
misago/users/models/usermodel.py

@@ -0,0 +1,90 @@
+from django.contrib.auth.models import (AbstractBaseUser, PermissionsMixin,
+                                        UserManager as BaseUserManager)
+from django.db import models
+from django.utils import timezone
+from django.utils.translation import ugettext_lazy as _
+from misago.core.utils import slugify
+from misago.core.validators import validate_sluggable
+from misago.users.utils import hash_email
+
+
+class UserManager(BaseUserManager):
+    def create_user(self, username, email, password=None, **extra_fields):
+        if not email:
+            raise ValueError(_('User must have an email address.'))
+
+        now = timezone.now()
+        user = self.model(is_staff=False, is_active=True, is_superuser=False,
+                          last_login=now, joined_on=now, **extra_fields)
+
+        user.set_username(username)
+        user.set_email(email)
+        user.set_password(password)
+        user.save(using=self._db)
+        return user
+
+    def create_superuser(self, username, email, password):
+        user = self.create_user(username, email, password=password)
+        user.is_staff = True
+        user.is_active = True
+        user.is_superuser = True
+        user.save(using=self._db)
+        return user
+
+    def get_by_username_or_email(self, login):
+        queryset = models.Q(username_slug=slugify(login))
+        queryset = queryset | models.Q(email_hash=hash_email(new_email))
+        return self.get(queryset)
+
+
+class User(AbstractBaseUser, PermissionsMixin):
+    """
+    Note that "username" field is purely for shows.
+    When searching users by their names, always use lowercased string
+    and username_slug field instead that is normalized around DB engines
+    differences in case handling.
+    """
+    username = models.CharField(max_length=30)
+    username_slug = models.CharField(max_length=30, unique=True)
+    """
+    Misago stores user email in two fields:
+    "email" holds normalized email address
+    "email_hash" is lowercase hash of email address used to identify account
+    as well as enforcing on database level that no more than one user can be
+    using one email address
+    """
+    email = models.EmailField(max_length=255, db_index=True)
+    email_hash = models.CharField(max_length=32, unique=True)
+    joined_on = models.DateTimeField(_('joined on'), default=timezone.now)
+    is_staff = models.BooleanField(
+        _('staff status'), default=False, db_index=True,
+        help_text=_('Designates whether the user can log into admin sites.'))
+    is_active = True
+
+    USERNAME_FIELD = 'username_slug'
+    REQUIRED_FIELDS = ['email']
+
+    objects = UserManager()
+
+    class Meta:
+        app_label = 'users'
+
+    def get_username(self):
+        """
+        Dirty hack: return real username instead of normalized slug
+        """
+        return self.username
+
+    def get_full_name(self):
+        return self.username
+
+    def get_short_name(self):
+        return self.username
+
+    def set_username(self, new_username):
+        self.username = new_username
+        self.username_slug = slugify(new_username)
+
+    def set_email(self, new_email):
+        self.email = UserManager.normalize_email(new_email)
+        self.email_hash = hash_email(new_email)

+ 0 - 0
misago/users/tests/__init__.py


+ 24 - 0
misago/users/tests/test_user_model.py

@@ -0,0 +1,24 @@
+from django.test import TestCase
+from misago.users.models import User
+
+
+class UserModelTests(TestCase):
+    def test_set_username(self):
+        """set_username sets username and slug on model"""
+        user = User()
+
+        user.set_username('Boberson')
+        self.assertEqual(user.username, 'Boberson')
+        self.assertEqual(user.username_slug, 'boberson')
+
+        self.assertEqual(user.get_username(), 'Boberson')
+        self.assertEqual(user.get_full_name(), 'Boberson')
+        self.assertEqual(user.get_short_name(), 'Boberson')
+
+    def test_set_email(self):
+        """set_email sets email and hash on model"""
+        user = User()
+
+        user.set_email('bOb@TEst.com')
+        self.assertEqual(user.email, 'bOb@test.com')
+        self.assertTrue(user.email_hash)

+ 9 - 0
misago/users/tests/test_utils.py

@@ -0,0 +1,9 @@
+from django.test import TestCase
+from misago.users.utils import hash_email
+
+
+class UserModelTests(TestCase):
+    def test_hash_email_works(self):
+        """hash email produces repeatable outcomes"""
+        self.assertEqual(hash_email('abc@test.com'),
+                         hash_email('aBc@tEst.cOm'))

+ 8 - 0
misago/users/utils.py

@@ -0,0 +1,8 @@
+import hashlib
+
+
+def hash_email(email):
+    email = email.lower()
+    while len(email) < 15:
+        email *= 2
+    return hashlib.sha256(email).hexdigest()