123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509 |
- import hashlib
- import math
- from random import choice
- from path import path
- from django.conf import settings
- from django.contrib.auth.hashers import (
- check_password, make_password, is_password_usable, UNUSABLE_PASSWORD)
- from django.core.cache import cache, InvalidCacheBackendError
- from django.core.exceptions import ValidationError
- from django.core.mail import EmailMultiAlternatives
- from django.db import models
- from django.template import RequestContext
- from django.utils import timezone as tz_util
- from django.utils.translation import ugettext_lazy as _
- from misago.acl.builder import build_acl
- from misago.monitor.monitor import Monitor
- from misago.roles.models import Role
- from misago.settings.settings import Settings as DBSettings
- from misago.users.validators import validate_username, validate_password, validate_email
- from misago.utils import get_random_string, slugify
- from misago.utils.avatars import avatar_size
- class UserManager(models.Manager):
- """
- User Manager provides us with some additional methods for users
- """
- def get_blank_user(self):
- blank_user = User(
- join_date=tz_util.now(),
- join_ip='127.0.0.1'
- )
- return blank_user
-
- def resync_monitor(self, monitor):
- monitor['users'] = self.count()
- monitor['users_inactive'] = self.filter(activation__gt=0).count()
- last_user = self.latest('id')
- monitor['last_user'] = last_user.pk
- monitor['last_user_name'] = last_user.username
- monitor['last_user_slug'] = last_user.username_slug
-
- def create_user(self, username, email, password, timezone=False, ip='127.0.0.1', no_roles=False, activation=0, request=False):
- token = ''
- if activation > 0:
- token = get_random_string(12)
-
- try:
- db_settings = request.settings
- except AttributeError:
- db_settings = DBSettings()
-
- if timezone == False:
- timezone = db_settings['default_timezone']
-
- # Get first rank
- try:
- from misago.ranks.models import Rank
- default_rank = Rank.objects.filter(special=0).order_by('order')[0]
- except IndexError:
- default_rank = None
-
- # Store user in database
- new_user = User(
- last_sync=tz_util.now(),
- join_date=tz_util.now(),
- join_ip=ip,
- activation=activation,
- token=token,
- timezone=timezone,
- rank=default_rank,
- )
-
- new_user.set_username(username)
- new_user.set_email(email)
- new_user.set_password(password)
- new_user.full_clean()
- new_user.default_avatar(db_settings)
- new_user.save(force_insert=True)
-
- # Set user roles?
- if not no_roles:
- from misago.roles.models import Role
- new_user.roles.add(Role.objects.get(token='registered'))
- new_user.make_acl_key()
- new_user.save(force_update=True)
-
- # Load monitor
- try:
- monitor = request.monitor
- except AttributeError:
- monitor = Monitor()
-
- # Update forum stats
- if activation == 0:
- monitor['users'] = int(monitor['users']) + 1
- monitor['last_user'] = new_user.pk
- monitor['last_user_name'] = new_user.username
- monitor['last_user_slug'] = new_user.username_slug
- else:
- monitor['users_inactive'] = int(monitor['users_inactive']) + 1
-
- # Return new user
- return new_user
-
- def get_by_email(self, email):
- return self.get(email_hash=hashlib.md5(email).hexdigest())
-
- def filter_stats(self, start, end):
- return self.filter(join_date__gte=start).filter(join_date__lte=end)
-
-
- class User(models.Model):
- """
- Misago User model
- """
- username = models.CharField(max_length=255,validators=[validate_username])
- username_slug = models.SlugField(max_length=255,unique=True,
- error_messages={'unique': _("This user name is already in use by another user.")})
- email = models.EmailField(max_length=255,validators=[validate_email])
- email_hash = models.CharField(max_length=32,unique=True,
- error_messages={'unique': _("This email address is already in use by another user.")})
- password = models.CharField(max_length=255)
- password_date = models.DateTimeField()
- avatar_type = models.CharField(max_length=10,null=True,blank=True)
- avatar_image = models.CharField(max_length=255,null=True,blank=True)
- avatar_original = models.CharField(max_length=255,null=True,blank=True)
- avatar_temp = models.CharField(max_length=255,null=True,blank=True)
- signature = models.TextField(null=True,blank=True)
- signature_preparsed = models.TextField(null=True,blank=True)
- join_date = models.DateTimeField()
- join_ip = models.GenericIPAddressField()
- join_agent = models.TextField(null=True,blank=True)
- last_date = models.DateTimeField(null=True,blank=True)
- last_ip = models.GenericIPAddressField(null=True,blank=True)
- last_agent = models.TextField(null=True,blank=True)
- hide_activity = models.PositiveIntegerField(default=0)
- alert_ats = models.PositiveIntegerField(default=0)
- allow_pms = models.PositiveIntegerField(default=0)
- receive_newsletters = models.BooleanField(default=True)
- threads = models.PositiveIntegerField(default=0)
- posts = models.PositiveIntegerField(default=0)
- votes = models.PositiveIntegerField(default=0)
- karma_given_p = models.PositiveIntegerField(default=0)
- karma_given_n = models.PositiveIntegerField(default=0)
- karma_p = models.PositiveIntegerField(default=0)
- karma_n = models.PositiveIntegerField(default=0)
- following = models.PositiveIntegerField(default=0)
- followers = models.PositiveIntegerField(default=0)
- score = models.IntegerField(default=0,db_index=True)
- rank = models.ForeignKey('ranks.Rank',null=True,blank=True,on_delete=models.SET_NULL)
- last_sync = models.DateTimeField(null=True,blank=True)
- follows = models.ManyToManyField('self',related_name='follows_set',symmetrical=False)
- ignores = models.ManyToManyField('self',related_name='ignores_set',symmetrical=False)
- title = models.CharField(max_length=255,null=True,blank=True)
- last_post = models.DateTimeField(null=True,blank=True)
- last_search = models.DateTimeField(null=True,blank=True)
- alerts = models.PositiveIntegerField(default=0)
- alerts_new = models.PositiveIntegerField(default=0)
- activation = models.IntegerField(default=0)
- token = models.CharField(max_length=12,null=True,blank=True)
- avatar_ban = models.BooleanField(default=False)
- avatar_ban_reason_user = models.TextField(null=True,blank=True)
- avatar_ban_reason_admin = models.TextField(null=True,blank=True)
- signature_ban = models.BooleanField(default=False)
- signature_ban_reason_user = models.TextField(null=True,blank=True)
- signature_ban_reason_admin = models.TextField(null=True,blank=True)
- timezone = models.CharField(max_length=255,default='utc')
- roles = models.ManyToManyField('roles.Role')
- is_team = models.BooleanField(default=False,db_index=True)
- acl_key = models.CharField(max_length=12,null=True,blank=True)
-
- objects = UserManager()
-
- ACTIVATION_NONE = 0
- ACTIVATION_USER = 1
- ACTIVATION_ADMIN = 2
- ACTIVATION_CREDENTIALS = 3
-
- statistics_name = _('Users Registrations')
-
- def is_god(self):
- try:
- return self.is_god_cache
- except AttributeError:
- for user in settings.ADMINS:
- if user[1].lower() == self.email:
- self.is_god_cache = True
- return True
- self.is_god_cache = False
- return False
-
- def is_anonymous(self):
- return False
-
- def is_authenticated(self):
- return True
-
- def is_crawler(self):
- return False
- def is_protected(self):
- for role in self.roles.all():
- if role.protected:
- return True
- return False
-
- def lock_avatar(self):
- # Kill existing avatar and lock our ability to change it
- self.delete_avatar()
- self.avatar_ban = True
-
- # Pick new one from _locked gallery
- galleries = path(settings.STATICFILES_DIRS[0]).joinpath('avatars').joinpath('_locked')
- avatars_list = galleries.files('*.gif')
- avatars_list += galleries.files('*.jpg')
- avatars_list += galleries.files('*.jpeg')
- avatars_list += galleries.files('*.png')
- self.avatar_type = 'gallery'
- self.avatar_image = '/'.join(path(choice(avatars_list)).splitall()[-2:])
-
- def default_avatar(self, db_settings):
- if db_settings['default_avatar'] == 'gallery':
- try:
- avatars_list = []
- try:
- # First try, _default path
- galleries = path(settings.STATICFILES_DIRS[0]).joinpath('avatars').joinpath('_default')
- avatars_list += galleries.files('*.gif')
- avatars_list += galleries.files('*.jpg')
- avatars_list += galleries.files('*.jpeg')
- avatars_list += galleries.files('*.png')
- except Exception as e:
- pass
- # Second try, all paths
- if not avatars_list:
- avatars_list = []
- for directory in path(settings.STATICFILES_DIRS[0]).joinpath('avatars').dirs():
- if not directory[-7:] == '_locked' and not directory[-7:] == '_thumbs':
- avatars_list += directory.files('*.gif')
- avatars_list += directory.files('*.jpg')
- avatars_list += directory.files('*.jpeg')
- avatars_list += directory.files('*.png')
- if avatars_list:
- # Pick random avatar from list
- self.avatar_type = 'gallery'
- self.avatar_image = '/'.join(path(choice(avatars_list)).splitall()[-2:])
- return True
- except Exception as e:
- pass
-
- self.avatar_type = 'gravatar'
- self.avatar_image = None
- return True
- def delete_avatar_temp(self):
- if self.avatar_temp:
- try:
- av_file = path(settings.MEDIA_ROOT + 'avatars/' + self.avatar_temp)
- if not av_file.isdir():
- av_file.remove()
- except Exception:
- pass
-
- self.avatar_temp = None
- def delete_avatar_original(self):
- if self.avatar_original:
- try:
- av_file = path(settings.MEDIA_ROOT + 'avatars/' + self.avatar_original)
- if not av_file.isdir():
- av_file.remove()
- except Exception:
- pass
-
- self.avatar_original = None
- def delete_avatar_image(self):
- if self.avatar_image:
- for size in settings.AVATAR_SIZES[1:]:
- try:
- av_file = path(settings.MEDIA_ROOT + 'avatars/' + str(size) + '_' + self.avatar_image)
- if not av_file.isdir():
- av_file.remove()
- except Exception:
- pass
- try:
- av_file = path(settings.MEDIA_ROOT + 'avatars/' + self.avatar_image)
- if not av_file.isdir():
- av_file.remove()
- except Exception:
- pass
-
- self.avatar_image = None
- def delete_avatar(self):
- self.delete_avatar_temp()
- self.delete_avatar_original()
- self.delete_avatar_image()
-
- def delete_content(self):
- if self.pk:
- for model_obj in models.get_models():
- try:
- model_obj.objects.delete_user_content(self)
- except AttributeError:
- pass
-
- def delete(self, *args, **kwargs):
- self.delete_avatar()
- super(User, self).delete(*args, **kwargs)
-
- def set_username(self, username):
- self.username = username.strip()
- self.username_slug = slugify(username)
-
- if self.pk:
- for model_obj in models.get_models():
- try:
- model_obj.objects.update_username(self)
- except AttributeError:
- pass
-
- def is_username_valid(self, e):
- try:
- raise ValidationError(e.message_dict['username'])
- except KeyError:
- pass
- try:
- raise ValidationError(e.message_dict['username_slug'])
- except KeyError:
- pass
-
- def is_email_valid(self, e):
- try:
- raise ValidationError(e.message_dict['email'])
- except KeyError:
- pass
- try:
- raise ValidationError(e.message_dict['email_hash'])
- except KeyError:
- pass
-
- def is_password_valid(self, e):
- try:
- raise ValidationError(e.message_dict['password'])
- except KeyError:
- pass
-
- def set_email(self, email):
- self.email = email.strip().lower()
- self.email_hash = hashlib.md5(self.email).hexdigest()
-
- def set_password(self, raw_password):
- self.password_date = tz_util.now()
- self.password = make_password(raw_password.strip())
- def set_last_visit(self, ip, agent, hidden=False):
- self.last_date = tz_util.now()
- self.last_ip = ip
- self.last_agent = agent
- self.last_hide = hidden
- def check_password(self, raw_password, mobile=False):
- """
- Returns a boolean of whether the raw_password was correct. Handles
- hashing formats behind the scenes.
- """
- def setter(raw_password):
- self.set_password(raw_password)
- self.save()
-
- # Is standard password allright?
- if check_password(raw_password, self.password, setter):
- return True
-
- # Check mobile password?
- if mobile:
- raw_password = raw_password[:1].lower() + raw_password[1:]
- else:
- password_reversed = u''
- for c in raw_password:
- r = c.upper()
- if r == c:
- r = c.lower()
- password_reversed += r
- raw_password = password_reversed
- return check_password(raw_password, self.password, setter)
-
- def get_roles(self):
- return self.roles.all()
-
- def make_acl_key(self, force=False):
- if not force and self.acl_key:
- return self.acl_key
- roles_ids = []
- for role in self.roles.all():
- roles_ids.append(str(role.pk))
- self.acl_key = 'acl_%s' % hashlib.md5('_'.join(roles_ids)).hexdigest()[0:8]
- return self.acl_key
-
- def get_acl(self, request):
- try:
- acl = cache.get(self.acl_key)
- if acl.version != request.monitor.acl_version:
- raise InvalidCacheBackendError()
- except AttributeError, InvalidCacheBackendError:
- # build acl cache
- acl = build_acl(request, self.get_roles())
- cache.set(self.acl_key, acl, 2592000)
- return acl
-
- def get_avatar(self, size=None):
- image_size = avatar_size(size) if size else None
-
- # Get uploaded avatar
- if self.avatar_type == 'upload':
- image_prefix = '%s_' % image_size if image_size else ''
- return settings.MEDIA_URL + 'avatars/' + image_prefix + self.avatar_image
-
- # Get gallery avatar
- if self.avatar_type == 'gallery':
- image_prefix = '_thumbs/%s/' % image_size if image_size else ''
- return settings.STATIC_URL + 'avatars/' + image_prefix + self.avatar_image
-
- # No avatar found, get gravatar
- if not image_size:
- image_size = settings.AVATAR_SIZES[0]
- return 'http://www.gravatar.com/avatar/%s?s=%s' % (hashlib.md5(self.email).hexdigest(), image_size)
-
- def get_title(self):
- if self.title:
- return self.title
- if self.rank:
- return self.rank.title
- return None
-
- def get_style(self):
- if self.rank:
- return self.rank.style
- return ''
-
- def email_user(self, request, template, subject, context={}):
- templates = request.theme.get_email_templates(template)
- context = RequestContext(request, context)
- context['author'] = context['user']
- context['user'] = self
-
- # Set message recipient
- if settings.DEBUG and settings.CATCH_ALL_EMAIL_ADDRESS:
- recipient = settings.CATCH_ALL_EMAIL_ADDRESS
- else:
- recipient = self.email
-
- # Build and send message
- email = EmailMultiAlternatives(subject, templates[0].render(context), settings.EMAIL_HOST_USER, [recipient])
- email.attach_alternative(templates[1].render(context), "text/html")
- email.send()
-
- def get_activation(self):
- activations = ['none', 'user', 'admin', 'credentials']
- return activations[self.activation]
-
- def get_date(self):
- return self.join_date
-
- def sync_user(self):
- pass
- class Guest(object):
- """
- Misago Guest dummy
- """
- is_team = False
-
- def is_anonymous(self):
- return True
-
- def is_authenticated(self):
- return False
-
- def is_crawler(self):
- return False
-
- def get_roles(self):
- return Role.objects.filter(token='guest')
-
- def make_acl_key(self):
- return 'acl_guest'
-
- class Crawler(Guest):
- """
- Misago Crawler dummy
- """
- is_team = False
-
- def __init__(self, username):
- self.username = username
-
- def is_anonymous(self):
- return True
-
- def is_authenticated(self):
- return False
-
- def is_crawler(self):
- return True
|