diff options
Diffstat (limited to 'account')
-rw-r--r-- | account/__init__.py | 0 | ||||
-rw-r--r-- | account/admin.py | 3 | ||||
-rw-r--r-- | account/forms.py | 54 | ||||
-rw-r--r-- | account/models.py | 81 | ||||
-rw-r--r-- | account/templates/account/login.html | 24 | ||||
-rw-r--r-- | account/templates/account/logout.html | 19 | ||||
-rw-r--r-- | account/templates/account/password_change.html | 24 | ||||
-rw-r--r-- | account/templates/account/password_change_done.html | 21 | ||||
-rw-r--r-- | account/templates/account/profile.html | 83 | ||||
-rw-r--r-- | account/templates/account/profile_update.html | 24 | ||||
-rw-r--r-- | account/templates/account/profile_update_done.html | 21 | ||||
-rw-r--r-- | account/tests.py | 3 | ||||
-rw-r--r-- | account/urls.py | 81 | ||||
-rw-r--r-- | account/views.py | 68 |
14 files changed, 506 insertions, 0 deletions
diff --git a/account/__init__.py b/account/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/account/__init__.py diff --git a/account/admin.py b/account/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/account/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/account/forms.py b/account/forms.py new file mode 100644 index 0000000..ca54711 --- /dev/null +++ b/account/forms.py @@ -0,0 +1,54 @@ +# -*- coding: utf-8 -*- + +""" +account/forms.py for skaschool +""" + +from django import forms +from registration.forms import RegistrationFormUniqueEmail +from django.utils.translation import ugettext_lazy as _ + +from account.models import UserProfile + + +class UserRegForm(RegistrationFormUniqueEmail): + """ + based on 'django-registration' RegistrationFormUniqueEmail + add fields 'realname', 'gender', 'institute' and 'captcha' + """ + # XXX: keep consistent with GENDERS in 'models.UserProfile' + GENDERS = ( + ('M', _("Male")), + ('F', _("Female")), + ('X', _("Secret")), + ) + realname = forms.CharField(max_length=30, label=_("Name")) + gender = forms.ChoiceField(choices=GENDERS, label=_("Gender")) + institute = forms.CharField(max_length=100, label=_("Institute")) + + def __init__(self, *args, **kw): + super(UserRegForm, self).__init__(*args, **kw) + # order form fields + self.fields.keyOrder = [ + 'username', + 'email', + 'password1', + 'password2', + 'realname', + 'gender', + 'institute', + ] + + +class UpdateProfileForm(forms.ModelForm): + """ + ModelForm of 'UserProfile' used in 'UpdateProfileView' + """ + # extra email field + email = forms.EmailField(label=_("E-mail")) + + class Meta: + model = UserProfile + fields = ('realname', 'gender', 'institute') + + diff --git a/account/models.py b/account/models.py new file mode 100644 index 0000000..38047d6 --- /dev/null +++ b/account/models.py @@ -0,0 +1,81 @@ +# -*- coding: utf-8 -*- +# +# app 'account' models +# registration +# + +from django.db import models +from django.contrib.auth.models import User +from django.contrib import admin + +from django.utils.translation import ugettext_lazy as _ + +from registration.signals import user_registered + + +class UserProfile(models.Model): + """ + custom user profile + connected with signal 'user_registered' sent by 'django-registration' + """ + # XXX: keep consistent with GENDERS in 'forms.UserRegForm' + GENDERS = ( + ('M', _("Male")), + ('F', _("Female")), + ('X', _("Secret")), + ) + # status choices of is_approved + APPROVED_STATUS = ( + ('Y', _("Yes")), + ('N', _("No")), + ('C', _("Checking")), + ) + # status choices of is_sponsored + SPONSORED_STATUS = ( + ('Y', _("Yes")), + ('N', _("No")), + ('C', _("Checking")), + ) + # model fields + # FK default backward manager name 'userprofile_set' + user = models.ForeignKey(User, unique=True, verbose_name=_("Username")) + realname = models.CharField(_("Name"), max_length=30) + gender = models.CharField(_("Gender"), max_length=1, choices=GENDERS) + institute = models.CharField(_("Institute"), max_length=100) + # store the infomation about approval and sponsorship + is_approved = models.CharField(_("Is approved"), max_length=1, + choices=APPROVED_STATUS, default='C') + is_sponsored = models.CharField(_("Is sponsored"), max_length=1, + choices=SPONSORED_STATUS, default='C') + + class Meta: + verbose_name = _('user profile') + verbose_name_plural = _('user profiles') + + def __unicode__(self): + return u'UserProfile for %s' % self.user + + +###### signal callback ###### +def user_registered_callback(sender, user, request, **kwargs): + """ + callback of signal 'user_registered' from 'django-registration' + to create custom user profile + ref: http://johnparsons.net/index.php/2013/06/28/creating-profiles-with-django-registration/ + """ + profile = UserProfile(user = user) + profile.realname = request.POST['realname'] + profile.gender = request.POST['gender'] + profile.institute = request.POST['institute'] + profile.save() + +### connect 'user_registered_callback' to signal +user_registered.connect(user_registered_callback) + + +### add to adim +admin.site.register([ + UserProfile, +]) + + diff --git a/account/templates/account/login.html b/account/templates/account/login.html new file mode 100644 index 0000000..4fee972 --- /dev/null +++ b/account/templates/account/login.html @@ -0,0 +1,24 @@ +{% extends 'base.html' %} +{% load staticfiles %} +{% load url from future %} +{% load bootstrap3 %} + +{# login template #} + +{% block title %} +登录 | 2014 SKA Summer School +{% endblock %} + +{% block content %} + <div class="container"> + <h2>登录</h2> + <br> + <form role="form" class="form-horizontal" method="post"> + {% csrf_token %} + {% bootstrap_form form layout='horizontal' %} + {% buttons submit='提交' reset='重置' layout='horizontal' %}{% endbuttons %} + </form> + </div> +{% endblock %} + +{# vim: set ts=8 sw=2 tw=0 fenc=utf-8 ft=htmldjango.html: #} diff --git a/account/templates/account/logout.html b/account/templates/account/logout.html new file mode 100644 index 0000000..7e3ac4a --- /dev/null +++ b/account/templates/account/logout.html @@ -0,0 +1,19 @@ +{% extends 'base.html' %} +{% load staticfiles %} +{% load url from future %} +{% load bootstrap3 %} + +{# login template #} + +{% block title %} +已退出 | 2014 SKA Summer School +{% endblock %} + +{% block content %} + <div class="container"> + <h2>已退出</h2> + <p class="lead">您已成功退出。</p> + </div> +{% endblock %} + +{# vim: set ts=8 sw=2 tw=0 fenc=utf-8 ft=htmldjango.html: #} diff --git a/account/templates/account/password_change.html b/account/templates/account/password_change.html new file mode 100644 index 0000000..f4e2e90 --- /dev/null +++ b/account/templates/account/password_change.html @@ -0,0 +1,24 @@ +{% extends 'base.html' %} +{% load staticfiles %} +{% load url from future %} +{% load bootstrap3 %} + +{# login template #} + +{% block title %} +修改密码 | 2014 SKA Summer School +{% endblock %} + +{% block content %} + <div class="container"> + <h2>修改密码</h2> + <br> + <form role="form" class="form-horizontal" method="post"> + {% csrf_token %} + {% bootstrap_form form layout='horizontal' %} + {% buttons submit='提交' reset='重置' layout='horizontal' %}{% endbuttons %} + </form> + </div> +{% endblock %} + +{# vim: set ts=8 sw=2 tw=0 fenc=utf-8 ft=htmldjango.html: #} diff --git a/account/templates/account/password_change_done.html b/account/templates/account/password_change_done.html new file mode 100644 index 0000000..e8770ca --- /dev/null +++ b/account/templates/account/password_change_done.html @@ -0,0 +1,21 @@ +{% extends 'base.html' %} +{% load staticfiles %} +{% load url from future %} +{% load bootstrap3 %} + +{# login template #} + +{% block title %} +密码修改成功 | 2014 SKA Summer School +{% endblock %} + +{% block content %} + <div class="container"> + <h2>密码修改成功</h2> + <p class="lead">您已成功修改密码。</p> + <br> + <p><a href="{% url 'profile' %}" class="btn btn-default">返回个人主页</a></p> + </div> +{% endblock %} + +{# vim: set ts=8 sw=2 tw=0 fenc=utf-8 ft=htmldjango.html: #} diff --git a/account/templates/account/profile.html b/account/templates/account/profile.html new file mode 100644 index 0000000..e8d295c --- /dev/null +++ b/account/templates/account/profile.html @@ -0,0 +1,83 @@ +{% extends 'base.html' %} +{% load staticfiles %} +{% load url from future %} +{% load bootstrap3 %} + +{# login template #} + +{% block title %} +个人主页 | 2014 SKA Summer School +{% endblock %} + +{% block content %} + <div class="container"> + <h2>个人主页</h2> + <br> + + <table class="table table-striped table-bordered table-hover"> + <tbody> + <tr> + <th class="profile-name">姓名</th> + <td class="profile-name-data">{{ profile.realname }}</td> + </tr> + <tr> + <th class="profile-gender">性别</th> + <td class="profile-gender-data"> + {% if profile.gender == 'M' %} + 男 + {% elif profile.gender == 'F' %} + 女 + {% elif profile.gender == 'X' %} + <span class="glyphicon glyphicon-ban-circle"></span> + {% else %} + <span class="glyphicon glyphicon-warning-sign"></span> <span class="label label-danger">系统错误</span> + {% endif %} + </td> + </tr> + <tr> + <th class="profile-email">邮箱</th> + <td class="profile-email-data">{{ user.email }}</td> + </tr> + <tr> + <th class="profile-institute">单位</th> + <td class="profile-institute-data">{{ profile.institute }}</td> + </tr> + <tr> + <th class="profile-approval">是否审定</th> + <td class="profile-approval-data"> + {% if profile.is_approved == 'Y' %} + <span class="glyphicon glyphicon-ok"></span> <span class="label label-success">是</span> + {% elif profile.is_approved == 'N' %} + <span class="glyphicon glyphicon-remove"></span> <span class="label label-warning">否</span> + {% elif profile.is_approved == 'C' %} + <span class="glyphicon glyphicon-question-sign"></span> <span class="label label-default">审核中</span> + {% else %} + <span class="glyphicon glyphicon-warning-sign"></span> <span class="label label-danger">系统错误</span> + {% endif %} + </td> + </tr> + <tr> + <th class="profile-sponsorship">是否资助</th> + <td class="profile-sponsorship-data"> + {% if profile.is_sponsored == 'Y' %} + <span class="glyphicon glyphicon-ok"></span> <span class="label label-success">是</span> + {% elif profile.is_sponsored == 'N' %} + <span class="glyphicon glyphicon-remove"></span> <span class="label label-warning">否</span> + {% elif profile.is_sponsored == 'C' %} + <span class="glyphicon glyphicon-question-sign"></span> <span class="label label-default">审核中</span> + {% else %} + <span class="glyphicon glyphicon-warning-sign"></span> <span class="label label-danger">系统错误</span> + {% endif %} + </td> + </tr> + </table> + + <br> + <p> + <a href="{% url 'profile_update' %}" class="btn btn-default">更新个人信息</a> + <a href="{% url 'password_change' %}" class="btn btn-default">修改密码</a> + </p> + </div> +{% endblock %} + +{# vim: set ts=8 sw=2 tw=0 fenc=utf-8 ft=htmldjango.html: #} diff --git a/account/templates/account/profile_update.html b/account/templates/account/profile_update.html new file mode 100644 index 0000000..1802bb0 --- /dev/null +++ b/account/templates/account/profile_update.html @@ -0,0 +1,24 @@ +{% extends 'base.html' %} +{% load staticfiles %} +{% load url from future %} +{% load bootstrap3 %} + +{# login template #} + +{% block title %} +更新个人信息 | 2014 SKA Summer School +{% endblock %} + +{% block content %} + <div class="container"> + <h2>更新个人信息</h2> + <br> + <form role="form" class="form-horizontal" method="post"> + {% csrf_token %} + {% bootstrap_form form layout='horizontal' %} + {% buttons submit='提交' reset='重置' layout='horizontal' %}{% endbuttons %} + </form> + </div> +{% endblock %} + +{# vim: set ts=8 sw=2 tw=0 fenc=utf-8 ft=htmldjango.html: #} diff --git a/account/templates/account/profile_update_done.html b/account/templates/account/profile_update_done.html new file mode 100644 index 0000000..5b10eb2 --- /dev/null +++ b/account/templates/account/profile_update_done.html @@ -0,0 +1,21 @@ +{% extends 'base.html' %} +{% load staticfiles %} +{% load url from future %} +{% load bootstrap3 %} + +{# login template #} + +{% block title %} +信息已更新 | 2014 SKA Summer School +{% endblock %} + +{% block content %} + <div class="container"> + <h2>信息已更新</h2> + <p class="lead">您的个人信息已更新。</p> + <br> + <p><a href="{% url 'profile' %}" class="btn btn-default">返回个人主页</a></p> + </div> +{% endblock %} + +{# vim: set ts=8 sw=2 tw=0 fenc=utf-8 ft=htmldjango.html: #} diff --git a/account/tests.py b/account/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/account/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/account/urls.py b/account/urls.py new file mode 100644 index 0000000..b61e0e2 --- /dev/null +++ b/account/urls.py @@ -0,0 +1,81 @@ +# -*- coding: utf-8 -*- + +""" +urls.py for app 'account' +customize 'registration.backends.default.urls' to use custom form +""" + +from django.conf.urls import patterns, include, url +from django.views.generic.base import TemplateView +from django.contrib.auth.decorators import login_required + +from registration.backends.default.views import ActivationView +from registration.backends.default.views import RegistrationView + +from account.views import ProfileView, UpdateProfileView +from account.forms import UserRegForm + + +urlpatterns = patterns('', + ## profile + url(r'^profile/$', + login_required(ProfileView.as_view()), + name='profile'), + # update profile + url(r'^profile/update/$', + login_required(UpdateProfileView.as_view()), + name='profile_update'), + # update profile done + url(r'^profile/update/done/$', + login_required(TemplateView.as_view(template_name='account/profile_update_done.html')), + name='profile_update_done'), + ## django auth views + # login + url(r'^login/$', 'django.contrib.auth.views.login', + {'template_name': 'account/login.html'}, + name='login'), + # logout + url(r'^logout/$', 'django.contrib.auth.views.logout', + {'template_name': 'account/logout.html'}, + name='logout'), + # change password + # If 'post_change_redirect' not provided, + # then redirect to url 'password_change_done'. + url(r'^password/change/$', 'django.contrib.auth.views.password_change', + {'template_name': 'account/password_change.html'}, + name='password_change'), + # change password done + url(r'^password/change/done$', 'django.contrib.auth.views.password_change_done', + {'template_name': 'account/password_change_done.html'}, + name='password_change_done'), +) + +urlpatterns += patterns('', + ## django-registration + # 0. registration_disallowed + url(r'^register/closed/$', + TemplateView.as_view(template_name='registration/registration_closed.html'), + name='registration_disallowed'), + # 1. registration_register + url(r'^register/$', + RegistrationView.as_view(form_class=UserRegForm), + name='registration_register'), + # 2. registration_complete + url(r'^register/complete/$', + TemplateView.as_view(template_name='registration/registration_complete.html'), + name='registration_complete'), + # 4. registration_activation_complete + url(r'^activate/complete/$', + TemplateView.as_view(template_name='registration/activation_complete.html'), + name='registration_activation_complete'), + # 3. registration_activate (place this section *AFTER* step 4) + # Activation keys get matched by \w+ instead of the more specific + # [a-fA-F0-9]{40} because a bad activation key should still get to the view; + # that way it can return a sensible "invalid key" message instead of a + # confusing 404. + url(r'^activate/(?P<activation_key>\w+)/$', + ActivationView.as_view(), + name='registration_activate'), +) + + diff --git a/account/views.py b/account/views.py new file mode 100644 index 0000000..e6b90b1 --- /dev/null +++ b/account/views.py @@ -0,0 +1,68 @@ +# -*- coding: utf-8 -*- + +""" +views.py of app 'account' +""" + +from django.shortcuts import render +from django.views.generic.base import TemplateView +from django.views.generic.edit import UpdateView +from django.core.urlresolvers import reverse_lazy +from django.http import HttpResponseRedirect + +from account.models import UserProfile +from account.forms import UpdateProfileForm + + +###### Class-based views ###### +class ProfileView(TemplateView): + """ + class view to show profile page + """ + template_name = 'account/profile.html' + + def get_context_data(self, **kwargs): + context = super(ProfileView, self).get_context_data(**kwargs) + user = self.request.user + profile = user.userprofile_set.get(user=user) + context['user'] = user + context['profile'] = profile + return context + +class UpdateProfileView(UpdateView): + form_class = UpdateProfileForm + model = UserProfile + template_name = 'account/profile_update.html' + success_url = reverse_lazy('profile_update_done') + + # get profile object + def get_object(self, queryset=None): + user = self.request.user + profile = user.userprofile_set.get(user=user) + return profile + + def get(self, request, *args, **kwargs): + """ + Returns the keyword arguments for instantiating the form. + modify this method to add 'email' data + """ + self.object = self.get_object() + form_class = self.get_form_class() + form = self.get_form(form_class) + # initialize form 'email' field + user = self.request.user + form.fields['email'].initial = user.email + return self.render_to_response(self.get_context_data(form=form)) + + def form_valid(self, form): + """ + modify 'form_valid' to update email field + """ + form_data = form.cleaned_data + # update email and save + user = self.request.user + user.email = form_data.get('email', user.email) + user.save() + return super(UpdateProfileView, self).form_valid(form) + + |