~azzar1/unity/add-show-desktop-key

« back to all changes in this revision

Viewing changes to ivle/webapp/admin/user.py

  • Committer: William Grant
  • Date: 2010-02-23 06:11:58 UTC
  • Revision ID: grantw@unimelb.edu.au-20100223061158-05cqg88387ymjtlj
Redo console CSS, and replace the examples on the help page with something more like the modern console that also doesn't break the real console with ID conflicts.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# IVLE - Informatics Virtual Learning Environment
 
2
# Copyright (C) 2007-2009 The University of Melbourne
 
3
#
 
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.
 
8
#
 
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.
 
13
#
 
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
 
17
 
 
18
# Author: Matt Giuca, Will Grant
 
19
 
 
20
import formencode
 
21
import formencode.validators
 
22
from genshi.filters import HTMLFormFiller
 
23
 
 
24
from ivle.webapp import ApplicationRoot
 
25
from ivle.webapp.base.rest import JSONRESTView, require_permission
 
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
 
30
import ivle.database
 
31
import ivle.date
 
32
import ivle.util
 
33
 
 
34
 
 
35
class UsersView(XHTMLView):
 
36
    """A list of all IVLE users."""
 
37
    template = 'templates/users.html'
 
38
    tab = 'users'
 
39
    breadcrumb_text = 'Users'
 
40
 
 
41
    def authorize(self, req):
 
42
        return req.user and req.user.admin
 
43
 
 
44
    def populate(self, req, ctx):
 
45
        ctx['req'] = req
 
46
        ctx['users'] = req.store.find(User).order_by(User.login)
 
47
 
 
48
 
 
49
# List of fields returned as part of the user JSON dictionary
 
50
# (as returned by the get_user action)
 
51
user_fields_list = (
 
52
    "login", "state", "unixid", "email", "nick", "fullname",
 
53
    "admin", "studentid", "acct_exp", "pass_exp", "last_login",
 
54
    "svn_pass"
 
55
)
 
56
 
 
57
class UserRESTView(JSONRESTView):
 
58
    """
 
59
    A REST interface to the user object.
 
60
    """
 
61
 
 
62
    @require_permission('view')
 
63
    def GET(self, req):
 
64
        # XXX Check Caps
 
65
        user = ivle.util.object_to_dict(user_fields_list, self.context)
 
66
        # Convert time stamps to nice strings
 
67
        for k in 'pass_exp', 'acct_exp', 'last_login':
 
68
            if user[k] is not None:
 
69
                user[k] = unicode(user[k])
 
70
 
 
71
        user['local_password'] = self.context.passhash is not None
 
72
        return user
 
73
 
 
74
class UserEditSchema(formencode.Schema):
 
75
    nick = formencode.validators.UnicodeString(not_empty=True)
 
76
    email = formencode.validators.Email(not_empty=False,
 
77
                                        if_missing=None)
 
78
 
 
79
class UserEditView(XHTMLView):
 
80
    """A form to change a user's details."""
 
81
    template = 'templates/user-edit.html'
 
82
    tab = 'users'
 
83
    permission = 'edit'
 
84
 
 
85
    def filter(self, stream, ctx):
 
86
        return stream | HTMLFormFiller(data=ctx['data'])
 
87
 
 
88
    def populate(self, req, ctx):
 
89
        if req.method == 'POST':
 
90
            data = dict(req.get_fieldstorage())
 
91
            try:
 
92
                validator = UserEditSchema()
 
93
                data = validator.to_python(data, state=req)
 
94
                self.context.nick = data['nick']
 
95
                self.context.email = unicode(data['email']) if data['email'] \
 
96
                                     else None
 
97
                req.store.commit()
 
98
                req.throw_redirect(req.uri)
 
99
            except formencode.Invalid, e:
 
100
                errors = e.unpack_errors()
 
101
        else:
 
102
            data = {'nick': self.context.nick,
 
103
                    'email': self.context.email
 
104
                   }
 
105
            errors = {}
 
106
 
 
107
        ctx['format_datetime'] = ivle.date.make_date_nice
 
108
        ctx['format_datetime_short'] = ivle.date.format_datetime_for_paragraph
 
109
 
 
110
        ctx['req'] = req
 
111
        ctx['user'] = self.context
 
112
        ctx['data'] = data
 
113
        ctx['errors'] = errors
 
114
 
 
115
class UserAdminSchema(formencode.Schema):
 
116
    admin = formencode.validators.StringBoolean(if_missing=False)
 
117
    disabled = formencode.validators.StringBoolean(if_missing=False)
 
118
    fullname = formencode.validators.UnicodeString(not_empty=True)
 
119
    studentid = formencode.validators.UnicodeString(not_empty=False,
 
120
                                                    if_missing=None
 
121
                                                    )
 
122
 
 
123
class UserAdminView(XHTMLView):
 
124
    """A form for admins to change more of a user's details."""
 
125
    template = 'templates/user-admin.html'
 
126
    tab = 'users'
 
127
 
 
128
    def authorize(self, req):
 
129
        """Only allow access if the requesting user is an admin."""
 
130
        return req.user and req.user.admin
 
131
 
 
132
    def filter(self, stream, ctx):
 
133
        return stream | HTMLFormFiller(data=ctx['data'])
 
