addons-server/apps/users/models.py

263 строки
9.4 KiB
Python
Исходник Обычный вид История

2010-01-05 00:41:32 +03:00
from datetime import datetime
2010-01-13 04:22:26 +03:00
import hashlib
import random
2010-02-12 21:46:44 +03:00
import re
2010-01-13 04:22:26 +03:00
import string
import time
2010-01-05 00:41:32 +03:00
2010-02-12 21:46:44 +03:00
from django.conf import settings
from django.contrib.auth.models import User as DjangoUser
from django.core.mail import send_mail
from django.db import models
from django.template import Context, loader
from django.utils.encoding import smart_unicode
2009-10-23 02:37:15 +04:00
import caching.base as caching
import commonware.log
from tower import ugettext as _
import amo
import amo.models
2010-02-24 02:26:11 +03:00
from amo.urlresolvers import reverse
from translations.fields import PurifiedField
2009-10-23 02:37:15 +04:00
log = commonware.log.getLogger('z.users')
2009-10-23 02:37:15 +04:00
2010-01-13 04:22:26 +03:00
def get_hexdigest(algorithm, salt, raw_password):
return hashlib.new(algorithm, salt + raw_password).hexdigest()
def rand_string(length):
return ''.join(random.choice(string.letters) for i in xrange(length))
def create_password(algorithm, raw_password):
salt = get_hexdigest(algorithm, rand_string(12), rand_string(12))[:64]
hsh = get_hexdigest(algorithm, salt, raw_password)
return '$'.join([algorithm, salt, hsh])
class UserManager(amo.models.ManagerBase):
def request_user(self):
return (self.extra(select={'request': 1})
.transform(UserProfile.request_user_transformer))
class UserProfile(amo.models.ModelBase):
2009-10-23 02:37:15 +04:00
2010-08-05 00:49:44 +04:00
nickname = models.CharField(max_length=255, default='', null=True,
blank=True)
2010-08-19 21:42:01 +04:00
firstname = models.CharField(max_length=255, default='', blank=True)
lastname = models.CharField(max_length=255, default='', blank=True)
2010-08-05 00:49:44 +04:00
username = models.CharField(max_length=255, default='', unique=True)
display_name = models.CharField(max_length=255, default='', null=True,
blank=True)
2010-01-05 00:41:32 +03:00
password = models.CharField(max_length=255, default='')
email = models.EmailField(unique=True)
2010-01-05 00:41:32 +03:00
2010-06-24 01:20:46 +04:00
averagerating = models.CharField(max_length=255, blank=True, null=True)
bio = PurifiedField()
confirmationcode = models.CharField(max_length=255, default='',
blank=True)
2010-04-02 03:16:59 +04:00
deleted = models.BooleanField(default=False)
2010-01-05 00:41:32 +03:00
display_collections = models.BooleanField(default=False)
display_collections_fav = models.BooleanField(default=False)
emailhidden = models.BooleanField(default=False)
2010-04-02 03:16:59 +04:00
homepage = models.CharField(max_length=255, blank=True, default='')
location = models.CharField(max_length=255, blank=True, default='')
2010-06-24 01:20:46 +04:00
notes = models.TextField(blank=True, null=True)
2010-01-05 00:41:32 +03:00
notifycompat = models.BooleanField(default=True)
notifyevents = models.BooleanField(default=True)
2010-04-02 03:16:59 +04:00
occupation = models.CharField(max_length=255, default='', blank=True)
# This is essentially a "has_picture" flag right now
picture_type = models.CharField(max_length=75, default='', blank=True)
resetcode = models.CharField(max_length=255, default='', blank=True)
resetcode_expires = models.DateTimeField(default=datetime.now, null=True,
blank=True)
sandboxshown = models.BooleanField(default=False)
2010-01-05 00:41:32 +03:00
user = models.ForeignKey(DjangoUser, null=True, editable=False, blank=True)
2009-10-23 02:37:15 +04:00
objects = UserManager()
2009-10-23 02:37:15 +04:00
class Meta:
db_table = 'users'
2010-01-05 00:41:32 +03:00
def __unicode__(self):
2010-08-05 00:49:44 +04:00
return '%s: %s' % (self.id, self.display_name or self.username)
2010-01-05 00:41:32 +03:00
def get_url_path(self):
return reverse('users.profile', args=[self.id])
2009-10-23 02:37:15 +04:00
2010-08-13 20:24:36 +04:00
def flush_urls(self):
urls = ['*/user/%d/' % self.id]
return urls
2010-02-12 21:46:44 +03:00
@amo.cached_property
def addons_listed(self):
"""Public add-ons this user is listed as author of."""
return self.addons.valid().filter(addonuser__listed=True).distinct()
2010-02-12 21:46:44 +03:00
@property
def picture_url(self):
split_id = re.match(r'((\d*?)(\d{0,3}?))\d{1,3}$', str(self.id))
if not self.picture_type:
return settings.MEDIA_URL + '/img/zamboni/anon_user.png'
else:
return settings.USER_PIC_URL % (
split_id.group(2) or 0, split_id.group(1) or 0, self.id,
int(time.mktime(self.modified.timetuple())))
2010-02-12 21:46:44 +03:00
@amo.cached_property
def is_developer(self):
return bool(self.addons.filter(authors=self,
addonuser__listed=True)[:1])
2010-02-12 21:46:44 +03:00
@property
def welcome_name(self):
2010-08-05 00:49:44 +04:00
return self.display_name or self.username
@amo.cached_property
def reviews(self):
"""All reviews that are not dev replies."""
return self._reviews_all.filter(reply_to=None)
def anonymize(self):
log.info("User (%s: <%s>) is being anonymized." % (self, self.email))
self.email = ""
self.password = "sha512$Anonymous$Password"
self.firstname = ""
self.lastname = ""
2010-04-02 03:16:59 +04:00
self.nickname = None
2010-08-05 00:49:44 +04:00
self.username = "Anonymous-%s" % self.id # Can't be null
self.display_name = None
self.homepage = ""
self.deleted = True
self.picture_type = ""
self.save()
def save(self, force_insert=False, force_update=False, using=None):
# we have to fix stupid things that we defined poorly in remora
if self.resetcode_expires is None:
self.resetcode_expires = datetime.now()
super(UserProfile, self).save(force_insert, force_update, using)
2010-01-13 04:22:26 +03:00
def check_password(self, raw_password):
if '$' not in self.password:
valid = (get_hexdigest('md5', '', raw_password) == self.password)
if valid:
# Upgrade an old password.
self.set_password(raw_password)
self.save()
return valid
algo, salt, hsh = self.password.split('$')
return hsh == get_hexdigest(algo, salt, raw_password)
def set_password(self, raw_password, algorithm='sha512'):
self.password = create_password(algorithm, raw_password)
def email_confirmation_code(self):
log.debug("Sending account confirmation code for user (%s)", self)
url = "%s%s" % (settings.SITE_URL,
reverse('users.confirm',
args=[self.id, self.confirmationcode]))
domain = settings.DOMAIN
2010-04-01 07:49:13 +04:00
t = loader.get_template('users/email/confirm.ltxt')
c = {'domain': domain, 'url': url, }
send_mail(_("Please confirm your email address"),
t.render(Context(c)), None, [self.email])
def create_django_user(self):
"""Make a django.contrib.auth.User for this UserProfile."""
# Reusing the id will make our life easier, because we can use the
# OneToOneField as pk for Profile linked back to the auth.user
# in the future.
self.user = DjangoUser(id=self.pk)
2010-08-05 00:49:44 +04:00
self.user.first_name = ''
self.user.last_name = ''
self.user.username = self.email # f
self.user.email = self.email
self.user.password = self.password
self.user.date_joined = self.created
if self.groups.filter(rules='*:*').count():
self.user.is_superuser = self.user.is_staff = True
self.user.save()
self.save()
return self.user
def mobile_collection(self):
return self.special_collection(amo.COLLECTION_MOBILE,
defaults={'slug': 'mobile', 'listed': False,
'name': _('My Mobile Add-ons')})
def favorites_collection(self):
return self.special_collection(amo.COLLECTION_FAVORITES,
defaults={'slug': 'favorites', 'listed': False,
'name': _('My Favorite Add-ons')})
def special_collection(self, type_, defaults):
from bandwagon.models import Collection
c, _ = Collection.objects.get_or_create(
author=self, type=type_, defaults=defaults)
return c
@staticmethod
def request_user_transformer(users):
"""Adds extra goodies to a UserProfile (meant for request.amo_user)."""
# We don't want to cache these things on every UserProfile; they're
# only used by a user attached to a request.
from bandwagon.models import CollectionAddon
SPECIAL = amo.COLLECTION_SPECIAL_SLUGS.keys()
user = users[0]
qs = CollectionAddon.objects.filter(
collection__author=user, collection__type__in=SPECIAL)
addons = dict((type_, []) for type_ in SPECIAL)
for addon, ctype in qs.values_list('addon', 'collection__type'):
addons[ctype].append(addon)
user.mobile_addons = addons[amo.COLLECTION_MOBILE]
user.favorite_addons = addons[amo.COLLECTION_FAVORITES]
2010-08-05 00:49:44 +04:00
class BlacklistedUsername(amo.models.ModelBase):
"""Blacklisted user usernames."""
username = models.CharField(max_length=255, unique=True, default='')
class Meta:
db_table = 'users_blacklistedusername'
def __unicode__(self):
2010-08-05 00:49:44 +04:00
return self.username
@classmethod
2010-08-05 00:49:44 +04:00
def blocked(cls, username):
"""Check to see if a username is in the (cached) blacklist."""
username = smart_unicode(username).lower()
qs = cls.objects.all()
2010-08-05 00:49:44 +04:00
f = lambda: [u.lower() for u in qs.values_list('username', flat=True)]
blacklist = caching.cached_with(qs, f, 'blocked')
2010-08-05 00:49:44 +04:00
return username in blacklist
class PersonaAuthor(unicode):
"""Stub user until the persona authors get imported."""
@property
def id(self):
"""I don't want to change code depending on PersonaAuthor.id, so I'm
just hardcoding 0. The only code using this is flush_urls."""
return 0
@property
def display_name(self):
return self