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