diff options
author | Alvin Li <liweitianux@gmail.com> | 2013-10-06 13:51:17 +0800 |
---|---|---|
committer | Alvin Li <liweitianux@gmail.com> | 2013-10-06 13:51:17 +0800 |
commit | fafce2cfc72f4e1cd14ff6cb693c8ec7854159c5 (patch) | |
tree | 188bcebbf466f803e773c87008eab83e3c26012e | |
parent | 46b4f8407f40cfcc0abf6c6e4fbc8fcbee00151d (diff) | |
download | 97dev-fafce2cfc72f4e1cd14ff6cb693c8ec7854159c5.tar.bz2 |
* added settings 'ASYNC_SEND_MAIL'
* added parameter 'async' to 'Account.send_activation_email()' method
* improved views 'sfaccount.views.activate_view'
* adjusted urls 'activate_key'
* added view 'sfaccount.views.activate_done_view'
-rw-r--r-- | 97suifangqa/apps/sfaccount/README.txt | 8 | ||||
-rw-r--r-- | 97suifangqa/apps/sfaccount/forms.py | 51 | ||||
-rw-r--r-- | 97suifangqa/apps/sfaccount/models.py | 28 | ||||
-rw-r--r-- | 97suifangqa/apps/sfaccount/tasks.py | 5 | ||||
-rw-r--r-- | 97suifangqa/apps/sfaccount/templates/sfaccount/activate.html | 15 | ||||
-rw-r--r-- | 97suifangqa/apps/sfaccount/templates/sfaccount/activate_done.html | 36 | ||||
-rw-r--r-- | 97suifangqa/apps/sfaccount/templates/sfaccount/activate_send_mail.html | 36 | ||||
-rw-r--r-- | 97suifangqa/apps/sfaccount/templates/sfaccount/activation_email_body.html | 2 | ||||
-rw-r--r-- | 97suifangqa/apps/sfaccount/templates/sfaccount/activation_email_body.txt | 2 | ||||
-rw-r--r-- | 97suifangqa/apps/sfaccount/templates/sfaccount/login.html | 11 | ||||
-rw-r--r-- | 97suifangqa/apps/sfaccount/templates/sfaccount/password_change_done.html | 2 | ||||
-rw-r--r-- | 97suifangqa/apps/sfaccount/urls.py | 10 | ||||
-rw-r--r-- | 97suifangqa/apps/sfaccount/views.py | 51 | ||||
-rw-r--r-- | 97suifangqa/isuifangqa.db | bin | 379904 -> 379904 bytes | |||
-rw-r--r-- | 97suifangqa/settings.py | 14 |
15 files changed, 234 insertions, 37 deletions
diff --git a/97suifangqa/apps/sfaccount/README.txt b/97suifangqa/apps/sfaccount/README.txt index c55a405..651122c 100644 --- a/97suifangqa/apps/sfaccount/README.txt +++ b/97suifangqa/apps/sfaccount/README.txt @@ -15,11 +15,17 @@ HOWTO run: 5) ajust 'SF_MAIL' settings in 'mail_settings.py' 6) $ redis-server 7) $ ./manage.py syncdb -8) $ ./manage.py celeryd worker -E +8) $ ./manage.py celery worker --loglevel=info 9) $ ./manage.py celerycam (for monitoring) TEST: a) ./manage.py shell >>> from sfaccount.tasks import send_mail >>> send_mail(to, subject, body_text, body_html) + # async task + >>> r = send_mail.delay(to, subject, body_text, body_html) + >>> r.ready() + >>> r.result + >>> r.successful() + >>> diff --git a/97suifangqa/apps/sfaccount/forms.py b/97suifangqa/apps/sfaccount/forms.py index d2a3bf1..5502263 100644 --- a/97suifangqa/apps/sfaccount/forms.py +++ b/97suifangqa/apps/sfaccount/forms.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from django import forms +from django.conf import settings from django.template import loader from django.utils.http import int_to_base36 from django.contrib.auth.models import User @@ -34,8 +35,10 @@ class AccountForm(forms.Form): def clean_username(self): username = self.cleaned_data['username'] # check length - if len(username) < 6: + if len(username) < settings.MIN_USERNAME_LENGTH: raise forms.ValidationError(u'用户名长度需大于6位') + if len(username) > settings.MAX_USERNAME_LENGTH: + raise forms.ValidationError(u'用户名长度不能超过30位') # check first letter p = re.compile('[a-zA-Z_]') if p.match(username[0]): @@ -58,8 +61,11 @@ class AccountForm(forms.Form): def clean_password1(self): password1 = self.cleaned_data['password1'] - if len(password1) < 6: + # check length + if len(password1) < settings.MIN_PASSWORD_LENGTH: raise forms.ValidationError(u'密码长度需大于6位') + if len(password1) > settings.MAX_PASSWORD_LENGTH: + raise forms.ValidationError(u'密码长度不能超过30位') return password1 def clean(self): @@ -79,13 +85,19 @@ class SFPasswordResetForm(forms.Form): to use djcelery's async send mail """ error_messages = { - 'unknown': _("That e-mail address doesn't have an associated " - "user account. Are you sure you've registered?"), + 'unknown': u"该邮箱未在系统中注册,请填写您注册时使用的邮箱地址", 'unusable': _("The user account associated with this e-mail " "address cannot reset the password."), } email = forms.EmailField(label=_("E-mail"), max_length=75) + def clean_email(self): + email = self.cleaned_data['email'] + users = User.objects.filter(email__iexact=email) + if not len(users): + raise forms.ValidationError(self.error_messages['unknown']) + return email + def save(self, domain_override=None, subject_template_name='registration/password_reset_subject.txt', email_template_name='registration/password_reset_email.txt', @@ -96,14 +108,8 @@ class SFPasswordResetForm(forms.Form): Generates a one-use only link for resetting password and sends to the user. """ - # validate first - if not self.is_valid(): - return self - # validated: has 'self.cleaned_data' email = self.cleaned_data['email'] users = User.objects.filter(email__iexact=email) - if not len(users): - raise forms.ValidationError(self.error_messages['unknown']) for user in users: # make sure that no email is sent to a user that actually # has a password marked as unusable @@ -135,7 +141,30 @@ class SFPasswordResetForm(forms.Form): body_html = None # send mail to = user.email - send_mail(to, subject, body_text, body_html) + if getattr(settings, 'ASYNC_SEND_MAIL', False): + send_mail.delay(to, subject, body_text, body_html) + else: + send_mail(to, subject, body_text, body_html) +# }}} + + +# SFEmailForm {{{ +class SFEmailForm(forms.Form): + """ + get input email address and validate it + used in sending activation mail + """ + error_messages = { + 'unknown': u"该邮箱未在系统中注册,请填写您注册时使用的邮箱地址", + } + email = forms.EmailField(label=_("E-mail"), max_length=75) + + def clean_email(self): + email = self.cleaned_data['email'] + users = User.objects.filter(email__iexact=email) + if not len(users): + raise forms.ValidationError(self.error_messages['unknown']) + return email # }}} diff --git a/97suifangqa/apps/sfaccount/models.py b/97suifangqa/apps/sfaccount/models.py index 72904c4..5ebba1c 100644 --- a/97suifangqa/apps/sfaccount/models.py +++ b/97suifangqa/apps/sfaccount/models.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- +from django.core.urlresolvers import reverse from django.conf import settings from django.db import models from django.contrib import admin @@ -150,22 +151,39 @@ class Account(models.Model): # {{{ self.user.date_joined + expiration_days <= now_utc) # }}} - def send_activation_email(self): # {{{ + 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('sfaccount/activation_email_subject.txt', ctx_dict) + subject = render_to_string(subject_template_name, ctx_dict) subject = ''.join(subject.splitlines()) - body_text = render_to_string('sfaccount/activation_email_body.txt', ctx_dict) - body_html = render_to_string('sfaccount/activation_email_body.html', ctx_dict) + 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 - send_mail.delay(to, subject, body_text, body_html) + 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): diff --git a/97suifangqa/apps/sfaccount/tasks.py b/97suifangqa/apps/sfaccount/tasks.py index 1613646..86cf6ea 100644 --- a/97suifangqa/apps/sfaccount/tasks.py +++ b/97suifangqa/apps/sfaccount/tasks.py @@ -11,3 +11,8 @@ def send_mail(to, subject, content_text=None, content_html=None): content_text=content_text, content_html=content_html) + +@task +def test_add(x, y): + return x + y + diff --git a/97suifangqa/apps/sfaccount/templates/sfaccount/activate.html b/97suifangqa/apps/sfaccount/templates/sfaccount/activate.html index a81af6d..1a6c6f9 100644 --- a/97suifangqa/apps/sfaccount/templates/sfaccount/activate.html +++ b/97suifangqa/apps/sfaccount/templates/sfaccount/activate.html @@ -13,7 +13,8 @@ {% block scripts %} <script type="text/javascript"> // activate account url - var activate_url = '{% url activate %}'; + var activate_key_url = '{{ activate_key_url }}'; + var send_activation_mail_url = '{% url send_activation_mail %}'; $(document).ready(function(){ // validate key input @@ -47,7 +48,7 @@ // submit var key_raw = $("#activation_key").val(); var key = htmlEscape(key_raw.toLowerCase()); - var target_url = activate_url + key + '/'; + var target_url = activate_key_url + key + '/'; //console.log(target_url); window.location.href = target_url; } @@ -63,6 +64,10 @@ } }); + // send_activation_mail + $("#send_activation_mail").bind("click", function() { + window.location.href = send_activation_mail_url; + }); }); function htmlEscape(str) { @@ -93,6 +98,12 @@ <input type="button" id="activate_account" value="激活账户" /> </div> + <div class="no-mail"> + 还未收到激活邮件?您可以尝试重发激活邮件。 + <br /> + <input type="button" id="send_activation_mail" value="重发激活邮件" /> + </div> + {% endblock body %} <!-- vim: set ts=8 sw=2 tw=0 fenc=utf-8 ft=htmldjango.html: --> diff --git a/97suifangqa/apps/sfaccount/templates/sfaccount/activate_done.html b/97suifangqa/apps/sfaccount/templates/sfaccount/activate_done.html new file mode 100644 index 0000000..8432a39 --- /dev/null +++ b/97suifangqa/apps/sfaccount/templates/sfaccount/activate_done.html @@ -0,0 +1,36 @@ +{% extends "picture-base.html" %} +{% load staticfiles %} + +{% block title %} +账户已激活 | 97 随访 +{% endblock %} + +{% block othercss %} +<link rel="stylesheet" href="{% static "stylesheets/sass/registration.css" %}"> +{% endblock %} + +{% block scripts %} + <script type="text/javascript"> + // login url + var login_url = '{% url login %}'; + + $(document).ready(function() { + $("#login").bind("click", function() { + window.location.href = login_url; + }); + }); + </script> +{% endblock %} + +{% block body %} + <h2>加入97随访   科学了解乙肝治疗</h2> + + <h4>欢迎加入97随访!</h4> + + <p>您的账户已成功激活,现在您可以登录使用。</p> + <p> + <input type="button" id="login" value="登录" /> + </p> +{% endblock body %} + +<!-- vim: set ts=8 sw=2 tw=0 fenc=utf-8 ft=htmldjango.html: --> diff --git a/97suifangqa/apps/sfaccount/templates/sfaccount/activate_send_mail.html b/97suifangqa/apps/sfaccount/templates/sfaccount/activate_send_mail.html new file mode 100644 index 0000000..e95a881 --- /dev/null +++ b/97suifangqa/apps/sfaccount/templates/sfaccount/activate_send_mail.html @@ -0,0 +1,36 @@ +{% extends "picture-base.html" %} +{% load staticfiles %} + +{% block title %} +重发激活邮件 | 97 随访 +{% endblock %} + +{% block othercss %} +<link rel="stylesheet" href="{% static "stylesheets/sass/registration.css" %}"> +{% endblock %} + +{% block body %} + <h2>加入97随访   科学了解乙肝治疗</h2> + + <p> + 还未收到激活邮件? + </p> + <p> + 请在下面输入您注册时使用的邮箱地址, + 我们将为您重新发送激活邮件。 + </p> + + <form action="" method="post">{% csrf_token %} + <table class="send-activation-mail"> + <tr> + <td> + <input type="text" name="email" value="{{ form.email.value|default_if_none:"" }}" placeholder="您注册时的邮箱地址" class="email"> + </td> + <td class="error">{{ form.email.errors|join:"" }}</td> + </tr> + </table> + <input type="submit" value="重发激活邮件" /> + </form> +{% endblock %} + +<!-- vim: set ts=8 sw=2 tw=0 fenc=utf-8 ft=htmldjango.html: --> diff --git a/97suifangqa/apps/sfaccount/templates/sfaccount/activation_email_body.html b/97suifangqa/apps/sfaccount/templates/sfaccount/activation_email_body.html index 9792ad5..51b1751 100644 --- a/97suifangqa/apps/sfaccount/templates/sfaccount/activation_email_body.html +++ b/97suifangqa/apps/sfaccount/templates/sfaccount/activation_email_body.html @@ -5,7 +5,7 @@ <p> 您的激活码为 {{ activation_key }},请在 {{ expiration_days }} 天内激活账户,直接打开以下链接进行激活:<br /> -http://www.97suifang.com/accounts/activate/{{ activation_key }}/ +http://www.97suifang.com{{ activation_url }} </p> <br /> diff --git a/97suifangqa/apps/sfaccount/templates/sfaccount/activation_email_body.txt b/97suifangqa/apps/sfaccount/templates/sfaccount/activation_email_body.txt index b479ff0..44a2c4d 100644 --- a/97suifangqa/apps/sfaccount/templates/sfaccount/activation_email_body.txt +++ b/97suifangqa/apps/sfaccount/templates/sfaccount/activation_email_body.txt @@ -3,7 +3,7 @@ 欢迎您加入97随访(97suifang.com)。 您的激活码为 {{ activation_key }},请在 {{ expiration_days }} 天内激活账户,直接打开以下链接进行激活: -http://www.97suifang.com/accounts/activate/{{ activation_key }}/ +http://www.97suifang.com{{ activation_url }} 97随访 团队 diff --git a/97suifangqa/apps/sfaccount/templates/sfaccount/login.html b/97suifangqa/apps/sfaccount/templates/sfaccount/login.html index f5c7942..6e3be80 100644 --- a/97suifangqa/apps/sfaccount/templates/sfaccount/login.html +++ b/97suifangqa/apps/sfaccount/templates/sfaccount/login.html @@ -47,11 +47,14 @@ <input type="hidden" name="next" value="{{ next }}" /> <input type="submit" value="登 录" class="submit login"/> -   |   - <a href="{% url signup %}">还没有帐号?</a> -   |   - <a href="{% url password_reset %}">忘记密码?</a> </form> + <br /> + + <p> + <a href="{% url signup %}">还没有帐号?</a> +   |   + <a href="{% url password_reset %}">忘记密码?</a> + </p> {% endblock body%} {# vim: set ts=2 sw=2 tw=0 fenc=utf-8 ft=htmldjango.html: #} diff --git a/97suifangqa/apps/sfaccount/templates/sfaccount/password_change_done.html b/97suifangqa/apps/sfaccount/templates/sfaccount/password_change_done.html index ed91216..ac22668 100644 --- a/97suifangqa/apps/sfaccount/templates/sfaccount/password_change_done.html +++ b/97suifangqa/apps/sfaccount/templates/sfaccount/password_change_done.html @@ -11,7 +11,7 @@ {% block scripts %} <script type="text/javascript"> - // login url + // home url var home_url = '{% url go_home %}'; $(document).ready(function() { diff --git a/97suifangqa/apps/sfaccount/urls.py b/97suifangqa/apps/sfaccount/urls.py index f2a930b..29925e1 100644 --- a/97suifangqa/apps/sfaccount/urls.py +++ b/97suifangqa/apps/sfaccount/urls.py @@ -10,10 +10,16 @@ from django.contrib.auth import views as auth_views urlpatterns = patterns('sfaccount.views', url(r'^signup/$', 'signup_view', name='signup'), + # send activation mail + url(r'^activate/send_mail/$', 'send_activation_mail_view', + name='send_activation_mail'), # activate account url(r'^activate/$', 'activate_view', name='activate'), - url(r'^activate/(?P<activation_key>.+)/$', - 'activate_view'), + url(r'^activate/key/(?P<activation_key>[0-9a-zA-Z]+)/$', + 'activate_view', + name='activate_key'), + url(r'^activate/done/$', 'activate_done_view', + name='activate_done'), # go home url(r'^home/$', 'go_home_view', name='go_home'), ) diff --git a/97suifangqa/apps/sfaccount/views.py b/97suifangqa/apps/sfaccount/views.py index 9cc2868..014dc32 100644 --- a/97suifangqa/apps/sfaccount/views.py +++ b/97suifangqa/apps/sfaccount/views.py @@ -8,10 +8,14 @@ from django.views.decorators.csrf import csrf_protect from django.utils.translation import ugettext as _ from django.shortcuts import render, redirect +from django.contrib.auth.models import User from django.contrib.auth.tokens import default_token_generator from sfaccount.models import Account -from sfaccount.forms import AccountForm, SFPasswordResetForm +from sfaccount.forms import ( + AccountForm, SFPasswordResetForm, + SFEmailForm, +) # email address shown in the sent mail FROM_EMAIL = getattr(settings, 'SF_EMAIL').get('display_from') @@ -28,7 +32,9 @@ def go_home_view(request): kwargs={'username': username})) else: # not logged in - return redirect(reverse('login')) + cur_url = request.get_full_path() + target_url = reverse('login') + '?next=' + cur_url + return redirect(target_url) # }}} @@ -74,15 +80,50 @@ def activate_view(request, activation_key=None): account = Account.objects.activate(activation_key) if account: # activated - home_url = '/profile/%s/' % account.user.username - return HttpResponseRedirect(home_url) + return HttpResponseRedirect(reverse('activate_done')) else: # activate failed data = {'activate_failed': True} return render(request, 'sfaccount/activate.html', data) else: # ask user for the 'activation_key' - return render(request, 'sfaccount/activate.html') + activate_key_url = reverse('activate_key', + kwargs={'activation_key': 'XXX'}) + activate_key_url = activate_key_url.replace('XXX/', '') + data = {'activate_key_url': activate_key_url} + return render(request, 'sfaccount/activate.html', data) +# }}} + + +# activate_done {{{ +def activate_done_view(request): + template_name = 'sfaccount/activate_done.html' + context = {} + return render(request, template_name, context) +# }}} + + +# send_activation_mail_view {{{ +def send_activation_mail_view(request, + template_name='sfaccount/activate_send_mail.html'): + """ + send activation mail to the given email address + """ + if request.method == "POST": + form = SFEmailForm(request.POST) + if form.is_valid(): + email = form.cleaned_data['email'] + accounts = Account.objects.filter(user__email__iexact=email) + for account in accounts: + account.send_activation_email(async=False) + return HttpResponseRedirect(reverse('activate')) + else: + form = SFEmailForm() + context = { + 'form': form, + 'title': u"重发激活邮件", + } + return TemplateResponse(request, template_name, context) # }}} diff --git a/97suifangqa/isuifangqa.db b/97suifangqa/isuifangqa.db Binary files differindex 9494aaf..1f13435 100644 --- a/97suifangqa/isuifangqa.db +++ b/97suifangqa/isuifangqa.db diff --git a/97suifangqa/settings.py b/97suifangqa/settings.py index 3245266..9dda171 100644 --- a/97suifangqa/settings.py +++ b/97suifangqa/settings.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- import os, sys +import djcelery +djcelery.setup_loader() DEBUG = True @@ -160,8 +162,15 @@ INSTALLED_APPS = ( LOGIN_REDIRECT_URL = '/blog/index' -## +## account settings ACCOUNT_ACTIVATION_DAYS = 3 +MIN_USERNAME_LENGTH = 6 +MAX_USERNAME_LENGTH = 30 +MIN_PASSWORD_LENGTH = 6 +MAX_PASSWORD_LENGTH = 30 + +## async send mail +ASYNC_SEND_MAIL = True ## avatar AVATAR_DIR = os.path.join(PROJECT_ROOT, 'uploads/avatars') @@ -177,9 +186,6 @@ except ImportError: BROKER_URL = 'redis://127.0.0.1:6379/0' CELERY_RESULT_BACKEND = 'redis://127.0.0.1:6379/0' -import djcelery -djcelery.setup_loader() - ## mail server settings from mail_settings import * |