~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
1099.1.112 by William Grant
Implement authorization in JSON REST views. Add security declarations to
24
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
25
from ivle.webapp.base.xhtml import XHTMLView
1099.1.99 by William Grant
Require that plugins providing media subclass MediaPlugin.
26
from ivle.webapp.base.plugins import ViewPlugin, MediaPlugin
1099.1.96 by William Grant
Add an XHTMLUnauthorizedView which redirects unauthenticated users to the
27
from ivle.webapp.errors import NotFound, Unauthorized
1099.1.1 by Matt Giuca
Began implementing new dispatch framework (with Will Grant and Nick Chadwick).
28
import ivle.database
1294.1.7 by William Grant
Display account/password expiry times on +edit.
29
import ivle.date
1099.1.1 by Matt Giuca
Began implementing new dispatch framework (with Will Grant and Nick Chadwick).
30
import ivle.util
31
32
# List of fields returned as part of the user JSON dictionary
33
# (as returned by the get_user action)
34
user_fields_list = (
35
    "login", "state", "unixid", "email", "nick", "fullname",
1101 by William Grant
Privileges (apart from admin) are now offering-local, not global.
36
    "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).
37
    "svn_pass"
38
)
39
40
class UserRESTView(JSONRESTView):
41
    """
42
    A REST interface to the user object.
43
    """
44
    def __init__(self, req, login):
45
        super(UserRESTView, self).__init__(self, req, login)
46
        self.context = ivle.database.User.get_by_login(req.store, login)
1099.1.87 by William Grant
Fix views in ivle.webapp.admin and ivle.webapp.tutorial to return 404 wherever
47
        if self.context is None:
48
            raise NotFound()
1099.1.1 by Matt Giuca
Began implementing new dispatch framework (with Will Grant and Nick Chadwick).
49
1099.1.112 by William Grant
Implement authorization in JSON REST views. Add security declarations to
50
    @require_permission('view')
1099.1.1 by Matt Giuca
Began implementing new dispatch framework (with Will Grant and Nick Chadwick).
51
    def GET(self, req):
52
        # XXX Check Caps
53
        user = ivle.util.object_to_dict(user_fields_list, self.context)
54
        # Convert time stamps to nice strings
55
        for k in 'pass_exp', 'acct_exp', 'last_login':
56
            if user[k] is not None:
57
                user[k] = unicode(user[k])
58
59
        user['local_password'] = self.context.passhash is not None
60
        return user
61
1294.1.1 by William Grant
Add UserEditView, a non-AJAX partial replacement of UserSettingsView.
62
class UserEditSchema(formencode.Schema):
63
    nick = formencode.validators.UnicodeString(not_empty=True)
64
    email = formencode.validators.Email(not_empty=False,
65
                                        if_missing=None)
66
67
class UserEditView(XHTMLView):
68
    """A form to change a user's details."""
69
    template = 'templates/user-edit.html'
70
    tab = 'settings'
71
    permission = 'edit'
72
73
    def __init__(self, req, login):
74
        self.context = ivle.database.User.get_by_login(req.store, login)
75
        if self.context is None:
76
            raise NotFound()
77
78
    def filter(self, stream, ctx):
79
        return stream | HTMLFormFiller(data=ctx['data'])
80
81
    def populate(self, req, ctx):
82
        if req.method == 'POST':
83
            data = dict(req.get_fieldstorage())
84
            try:
85
                validator = UserEditSchema()
86
                data = validator.to_python(data, state=req)
87
                self.context.nick = data['nick']
88
                self.context.email = unicode(data['email']) if data['email'] \
89
                                     else None
90
                req.store.commit()
91
                req.throw_redirect(req.uri)
92
            except formencode.Invalid, e:
93
                errors = e.unpack_errors()
94
        else:
95
            data = {'nick': self.context.nick,
96
                    'email': self.context.email
97
                   }
98
            errors = {}
99
1294.1.7 by William Grant
Display account/password expiry times on +edit.
100
        ctx['format_datetime'] = ivle.date.make_date_nice
101
        ctx['format_datetime_short'] = ivle.date.format_datetime_for_paragraph
102
1294.1.5 by William Grant
Add a view for admins to reset other users' passwords.
103
        ctx['req'] = req
1294.1.1 by William Grant
Add UserEditView, a non-AJAX partial replacement of UserSettingsView.
104
        ctx['user'] = self.context
105
        ctx['data'] = data
106
        ctx['errors'] = errors
107
1294.1.9 by William Grant
Add a UserAdminView, to set fullname/studentid/admin.
108
class UserAdminSchema(formencode.Schema):
109
    admin = formencode.validators.StringBoolean(if_missing=False)
