~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-11 12:17:37 UTC
  • Revision ID: grantw@unimelb.edu.au-20100211121737-1zsmpp8i8rbyliku
Add a subject listing with new/edit icons.

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
 
18
18
# Author: Matt Giuca, Will Grant
19
19
 
20
 
from ivle.webapp.base.rest import JSONRESTView
 
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
21
26
from ivle.webapp.base.xhtml import XHTMLView
22
27
from ivle.webapp.base.plugins import ViewPlugin, MediaPlugin
23
 
from ivle.webapp.errors import NotFound, Unauthorized
 
28
from ivle.webapp.admin.publishing import root_to_user, user_url
 
29
from ivle.database import User
24
30
import ivle.database
 
31
import ivle.date
25
32
import ivle.util
26
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
 
27
49
# List of fields returned as part of the user JSON dictionary
28
50
# (as returned by the get_user action)
29
51
user_fields_list = (
30
52
    "login", "state", "unixid", "email", "nick", "fullname",
31
 
    "rolenm", "studentid", "acct_exp", "pass_exp", "last_login",
 
53
    "admin", "studentid", "acct_exp", "pass_exp", "last_login",
32
54
    "svn_pass"
33
55
)
34
56
 
36
58
    """
37
59
    A REST interface to the user object.
38
60
    """
39
 
    def __init__(self, req, login):
40
 
        super(UserRESTView, self).__init__(self, req, login)
41
 
        self.context = ivle.database.User.get_by_login(req.store, login)
42
 
        if self.context is None:
43
 
            raise NotFound()
44
61
 
 
62
    @require_permission('view')
45
63
    def GET(self, req):
46
64
        # XXX Check Caps
47
65
        user = ivle.util.object_to_dict(user_fields_list, self.context)
53
71
        user['local_password'] = self.context.passhash is not None
54
72
        return user
55
73
 
56
 
    def PATCH(self, req, data):
57
 
        # XXX Check Caps
58
 
        # XXX Admins can set extra fields
59
 
        # Note: Cannot change password here (use change_password named op)
60
 
 
61
 
        for f in user_fields_list:
62
 
            try:
63
 
                field = data[f]
64
 
                if isinstance(field, str):
65
 
                    field = unicode(field)
66
 
                setattr(self.context, f, field)
67
 
            except KeyError:
68
 
                continue
69
 
 
70
 
class UserSettingsView(XHTMLView):
71
 
    template = 'user-settings.html'
72
 
    appname = 'settings'
73
 
 
74
 
    def __init__(self, req, login):
75
 
        self.context = ivle.database.User.get_by_login(req.store, login)
76
 
        if self.context is None:
77
 
            raise NotFound()
78
 
 
79
 
        if req.user is None or (req.user is not self.context and
80
 
                                req.user.rolenm != 'admin'):
81
 
            raise Unauthorized()
82
 
 
83
 
    def populate(self, req, ctx):
84
 
        self.plugin_scripts[Plugin] = ['settings.js']
85
 
        req.scripts_init = ['revert_settings']
86
 
 
87
 
        ctx['login'] = self.context.login
 
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
88
232
 
89
233
class Plugin(ViewPlugin, MediaPlugin):
90
234
    """
91
235
    The Plugin class for the user plugin.
92
236
    """
93
 
    # Magic attribute: urls
94
 
    # Sequence of pairs/triples of
95
 
    # (regex str, handler class, kwargs dict)
96
 
    # The kwargs dict is passed to the __init__ of the view object
97
 
    urls = [
98
 
        ('~:login/+settings', UserSettingsView),
99
 
        ('api/~:login', UserRESTView),
 
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)
100
251
    ]
101
252
 
 
253
    public_forward_routes = forward_routes
 
254
    public_reverse_routes = reverse_routes
 
255
 
102
256
    media = 'user-media'