1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
|
# -*- coding: utf-8 -*-
from django.core.urlresolvers import reverse
from django.conf import settings
from django.db import models
from django.contrib import admin
from django.contrib.auth.models import User
from django.utils.hashcompat import sha_constructor
from django.utils.timezone import utc
from django.template.loader import render_to_string
from sfaccount.tasks import send_mail
import re
import random
import datetime
# SHA1 Hash regex
SHA1 = re.compile('^[a-f0-9]{40}$')
class AccountManager(models.Manager): # {{{
"""
custom manager for 'Account' model
"""
def activate(self, activation_key):
"""
validate an activation key and activate the corresponding
'User' if valid.
if the key is valid and not expired, return the 'Account'
if the key is invalid or expired, return 'False'
if the key is valid but the 'User' is already activated,
return 'False'
reset the key string to prevent reactivation of an account
which has been deactivated by the admin
"""
if SHA1.search(activation_key):
try:
account = self.get(activation_key=activation_key)
except self.model.DoesNotExist:
return False
if not account.activation_key_expired():
user = account.user
user.is_active = True
user.save()
account.activation_key = self.model.ACTIVATED
account.save()
return account
return False
def create_inactive_account(self, username, email, password,
send_email=True):
"""
create a new, *local*, inactive 'User',
and generate an 'Account' and
email the activation key. return the new 'User'
the activation key is a SHA1 hash, generated from
a combination of the 'username' and a random slat
"""
new_user = User.objects.create_user(username, email, password)
new_user.is_active = False
new_user.save()
# create corresponding 'Account'
salt = sha_constructor(str(random.random())).hexdigest()[:5]
activation_key = sha_constructor(salt+username).hexdigest()
new_account = self.create(user=new_user, is_social=False,
activation_key=activation_key)
new_account.save()
# send email
if send_email:
new_account.send_activation_email()
return new_account
def delete_expired_accounts(self):
"""
Remove expired instances of 'Account's and their
associated 'User's.
"""
for account in self.all():
if account.activation_key_expired():
user = account.user
if not user.is_active:
user.delete()
account.delete()
# }}}
class Account(models.Model): # {{{
"""
Account model for 97suifang
"""
ACTIVATED = u'ALREADY_ACTIVATED'
user = models.OneToOneField(User, related_name="account")
# username -> user.username
# date_joined -> user.date_joined
screen_name = models.CharField(u"昵称", max_length=30,
null=True, blank=True)
avatar = models.ImageField(u"头像", upload_to="uploads/avatars/",
null=True, blank=True)
# if social account
is_social = models.BooleanField(default=False)
# activation (SHA1 hash)
activation_key = models.CharField(u"激活密钥", max_length=40)
objects = AccountManager()
class Meta:
verbose_name_plural = u"账户信息"
def __unicode__(self):
if self.is_social:
type = u"social"
else:
type = u"local"
if self.user.is_active:
state = u"activated"
else:
state = u"nonactivated"
#
return u'< Account: %s, %s, %s >' % (self.user.username,
type, state)
def activation_key_expired(self): # {{{
"""
determine whether the activation key has expired
Key expiration is determined by a two-step process:
1. If the user has already activated, the key will have been
reset to the string constant ``ACTIVATED``. Re-activating
is not permitted, and so this method returns ``True`` in
this case.
2. Otherwise, the date the user signed up is incremented by
the number of days specified in the setting
``ACCOUNT_ACTIVATION_DAYS`` (which should be the number of
days after signup during which a user is allowed to
activate their account); if the result is less than or
equal to the current date, the key has expired and this
method returns ``True``.
"""
expiration_days = datetime.timedelta(
days=settings.ACCOUNT_ACTIVATION_DAYS)
now_utc = datetime.datetime.utcnow().replace(tzinfo=utc)
return self.user.is_active or (
self.user.date_joined + expiration_days <= now_utc)
# }}}
def get_activation_url(self):
return reverse('activate_key',
kwargs={'activation_key': self.activation_key})
# send_activation_email {{{
def send_activation_email(self,
async=None,
subject_template_name='sfaccount/activation_email_subject.txt',
email_template_name='sfaccount/activation_email_body.txt',
html_email_template_name='sfaccount/activation_email_body.html'):
"""
send an activation email to the newly registered user
"""
ctx_dict = {
'username': self.user.username,
'activation_key': self.activation_key,
'activation_url': self.get_activation_url(),
'expiration_days': settings.ACCOUNT_ACTIVATION_DAYS,
}
subject = render_to_string(subject_template_name, ctx_dict)
subject = ''.join(subject.splitlines())
body_text = render_to_string(email_template_name, ctx_dict)
body_html = render_to_string(html_email_template_name, ctx_dict)
to = self.user.email
# send email
if async is None:
async_send_mail = getattr(settings, 'ASYNC_SEND_MAIL', False)
else:
async_send_mail = async
if async_send_mail:
send_mail.delay(to, subject, body_text, body_html)
else:
send_mail(to, subject, body_text, body_html)
# }}}
def delete_account(self):
user = self.user
user.delete()
self.delete()
# }}}
admin.site.register([Account])
# vim: set ts=4 sw=4 tw=0 fenc=utf-8 ft=python: #
|