~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: 2012-06-28 01:52:02 UTC
  • Revision ID: me@williamgrant.id.au-20120628015202-f6ru7o367gt6nvgz
Hah

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, require_permission
 
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
21
29
from ivle.webapp.base.xhtml import XHTMLView
22
30
from ivle.webapp.base.plugins import ViewPlugin, MediaPlugin
23
 
from ivle.webapp.errors import NotFound, Unauthorized
24
 
import ivle.database
25
 
import ivle.util
26
 
 
27
 
# List of fields returned as part of the user JSON dictionary
28
 
# (as returned by the get_user action)
29
 
user_fields_list = (
30
 
    "login", "state", "unixid", "email", "nick", "fullname",
31
 
    "admin", "studentid", "acct_exp", "pass_exp", "last_login",
32
 
    "svn_pass"
33
 
)
34
 
 
35
 
class UserRESTView(JSONRESTView):
36
 
    """
37
 
    A REST interface to the user object.
38
 
    """
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
 
 
45
 
    @require_permission('view')
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
 
 
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
 
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
        ctx['svn_url'] = req.user.get_svn_url(req.config)
 
79
        ctx['svn_pass'] = req.user.svn_pass
 
80
 
 
81
 
 
82
class UserAdminSchema(formencode.Schema):
 
83
    admin = formencode.validators.StringBoolean(if_missing=False)
 
84
    disabled = formencode.validators.StringBoolean(if_missing=False)
 
85
    fullname = formencode.validators.UnicodeString(not_empty=True)
 
86
    studentid = formencode.validators.UnicodeString(not_empty=False,
 
87
                                                    if_missing=None
 
88
                                                    )
 
89
 
 
90
class UserAdminView(BaseFormView):
 
91
    """A form for admins to change more of a user's details."""
 
92
    template = 'templates/user-admin.html'
 
93
    tab = 'users'
 
94
 
 
95
    def authorize(self, req):
 
96
        """Only allow access if the requesting user is an admin."""
 
97
        return req.user and req.user.admin
 
98
 
 
99
    @property
 
100
    def validator(self):
 
101
        return UserAdminSchema()
 
102
 
 
103
    def get_default_data(self, req):
 
104
        return {'admin': self.context.admin,
 
105
                'disabled': self.context.state == u'disabled',
 
106
                'fullname': self.context.fullname,
 
107
                'studentid': self.context.studentid,
 
108
                }
 
109
 
 
110
    def save_object(self, req, data):
 
111
        if self.context is req.user:
 
112
            # Admin checkbox is disabled -- assume unchanged
 
113
            data['admin'] = self.context.admin
 
114
            data['disabled'] = self.context.state == u'disabled'
 
115
        else:
 
116
            self.context.admin = data['admin']
 
117
            if self.context.state in (u'enabled', u'disabled'):
 
118
                self.context.state = (u'disabled' if data['disabled']
 
119
                        else u'enabled')
 
120
        self.context.fullname = data['fullname'] \
 
121
                                if data['fullname'] else None
 
122
        self.context.studentid = data['studentid'] \
 
123
                                 if data['studentid'] else None
 
124
        return self.context
 
125
 
 
126
    def populate(self, req, ctx):
 
127
        super(UserAdminView, self).populate(req, ctx)
 
128
 
 
129
        # Disable the admin checkbox if editing oneself
 
130
        ctx['disable_admin'] = self.context is req.user
 
131
 
 
132
class PasswordChangeView(XHTMLView):
 
133
    """A form to change a user's password, with knowledge of the old one."""
 
134
    template = 'templates/user-password-change.html'
 
135
    tab = 'users'
 
136
    permission = 'edit'
 
137
 
 
138
    def authorize(self, req):
 
139
        """Only allow access if the requesting user holds the permission,
 
140
           and the target user has a password set. Otherwise we might be
 
141
           clobbering external authn.
 
142
        """
 
143
        return super(PasswordChangeView, self).authorize(req) and \
 
144
               self.context.passhash is not None
 
145
 
 
146
    def populate(self, req, ctx):
 
147
        error = None
 
148
        if req.method == 'POST':
 
149
            data = dict(req.get_fieldstorage())
 