110
    fullname = formencode.validators.UnicodeString(not_empty=True)
111
    studentid = formencode.validators.UnicodeString(not_empty=False,
112
                                                    if_missing=None
113
                                                    )
114
115
class UserAdminView(XHTMLView):
116
    """A form for admins to change more of a user's details."""
117
    template = 'templates/user-admin.html'
118
    tab = 'settings'
119
120
    def __init__(self, req, login):
121
        self.context = ivle.database.User.get_by_login(req.store, login)
122
        if self.context is None:
123
            raise NotFound()
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
166
    def __init__(self, req, login):
167
        self.context = ivle.database.User.get_by_login(req.store, login)
168
        if self.context is None:
169
            raise NotFound()
170
1294.1.4 by William Grant
Forbid access to +changepassword if there is no passhash.
171
    def authorize(self, req):
172
        """Only allow access if the requesting user holds the permission,
173
           and the target user has a password set. Otherwise we might be
174
           clobbering external authn.
175
        """
176
        return super(PasswordChangeView, self).authorize(req) and \
177
               self.context.passhash is not None
178
1294.1.2 by William Grant
Add a non-AJAX password change view.
179
    def populate(self, req, ctx):
180
        error = None
181
        if req.method == 'POST':
182
            data = dict(req.get_fieldstorage())
183
            if data.get('old_password') is None or \
184
               not self.context.authenticate(data.get('old_password')):
185
                error = 'Incorrect password.'
186
            elif data.get('new_password') != data.get('new_password_again'):
187
                error = 'New passwords do not match.'
188
            elif not data.get('new_password'):
189
                error = 'New password cannot be empty.'
190
            else:
191
                self.context.password = data['new_password']
192
                req.store.commit()
193
                req.throw_redirect(req.uri)
194
1294.1.5 by William Grant
Add a view for admins to reset other users' passwords.
195
        ctx['req'] = req
196
        ctx['user'] = self.context
197
        ctx['error'] = error
198
199
class PasswordResetView(XHTMLView):
200
    """A form to reset a user's password, without knowledge of the old one."""
201
    template = 'templates/user-password-reset.html'
202
    tab = 'settings'
203
204
    def __init__(self, req, login):
205
        self.context = ivle.database.User.get_by_login(req.store, login)
206
        if self.context is None:
207
            raise NotFound()
208
209
    def authorize(self, req):
210
        """Only allow access if the requesting user is an admin."""
211
        return req.user.admin
212
213
    def populate(self, req, ctx):
214
        error = None
215
        if req.method == 'POST':
216
            data = dict(req.get_fieldstorage())
217
            if data.get('new_password') != data.get('new_password_again'):
218
                error = 'New passwords do not match.'
219
            elif not data.get('new_password'):
220
                error = 'New password cannot be empty.'
221
            else:
222
                self.context.password = data['new_password']
223
                req.store.commit()
224
                req.throw_redirect(req.uri)
225
1294.1.2 by William Grant
Add a non-AJAX password change view.
226
        ctx['user'] = self.context
227
        ctx['error'] = error
228
1099.1.99 by William Grant
Require that plugins providing media subclass MediaPlugin.
229
class Plugin(ViewPlugin, MediaPlugin):
1099.1.1 by Matt Giuca
Began implementing new dispatch framework (with Will Grant and Nick Chadwick).
230
    """
231
    The Plugin class for the user plugin.
232
    """
233
    # Magic attribute: urls
234
    # Sequence of pairs/triples of
235
    # (regex str, handler class, kwargs dict)
236
    # The kwargs dict is passed to the __init__ of the view object
237
    urls = [
1294.1.1 by William Grant
Add UserEditView, a non-AJAX partial replacement of UserSettingsView.
238
        ('~:login/+edit', UserEditView),
1294.1.9 by William Grant
Add a UserAdminView, to set fullname/studentid/admin.
239
        ('~:login/+admin', UserAdminView),
1294.1.2 by William Grant
Add a non-AJAX password change view.
240
        ('~:login/+changepassword', PasswordChangeView),
1294.1.5 by William Grant
Add a view for admins to reset other users' passwords.
241
        ('~:login/+resetpassword', PasswordResetView),
1099.1.51 by William Grant
Move /users/:login/+settings to /~:login/+settings.
242
        ('api/~:login', UserRESTView),
1099.1.1 by Matt Giuca
Began implementing new dispatch framework (with Will Grant and Nick Chadwick).
243
    ]
1099.1.61 by William Grant
Port ivle.webapp.admin.user's media to the new framework.
244
245
    media = 'user-media'