134
 
 
135
    def populate(self, req, ctx):
 
136
        if req.method == 'POST':
 
137
            data = dict(req.get_fieldstorage())
 
138
            try:
 
139
                validator = UserAdminSchema()
 
140
                data = validator.to_python(data, state=req)
 
141
 
 
142
                if self.context is req.user:
 
143
                    # Admin checkbox is disabled -- assume unchanged
 
144
                    data['admin'] = self.context.admin
 
145
                    data['disabled'] = self.context.state == u'disabled'
 
146
                else:
 
147
                    self.context.admin = data['admin']
 
148
                    if self.context.state in (u'enabled', u'disabled'):
 
149
                        self.context.state = (u'disabled' if data['disabled']
 
150
                                else u'enabled')
 
151
                self.context.fullname = data['fullname'] \
 
152
                                        if data['fullname'] else None
 
153
                self.context.studentid = data['studentid'] \
 
154
                                         if data['studentid'] else None
 
155
                req.store.commit()
 
156
                req.throw_redirect(req.uri)
 
157
            except formencode.Invalid, e:
 
158
                errors = e.unpack_errors()
 
159
        else:
 
160
            data = {'admin': self.context.admin,
 
161
                    'disabled': self.context.state == u'disabled',
 
162
                    'fullname': self.context.fullname,
 
163
                    'studentid': self.context.studentid,
 
164
                   }
 
165
            errors = {}
 
166
 
 
167
        ctx['req'] = req
 
168
        ctx['user'] = self.context
 
169
        # Disable the Admin checkbox if editing oneself
 
170
        ctx['disable_admin'] = self.context is req.user
 
171
        ctx['data'] = data
 
172
        ctx['errors'] = errors
 
173
 
 
174
class PasswordChangeView(XHTMLView):
 
175
    """A form to change a user's password, with knowledge of the old one."""
 
176
    template = 'templates/user-password-change.html'
 
177
    tab = 'users'
 
178
    permission = 'edit'
 
179
 
 
180
    def authorize(self, req):
 
181
        """Only allow access if the requesting user holds the permission,
 
182
           and the target user has a password set. Otherwise we might be
 
183
           clobbering external authn.
 
184
        """
 
185
        return super(PasswordChangeView, self).authorize(req) and \
 
186
               self.context.passhash is not None
 
187
 
 
188
    def populate(self, req, ctx):
 
189
        error = None
 
190
        if req.method == 'POST':
 
191
            data = dict(req.get_fieldstorage())
 
192
            if data.get('old_password') is None or \
 
193
               not self.context.authenticate(data.get('old_password')):
 
194
                error = 'Incorrect password.'
 
195
            elif data.get('new_password') != data.get('new_password_again'):
 
196
                error = 'New passwords do not match.'
 
197
            elif not data.get('new_password'):
 
198
                error = 'New password cannot be empty.'
 
199
            else:
 
200
                self.context.password = data['new_password']
 
201
                req.store.commit()
 
202
                req.throw_redirect(req.uri)
 
203
 
 
204
        ctx['req'] = req
 
205
        ctx['user'] = self.context
 
206
        ctx['error'] = error
 
207
 
 
208
class PasswordResetView(XHTMLView):
 
209
    """A form to reset a user's password, without knowledge of the old one."""
 
210
    template = 'templates/user-password-reset.html'
 
211
    tab = 'users'
 
212
 
 
213
    def authorize(self, req):
 
214
        """Only allow access if the requesting user is an admin."""
 
215
        return req.user and req.user.admin
 
216
 
 
217
    def populate(self, req, ctx):
 
218
        error = None
 
219
        if req.method == 'POST':
 
220
            data = dict(req.get_fieldstorage())
 
221
            if data.get('new_password') != data.get('new_password_again'):
 
222
                error = 'New passwords do not match.'
 
223
            elif not data.get('new_password'):
 
224
                error = 'New password cannot be empty.'
 
225
            else:
 
226
                self.context.password = data['new_password']
 
227
                req.store.commit()
 
228
                req.throw_redirect(req.uri)
 
229
 
 
230
        ctx['user'] = self.context
 
231
        ctx['error'] = error
 
232
 
 
233
class Plugin(ViewPlugin, MediaPlugin):
 
234
    """
 
235
    The Plugin class for the user plugin.
 
236
    """
 
237
 
 
238
    forward_routes = (root_to_user,)
 
239
    reverse_routes = (user_url,)
 
240
    views = [(ApplicationRoot, 'users', UsersView),
 
241
             (ivle.database.User, '+index', UserEditView),
 
242
             (ivle.database.User, '+admin', UserAdminView),
 
243
             (ivle.database.User, '+changepassword', PasswordChangeView),
 
244
             (ivle.database.User, '+resetpassword', PasswordResetView),
 
245
             (ivle.database.User, '+index', UserRESTView, 'api'),
 
246
             ]
 
247
 
 
248
    tabs = [
 
249
        ('users', 'Users', 'Display and edit all users',
 
250
         'users.png', 'users', 90, True)
 
251
    ]
 
252
 
 
253
    public_forward_routes = forward_routes
 
254
    public_reverse_routes = reverse_routes
 
255
 
 
256
    media = 'user-media'