~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: 2009-12-08 03:50:24 UTC
  • mfrom: (1294.2.143 ui-the-third)
  • Revision ID: grantw@unimelb.edu.au-20091208035024-wjx8zp54gth15ph8
Merge ui-the-third. This is another UI revamp.

The header is now thin! Thin! The yellow bar is gone. The tabs are gone.
Breadcrumbs are here. Routes is replaced (with an object publishing
mechanism). Views are less repetitive. etc.

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
 
18
18
# Author: Matt Giuca, Will Grant
19
19
 
 
20
import formencode
 
21
import formencode.validators
 
22
from genshi.filters import HTMLFormFiller
 
23
 
20
24
from ivle.webapp.base.rest import JSONRESTView, require_permission
21
25
from ivle.webapp.base.xhtml import XHTMLView
22
26
from ivle.webapp.base.plugins import ViewPlugin, MediaPlugin
23
 
from ivle.webapp.errors import NotFound, Unauthorized
 
27
from ivle.webapp.admin.publishing import root_to_user, user_url
24
28
import ivle.database
 
29
import ivle.date
25
30
import ivle.util
26
31
 
27
32
# List of fields returned as part of the user JSON dictionary
36
41
    """
37
42
    A REST interface to the user object.
38
43
    """
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
44
 
45
45
    @require_permission('view')
46
46
    def GET(self, req):
54
54
        user['local_password'] = self.context.passhash is not None
55
55
        return user
56
56
 
57
 
class UserSettingsView(XHTMLView):
58
 
    template = 'templates/user-settings.html'
59
 
    tab = 'settings'
60
 
    permission = 'edit'
61
 
 
62
 
    def __init__(self, req, login):
63
 
        self.context = ivle.database.User.get_by_login(req.store, login)
64
 
        if self.context is None:
65
 
            raise NotFound()
66
 
 
67
 
    def populate(self, req, ctx):
68
 
        self.plugin_scripts[Plugin] = ['settings.js']
69
 
        self.scripts_init = ['revert_settings']
70
 
 
71
 
        ctx['login'] = self.context.login
 
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
 
 
90
        ctx['format_datetime'] = ivle.date.make_date_nice
 
91
        ctx['format_datetime_short'] = ivle.date.format_datetime_for_paragraph
 
92
 
 
93
        ctx['req'] = req
 
94
        ctx['user'] = self.context
 
95
        ctx['data'] = data
 
96
        ctx['errors'] = errors
 
97
 
 
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
 
 
145
class PasswordChangeView(XHTMLView):
 
146
    """A form to change a user's password, with knowledge of the old one."""
 
147
    template = 'templates/user-password-change.html'
 
148
    tab = 'settings'
 
149
    permission = 'edit'
 
150
 
 
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
 
 
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
 
 
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
 
 
201
        ctx['user'] = self.context
 
202
        ctx['error'] = error
72
203
 
73
204
class Plugin(ViewPlugin, MediaPlugin):
74
205
    """
75
206
    The Plugin class for the user plugin.
76
207
    """
77
 
    # Magic attribute: urls
78
 
    # Sequence of pairs/triples of
79
 
    # (regex str, handler class, kwargs dict)
80
 
    # The kwargs dict is passed to the __init__ of the view object
81
 
    urls = [
82
 
        ('~:login/+settings', UserSettingsView),
83
 
        ('api/~:login', UserRESTView),
84
 
    ]
 
208
 
 
209
    forward_routes = (root_to_user,)
 
210
    reverse_routes = (user_url,)
 
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),
 
215
             (ivle.database.User, '+index', UserRESTView, 'api'),
 
216
             ]
 
217
 
 
218
    public_forward_routes = forward_routes
 
219
    public_reverse_routes = reverse_routes
85
220
 
86
221
    media = 'user-media'