150
            if data.get('old_password') is None or \
 
151
               not self.context.authenticate(data.get('old_password')):
 
152
                error = 'Incorrect password.'
 
153
            elif data.get('new_password') != data.get('new_password_again'):
 
154
                error = 'New passwords do not match.'
 
155
            elif not data.get('new_password'):
 
156
                error = 'New password cannot be empty.'
 
157
            else:
 
158
                self.context.password = data['new_password']
 
159
                req.store.commit()
 
160
                req.throw_redirect(req.uri)
 
161
 
 
162
        ctx['req'] = req
 
163
        ctx['user'] = self.context
 
164
        ctx['error'] = error
 
165
 
 
166
class PasswordResetView(XHTMLView):
 
167
    """A form to reset a user's password, without knowledge of the old one."""
 
168
    template = 'templates/user-password-reset.html'
 
169
    tab = 'users'
 
170
 
 
171
    def authorize(self, req):
 
172
        """Only allow access if the requesting user is an admin."""
 
173
        return req.user and req.user.admin
 
174
 
 
175
    def populate(self, req, ctx):
 
176
        error = None
 
177
        if req.method == 'POST':
 
178
            data = dict(req.get_fieldstorage())
 
179
            if data.get('new_password') != data.get('new_password_again'):
 
180
                error = 'New passwords do not match.'
 
181
            elif not data.get('new_password'):
 
182
                error = 'New password cannot be empty.'
 
183
            else:
 
184
                self.context.password = data['new_password']
 
185
                req.store.commit()
 
186
                req.throw_redirect(req.uri)
 
187
 
 
188
        ctx['user'] = self.context
 
189
        ctx['error'] = error
 
190
 
 
191
 
 
192
class UserNewSchema(formencode.Schema):
 
193
    login = URLNameValidator() # XXX: Validate uniqueness.
 
194
    admin = formencode.validators.StringBoolean(if_missing=False)
 
195
    fullname = formencode.validators.UnicodeString(not_empty=True)
 
196
    studentid = formencode.validators.UnicodeString(not_empty=False,
 
197
                                                    if_missing=None
 
198
                                                    )
 
199
    email = formencode.validators.Email(not_empty=False,
 
200
                                        if_missing=None)
 
201
 
 
202
 
 
203
class UserNewView(BaseFormView):
 
204
    """A form for admins to create new users."""
 
205
    template = 'templates/user-new.html'
 
206
    tab = 'users'
 
207
 
 
208
    def authorize(self, req):
 
209
        """Only allow access if the requesting user is an admin."""
 
210
        return req.user and req.user.admin
 
211
 
 
212
    @property
 
213
    def validator(self):
 
214
        return UserNewSchema()
 
215
 
 
216
    def get_default_data(self, req):
 
217
        return {}
 
218
 
 
219
    def save_object(self, req, data):
 
220
        data['nick'] = data['fullname']
 
221
        data['email'] = unicode(data['email']) if data['email'] else None
 
222
        userobj = User(**data)
 
223
        req.store.add(userobj)
 
224
        enrol_user(req.config, req.store, userobj)
 
225
 
 
226
        return userobj
 
227
 
72
228
 
73
229
class Plugin(ViewPlugin, MediaPlugin):
74
230
    """
75
231
    The Plugin class for the user plugin.
76
232
    """
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),
 
233
 
 
234
    forward_routes = (root_to_user,)
 
235
    reverse_routes = (user_url,)
 
236
    views = [(ApplicationRoot, ('users', '+index'), UsersView),
 
237
             (ApplicationRoot, ('users', '+new'), UserNewView),
 
238
             (User, '+index', UserEditView),
 
239
             (User, '+admin', UserAdminView),
 
240
             (User, '+changepassword', PasswordChangeView),
 
241
             (User, '+resetpassword', PasswordResetView),
 
242
             ]
 
243
 
 
244
    tabs = [
 
245
        ('users', 'Users', 'Display and edit all users',
 
246
         'users.png', 'users', 90, True)
84
247
    ]
85
248
 
 
249
    public_forward_routes = forward_routes
 
250
    public_reverse_routes = reverse_routes
 
251
 
86
252
    media = 'user-media'