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

1099.1.1 by Matt Giuca
Began implementing new dispatch framework (with Will Grant and Nick Chadwick).
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
1294.1.1 by William Grant
Add UserEditView, a non-AJAX partial replacement of UserSettingsView.
20
import formencode
21
import formencode.validators
22
from genshi.filters import HTMLFormFiller
23
1375 by William Grant
Add a user list -- as yet unlinked.
24
from ivle.webapp import ApplicationRoot
1099.1.112 by William Grant
Implement authorization in JSON REST views. Add security declarations to
25
from ivle.webapp.base.rest import JSONRESTView, require_permission
1099.1.34 by William Grant
Split up ivle.webapp.base.views into ivle.webapp.base.{rest,xhtml}, as it was
26
from ivle.webapp.base.xhtml import XHTMLView
1099.1.99 by William Grant
Require that plugins providing media subclass MediaPlugin.
27
from ivle.webapp.base.plugins import ViewPlugin, MediaPlugin
1294.3.2 by William Grant
Router->Publisher
28
from ivle.webapp.admin.publishing import root_to_user, user_url
1375 by William Grant
Add a user list -- as yet unlinked.
29
from ivle.database import User
1099.1.1 by Matt Giuca
Began implementing new dispatch framework (with Will Grant and Nick Chadwick).
30
import ivle.database
1294.1.7 by William Grant
Display account/password expiry times on +edit.
31
import ivle.date
1099.1.1 by Matt Giuca
Began implementing new dispatch framework (with Will Grant and Nick Chadwick).
32
import ivle.util
33
1375 by William Grant
Add a user list -- as yet unlinked.
34
35
class UsersView(XHTMLView):
36
    """A list of all IVLE users."""
37
    template = 'templates/users.html'
38
39
    def authorize(self, req):
40
        return req.user.admin
41
42
    def populate(self, req, ctx):
43
        ctx['req'] = req
44
        ctx['users'] = req.store.find(User).order_by(User.login)
45
46
1099.1.1 by Matt Giuca
Began implementing new dispatch framework (with Will Grant and Nick Chadwick).
47
# List of fields returned as part of the user JSON dictionary
48
# (as returned by the get_user action)
49
user_fields_list = (
50
    "login", "state", "unixid", "email", "nick", "fullname",
1101 by William Grant
Privileges (apart from admin) are now offering-local, not global.
51
    "admin", "studentid", "acct_exp", "pass_exp", "last_login",
1099.1.1 by Matt Giuca
Began implementing new dispatch framework (with Will Grant and Nick Chadwick).
52
    "svn_pass"
53
)
54
55
class UserRESTView(JSONRESTView):
56
    """
57
    A REST interface to the user object.
58
    """
59
1099.1.112 by William Grant
Implement authorization in JSON REST views. Add security declarations to
60
    @require_permission('view')
1099.1.1 by Matt Giuca
Began implementing new dispatch framework (with Will Grant and Nick Chadwick).
61
    def GET(self, req):
62
        # XXX Check Caps
63
        user = ivle.util.object_to_dict(user_fields_list, self.context)
64
        # Convert time stamps to nice strings
65
        for k in 'pass_exp', 'acct_exp', 'last_login':
66
            if user[k] is not None:
67
                user[k] = unicode(user[k])
68
69
        user['local_password'] = self.context.passhash is not None
70
        return user
71
1294.1.1 by William Grant
Add UserEditView, a non-AJAX partial replacement of UserSettingsView.
72
class UserEditSchema(formencode.Schema):
73
    nick = formencode.validators.UnicodeString(not_empty=True)
74
    email = formencode.validators.Email(not_empty=False,
75
                                        if_missing=None)
76
77
class UserEditView(XHTMLView):
78
    """A form to change a user's details."""
79
    template = 'templates/user-edit.html'
80
    tab = 'settings'
81
    permission = 'edit'
82
83
    def filter(self, stream, ctx):
84
        return stream | HTMLFormFiller(data=ctx['data'])
85
86
    def populate(self, req, ctx):
87
        if req.method == 'POST':
88
            data = dict(req.get_fieldstorage())
89
            try:
90
                validator = UserEditSchema()
91
                data = validator.to_python(data, state=req)
92
                self.context.nick = data['nick']
93
                self.context.email = unicode(data['email']) if data['email'] \
94
                                     else None
95
                req.store.commit()
96
                req.throw_redirect(req.uri)
97
            except formencode.Invalid, e:
98
                errors = e.unpack_errors()
99
        else:
100
            data = {'nick': self.context.nick,
101
                    'email': self.context.email
102
                   }
103
            errors = {}
104
1294.1.7 by William Grant
Display account/password expiry times on +edit.
105
        ctx['format_datetime'] = ivle.date.make_date_nice
106
        ctx['format_datetime_short'] = ivle.date.format_datetime_for_paragraph
107
1294.1.5 by William Grant
Add a view for admins to reset other users' passwords.
108
        ctx['req'] = req
1294.1.1 by William Grant
Add UserEditView, a non-AJAX partial replacement of UserSettingsView.
109
        ctx['user'] = self.context
110
        ctx['data'] = data
111
        ctx['errors'] = errors
112
1294.1.9 by William Grant
Add a UserAdminView, to set fullname/studentid/admin.
113
class UserAdminSchema(formencode.Schema):
114
    admin = formencode.validators.StringBoolean(if_missing=False)
115
    fullname = formencode.validators.UnicodeString(not_empty=True)
116
    studentid = formencode.validators.UnicodeString(not_empty=False,
117
                                                    if_missing=None
118
                                                    )
