1
# IVLE - Informatics Virtual Learning Environment
2
# Copyright (C) 2007-2009 The University of Melbourne
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
# GNU General Public License for more details.
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
# Author: Matt Giuca, Will Grant
21
import formencode.validators
22
from genshi.filters import HTMLFormFiller
24
from ivle.webapp import ApplicationRoot
25
from ivle.webapp.base.forms import BaseFormView
26
from ivle.webapp.base.xhtml import XHTMLView
27
from ivle.webapp.base.plugins import ViewPlugin, MediaPlugin
28
from ivle.webapp.admin.publishing import root_to_user, user_url
29
from ivle.database import User
33
class UsersView(XHTMLView):
34
"""A list of all IVLE users."""
35
template = 'templates/users.html'
37
breadcrumb_text = 'Users'
39
def authorize(self, req):
40
return req.user and req.user.admin
42
def populate(self, req, ctx):
44
ctx['users'] = req.store.find(User).order_by(User.login)
47
class UserEditSchema(formencode.Schema):
48
nick = formencode.validators.UnicodeString(not_empty=True)
49
email = formencode.validators.Email(not_empty=False,
52
class UserEditView(BaseFormView):
53
"""A form to change a user's details."""
54
template = 'templates/user-edit.html'
60
return UserEditSchema()
62
def get_default_data(self, req):
63
return {'nick': self.context.nick,
64
'email': self.context.email
67
def save_object(self, req, data):
68
self.context.nick = data['nick']
69
self.context.email = unicode(data['email']) if data['email'] \
73
def populate(self, req, ctx):
74
super(UserEditView, self).populate(req, ctx)
75
ctx['format_datetime'] = ivle.date.make_date_nice
76
ctx['format_datetime_short'] = ivle.date.format_datetime_for_paragraph
79
class UserAdminSchema(formencode.Schema):
80
admin = formencode.validators.StringBoolean(if_missing=False)
81
disabled = formencode.validators.StringBoolean(if_missing=False)
82
fullname = formencode.validators.UnicodeString(not_empty=True)
83
studentid = formencode.validators.UnicodeString(not_empty=False,
87
class UserAdminView(BaseFormView):
88
"""A form for admins to change more of a user's details."""
89
template = 'templates/user-admin.html'
92
def authorize(self, req):
93
"""Only allow access if the requesting user is an admin."""
94
return req.user and req.user.admin
98
return UserAdminSchema()
100
def get_default_data(self, req):
101
return {'admin': self.context.admin,
102
'disabled': self.context.state == u'disabled',
103
'fullname': self.context.fullname,
104
'studentid': self.context.studentid,
107
def save_object(self, req, data):
108
if self.context is req.user:
109
# Admin checkbox is disabled -- assume unchanged
110
data['admin'] = self.context.admin
111
data['disabled'] = self.context.state == u'disabled'
113
self.context.admin = data['admin']
114
if self.context.state in (u'enabled', u'disabled'):
115
self.context.state = (u'disabled' if data['disabled']
117
self.context.fullname = data['fullname'] \
118
if data['fullname'] else None
119
self.context.studentid = data['studentid'] \
120
if data['studentid'] else None
123
def populate(self, req, ctx):
124
super(UserAdminView, self).populate(req, ctx)
126
# Disable the admin checkbox if editing oneself
127
ctx['disable_admin'] = self.context is req.user
129
class PasswordChangeView(XHTMLView):
130
"""A form to change a user's password, with knowledge of the old one."""
131
template = 'templates/user-password-change.html'
135
def authorize(self, req):
136
"""Only allow access if the requesting user holds the permission,
137
and the target user has a password set. Otherwise we might be
138
clobbering external authn.
140
return super(PasswordChangeView, self).authorize(req) and \
141
self.context.passhash is not None
143
def populate(self, req, ctx):
145
if req.method == 'POST':
146
data = dict(req.get_fieldstorage())
147
if data.get('old_password') is None or \
148
not self.context.authenticate(data.get('old_password')):
149
error = 'Incorrect password.'
150
elif data.get('new_password') != data.get('new_password_again'):
151
error = 'New passwords do not match.'
152
elif not data.get('new_password'):
153
error = 'New password cannot be empty.'
155
self.context.password = data['new_password']
157
req.throw_redirect(req.uri)
160
ctx['user'] = self.context
163
class PasswordResetView(XHTMLView):
164
"""A form to reset a user's password, without knowledge of the old one."""
165
template = 'templates/user-password-reset.html'
168
def authorize(self, req):
169
"""Only allow access if the requesting user is an admin."""
170
return req.user and req.user.admin
172
def populate(self, req, ctx):
174
if req.method == 'POST':
175
data = dict(req.get_fieldstorage())
176
if data.get('new_password') != data.get('new_password_again'):
177
error = 'New passwords do not match.'
178
elif not data.get('new_password'):
179
error = 'New password cannot be empty.'
181
self.context.password = data['new_password']
183
req.throw_redirect(req.uri)
185
ctx['user'] = self.context
188
class Plugin(ViewPlugin, MediaPlugin):
190
The Plugin class for the user plugin.
193
forward_routes = (root_to_user,)
194
reverse_routes = (user_url,)
195
views = [(ApplicationRoot, 'users', UsersView),
196
(User, '+index', UserEditView),
197
(User, '+admin', UserAdminView),
198
(User, '+changepassword', PasswordChangeView),
199
(User, '+resetpassword', PasswordResetView),
203
('users', 'Users', 'Display and edit all users',
204
'users.png', 'users', 90, True)
207
public_forward_routes = forward_routes
208
public_reverse_routes = reverse_routes