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

« back to all changes in this revision

Viewing changes to ivle/webapp/admin/user.py

ivle.webapp.console.service: Port www/apps/consoleservice to new framework.
    consoleservice now lives under /console/service, and actions are taken
    in the "ivle.op" query argument, not as the last segment of the path.
    Otherwise the interface is identical.
www/apps/console/console.js: Update to new consoleservice interface.

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
 
 
24
 
from ivle.webapp import ApplicationRoot
25
 
from ivle.webapp.base.xhtml import XHTMLView
26
 
from ivle.webapp.base.plugins import ViewPlugin, MediaPlugin
27
 
from ivle.webapp.admin.publishing import root_to_user, user_url
28
 
from ivle.database import User
29
 
import ivle.date
30
 
 
31
 
 
32
 
class UsersView(XHTMLView):
33
 
    """A list of all IVLE users."""
34
 
    template = 'templates/users.html'
35
 
    tab = 'users'
36
 
    breadcrumb_text = 'Users'
37
 
 
38
 
    def authorize(self, req):
39
 
        return req.user and req.user.admin
40
 
 
41
 
    def populate(self, req, ctx):
42
 
        ctx['req'] = req
43
 
        ctx['users'] = req.store.find(User).order_by(User.login)
44
 
 
45
 
 
46
 
class UserEditSchema(formencode.Schema):
47
 
    nick = formencode.validators.UnicodeString(not_empty=True)
48
 
    email = formencode.validators.Email(not_empty=False,
49
 
                                        if_missing=None)
50
 
 
51
 
class UserEditView(XHTMLView):
52
 
    """A form to change a user's details."""
53
 
    template = 'templates/user-edit.html'
54
 
    tab = 'users'
55
 
    permission = 'edit'
56
 
 
57
 
    def filter(self, stream, ctx):
58
 
        return stream | HTMLFormFiller(data=ctx['data'])
59
 
 
60
 
    def populate(self, req, ctx):
61
 
        if req.method == 'POST':
62
 
            data = dict(req.get_fieldstorage())
63
 
            try:
64
 
                validator = UserEditSchema()
65
 
                data = validator.to_python(data, state=req)
66
 
                self.context.nick = data['nick']
67
 
                self.context.email = unicode(data['email']) if data['email'] \
68
 
                                     else None
69
 
                req.store.commit()
70
 
                req.throw_redirect(req.uri)
71
 
            except formencode.Invalid, e:
72
 
                errors = e.unpack_errors()
73
 
        else:
74
 
            data = {'nick': self.context.nick,
75
 
                    'email': self.context.email
76
 
                   }
77
 
            errors = {}
78
 
 
79
 
        ctx['format_datetime'] = ivle.date.make_date_nice
80
 
        ctx['format_datetime_short'] = ivle.date.format_datetime_for_paragraph
81
 
 
82
 
        ctx['req'] = req
83
 
        ctx['user'] = self.context
84
 
        ctx['data'] = data
85
 
        ctx['errors'] = errors
86
 
 
87
 
class UserAdminSchema(formencode.Schema):
88
 
    admin = formencode.validators.StringBoolean(if_missing=False)
89
 
    disabled = formencode.validators.StringBoolean(if_missing=False)
90
 
    fullname = formencode.validators.UnicodeString(not_empty=True)
91
 
    studentid = formencode.validators.UnicodeString(not_empty=False,
92
 
                                                    if_missing=None
93
 
                                                    )
94
 
 
95
 
class UserAdminView(XHTMLView):
96
 
    """A form for admins to change more of a user's details."""
97
 
    template = 'templates/user-admin.html'
98
 
    tab = 'users'
99
 
 
100
 
    def authorize(self, req):
101
 
        """Only allow access if the requesting user is an admin."""
102
 
        return req.user and req.user.admin
103
 
 
104
 
    def filter(self, stream, ctx):
105
 
        return stream | HTMLFormFiller(data=ctx['data'])
106
 
 
107
 
    def populate(self, req, ctx):
108
 
        if req.method == 'POST':
109
 
            data = dict(req.get_fieldstorage())
110
 
            try:
111
 
                validator = UserAdminSchema()
112
 
                data = validator.to_python(data, state=req)
113
 
 
114
 
                if self.context is req.user:
115
 
                    # Admin checkbox is disabled -- assume unchanged
116
 
                    data['admin'] = self.context.admin
117
 
                    data['disabled'] = self.context.state == u'disabled'
118
 
                else:
119
 
                    self.context.admin = data['admin']
120
 
                    if self.context.state in (u'enabled', u'disabled'):
121
 
                        self.context.state = (u'disabled' if data['disabled']
122
 
                                else u'enabled')
123
 
                self.context.fullname = data['fullname'] \
124
 
                                        if data['fullname'] else None
125
 
                self.context.studentid = data['studentid'] \
126
 
                                         if data['studentid'] else None
127
 
                req.store.commit()
128
 
                req.throw_redirect(req.uri)
129
 
            except formencode.Invalid, e:
130
 
                errors = e.unpack_errors()
131
 
        else:
132
 
            data = {'admin': self.context.admin,
133
 
                    'disabled': self.context.state == u'disabled',
134
 
                    'fullname': self.context.fullname,
135
 
                    'studentid': self.context.studentid,
136
 
                   }
137
 
            errors = {}
138
 
 
139
 
        ctx['req'] = req
140
 
        ctx['user'] = self.context
141
 
        # Disable the Admin checkbox if editing oneself
142
 
        ctx['disable_admin'] = self.context is req.user
143
 
        ctx['data'] = data