119
120
class UserAdminView(XHTMLView):
121
    """A form for admins to change more of a user's details."""
122
    template = 'templates/user-admin.html'
123
    tab = 'settings'
124
125
    def authorize(self, req):
126
        """Only allow access if the requesting user is an admin."""
127
        return req.user.admin
128
129
    def filter(self, stream, ctx):
130
        return stream | HTMLFormFiller(data=ctx['data'])
131
132
    def populate(self, req, ctx):
133
        if req.method == 'POST':
134
            data = dict(req.get_fieldstorage())
135
            try:
136
                validator = UserAdminSchema()
137
                data = validator.to_python(data, state=req)
138
139
                self.context.admin = data['admin']
140
                self.context.fullname = data['fullname'] \
141
                                        if data['fullname'] else None
142
                self.context.studentid = data['studentid'] \
143
                                         if data['studentid'] else None
144
                req.store.commit()
145
                req.throw_redirect(req.uri)
146
            except formencode.Invalid, e:
147
                errors = e.unpack_errors()
148
        else:
149
            data = {'admin': self.context.admin,
150
                    'fullname': self.context.fullname,
151
                    'studentid': self.context.studentid,
152
                   }
153
            errors = {}
154
155
        ctx['req'] = req
156
        ctx['user'] = self.context
157
        ctx['data'] = data
158
        ctx['errors'] = errors
159
1294.1.2 by William Grant
Add a non-AJAX password change view.
160
class PasswordChangeView(XHTMLView):
1294.1.5 by William Grant
Add a view for admins to reset other users' passwords.
161
    """A form to change a user's password, with knowledge of the old one."""
1294.1.2 by William Grant
Add a non-AJAX password change view.
162
    template = 'templates/user-password-change.html'
163
    tab = 'settings'
164
    permission = 'edit'
165
1294.1.4 by William Grant
Forbid access to +changepassword if there is no passhash.
166
    def authorize(self, req):
167
        """Only allow access if the requesting user holds the permission,
168
           and the target user has a password set. Otherwise we might be
169
           clobbering external authn.
170
        """
171
        return super(PasswordChangeView, self).authorize(req) and \
172
               self.context.passhash is not None
173
1294.1.2 by William Grant
Add a non-AJAX password change view.
174
    def populate(self, req, ctx):
175
        error = None
176
        if req.method == 'POST':
177
            data = dict(req.get_fieldstorage())
178
            if data.get('old_password') is None or \
179
               not self.context.authenticate(data.get('old_password')):
180
                error = 'Incorrect password.'
181
            elif data.get('new_password') != data.get('new_password_again'):
182
                error = 'New passwords do not match.'
183
            elif not data.get('new_password'):
184
                error = 'New password cannot be empty.'
185
            else:
186
                self.context.password = data['new_password']
187
                req.store.commit()
188
                req.throw_redirect(req.uri)
189
1294.1.5 by William Grant
Add a view for admins to reset other users' passwords.
190
        ctx['req'] = req
191
        ctx['user'] = self.context
192
        ctx['error'] = error
193
194
class PasswordResetView(XHTMLView):
195
    """A form to reset a user's password, without knowledge of the old one."""
196
    template = 'templates/user-password-reset.html'
197
    tab = 'settings'
198
199
    def authorize(self, req):
200
        """Only allow access if the requesting user is an admin."""
201
        return req.user.admin
202
203
    def populate(self, req, ctx):
204
        error = None
205
        if req.method == 'POST':
206
            data = dict(req.get_fieldstorage())
207
            if data.get('new_password') != data.get('new_password_again'):
208
                error = 'New passwords do not match.'
209
            elif not data.get('new_password'):
210
                error = 'New password cannot be empty.'
211
            else:
212
                self.context.password = data['new_password']
213
                req.store.commit()
214
                req.throw_redirect(req.uri)
215
1294.1.2 by William Grant
Add a non-AJAX password change view.
216
        ctx['user'] = self.context
217
        ctx['error'] = error
218
1099.1.99 by William Grant
Require that plugins providing media subclass MediaPlugin.
219
class Plugin(ViewPlugin, MediaPlugin):
1099.1.1 by Matt Giuca
Began implementing new dispatch framework (with Will Grant and Nick Chadwick).
220
    """
221
    The Plugin class for the user plugin.
222
    """
1294.2.19 by William Grant
Port ivle.webapp.admin.user's views to the new dispatch system.
223
1294.2.70 by William Grant
Split out ivle.webapp.admin's routes into annotated functions in ivle.webapp.traversal.
224
    forward_routes = (root_to_user,)
225
    reverse_routes = (user_url,)
1375 by William Grant
Add a user list -- as yet unlinked.
226
    views = [(ApplicationRoot, 'users', UsersView),
227
             (ivle.database.User, '+index', UserEditView),
1294.2.140 by William Grant
Merge from trunk.
228
             (ivle.database.User, '+admin', UserAdminView),
229
             (ivle.database.User, '+changepassword', PasswordChangeView),
230
             (ivle.database.User, '+resetpassword', PasswordResetView),
1294.2.19 by William Grant
Port ivle.webapp.admin.user's views to the new dispatch system.
231
             (ivle.database.User, '+index', UserRESTView, 'api'),
232
             ]
1099.1.61 by William Grant
Port ivle.webapp.admin.user's media to the new framework.
233
1294.2.138 by William Grant
Publish users in public mode.
234
    public_forward_routes = forward_routes
235
    public_reverse_routes = reverse_routes
236
1099.1.61 by William Grant
Port ivle.webapp.admin.user's media to the new framework.
237
    media = 'user-media'