aboutsummaryrefslogtreecommitdiffstats
path: root/account
diff options
context:
space:
mode:
Diffstat (limited to 'account')
-rw-r--r--account/__init__.py0
-rw-r--r--account/admin.py3
-rw-r--r--account/forms.py54
-rw-r--r--account/models.py81
-rw-r--r--account/templates/account/login.html24
-rw-r--r--account/templates/account/logout.html19
-rw-r--r--account/templates/account/password_change.html24
-rw-r--r--account/templates/account/password_change_done.html21
-rw-r--r--account/templates/account/profile.html83
-rw-r--r--account/templates/account/profile_update.html24
-rw-r--r--account/templates/account/profile_update_done.html21
-rw-r--r--account/tests.py3
-rw-r--r--account/urls.py81
-rw-r--r--account/views.py68
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)
+
+