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

« back to all changes in this revision

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

ivle.webapp.filesystem.diff: Port www/apps/diff to new framework. This moves
    all of the presentation logic into the app, with the service returning
    just a raw diff. This now runs the service using ivle.interpret#execute_raw

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