144
 
        ctx['errors'] = errors
145
 
 
146
 
class PasswordChangeView(XHTMLView):
147
 
    """A form to change a user's password, with knowledge of the old one."""
148
 
    template = 'templates/user-password-change.html'
149
 
    tab = 'users'
150
 
    permission = 'edit'
151
 
 
152
 
    def authorize(self, req):
153
 
        """Only allow access if the requesting user holds the permission,
154
 
           and the target user has a password set. Otherwise we might be
155
 
           clobbering external authn.
156
 
        """
157
 
        return super(PasswordChangeView, self).authorize(req) and \
158
 
               self.context.passhash is not None
159
 
 
160
 
    def populate(self, req, ctx):
161
 
        error = None
162
 
        if req.method == 'POST':
163
 
            data = dict(req.get_fieldstorage())
164
 
            if data.get('old_password') is None or \
165
 
               not self.context.authenticate(data.get('old_password')):
166
 
                error = 'Incorrect password.'
167
 
            elif data.get('new_password') != data.get('new_password_again'):
168
 
                error = 'New passwords do not match.'
169
 
            elif not data.get('new_password'):
170
 
                error = 'New password cannot be empty.'
171
 
            else:
172
 
                self.context.password = data['new_password']
173
 
                req.store.commit()
174
 
                req.throw_redirect(req.uri)
175
 
 
176
 
        ctx['req'] = req
177
 
        ctx['user'] = self.context
178
 
        ctx['error'] = error
179
 
 
180
 
class PasswordResetView(XHTMLView):
181
 
    """A form to reset a user's password, without knowledge of the old one."""
182
 
    template = 'templates/user-password-reset.html'
183
 
    tab = 'users'
184
 
 
185
 
    def authorize(self, req):
186
 
        """Only allow access if the requesting user is an admin."""
187
 
        return req.user and req.user.admin
188
 
 
189
 
    def populate(self, req, ctx):
190
 
        error = None
191
 
        if req.method == 'POST':
192
 
            data = dict(req.get_fieldstorage())
193
 
            if data.get('new_password') != data.get('new_password_again'):
194
 
                error = 'New passwords do not match.'
195
 
            elif not data.get('new_password'):
196
 
                error = 'New password cannot be empty.'
197
 
            else:
198
 
                self.context.password = data['new_password']
199
 
                req.store.commit()
200
 
                req.throw_redirect(req.uri)
201
 
 
202
 
        ctx['user'] = self.context
203
 
        ctx['error'] = error
204
 
 
205
 
class Plugin(ViewPlugin, MediaPlugin):
 
20
from ivle.webapp.base.views import JSONRESTView
 
21
from ivle.webapp.base.plugins import BasePlugin
 
22
import ivle.database
 
23
import ivle.util
 
24
 
 
25
# List of fields returned as part of the user JSON dictionary
 
26
# (as returned by the get_user action)
 
27
user_fields_list = (
 
28
    "login", "state", "unixid", "email", "nick", "fullname",
 
29
    "rolenm", "studentid", "acct_exp", "pass_exp", "last_login",
 
30
    "svn_pass"
 
31
)
 
32
 
 
33
class UserRESTView(JSONRESTView):
 
34
    """
 
35
    A REST interface to the user object.
 
36
    """
 
37
    def __init__(self, req, login):
 
38
        super(UserRESTView, self).__init__(self, req, login)
 
39
        self.context = ivle.database.User.get_by_login(req.store, login)
 
40
 
 
41
    def GET(self, req):
 
42
        # XXX Check Caps
 
43
        user = ivle.util.object_to_dict(user_fields_list, self.context)
 
44
        # Convert time stamps to nice strings
 
45
        for k in 'pass_exp', 'acct_exp', 'last_login':
 
46
            if user[k] is not None:
 
47
                user[k] = unicode(user[k])
 
48
 
 
49
        user['local_password'] = self.context.passhash is not None
 
50
        return user
 
51
 
 
52
    def PATCH(self, req, data):
 
53
        # XXX Check Caps
 
54
        # XXX Admins can set extra fields
 
55
        # Note: Cannot change password here (use change_password named op)
 
56
 
 
57
        for f in user_fields_list:
 
58
            try:
 
59
                field = data[f]
 
60
                if isinstance(field, str):
 
61
                    field = unicode(field)
 
62
                setattr(self.context, f, field)
 
63
            except KeyError:
 
64
                continue
 
65
 
 
66
class Plugin(BasePlugin):
206
67
    """
207
68
    The Plugin class for the user plugin.
208
69
    """
209
 
 
210
 
    forward_routes = (root_to_user,)
211
 
    reverse_routes = (user_url,)
212
 
    views = [(ApplicationRoot, 'users', UsersView),
213
 
             (User, '+index', UserEditView),
214
 
             (User, '+admin', UserAdminView),
215
 
             (User, '+changepassword', PasswordChangeView),
216
 
             (User, '+resetpassword', PasswordResetView),
217
 
             ]
218
 
 
219
 
    tabs = [
220
 
        ('users', 'Users', 'Display and edit all users',
221
 
         'users.png', 'users', 90, True)
 
70
    # Magic attribute: urls
 
71
    # Sequence of pairs/triples of
 
72
    # (regex str, handler class, kwargs dict)
 
73
    # The kwargs dict is passed to the __init__ of the view object
 
74
    urls = [
 
75
        ('api/users/:login', UserRESTView)
222
76
    ]
223
 
 
224
 
    public_forward_routes = forward_routes
225
 
    public_reverse_routes = reverse_routes
226
 
 
227
 
    media = 'user-media'