##// END OF EJS Templates
Rewrite of user managment, improved forms, added some user info
marcink -
r238:a55c1787 default
parent child Browse files
Show More
@@ -0,0 +1,48 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 #
4 # Copyright (c) 2010 marcink. All rights reserved.
5 #
6 from pylons_app.model.db import User
7 from pylons_app.model.meta import Session
8 '''
9 Created on Apr 9, 2010
10
11 @author: marcink
12 '''
13
14 class UserModel(object):
15
16 def __init__(self):
17 self.sa = Session()
18
19 def get_user(self, id):
20 return self.sa.query(User).get(id)
21
22 def create(self, form_data):
23 try:
24 new_user = User()
25 for k, v in form_data.items():
26 setattr(new_user, k, v)
27
28 self.sa.add(new_user)
29 self.sa.commit()
30 except:
31 self.sa.rollback()
32 raise
33
34 def update(self, id, form_data):
35 try:
36 new_user = self.sa.query(User).get(id)
37 for k, v in form_data.items():
38 if k == 'new_password' and v != '':
39
40 new_user.password = v
41 else:
42 setattr(new_user, k, v)
43
44 self.sa.add(new_user)
45 self.sa.commit()
46 except:
47 self.sa.rollback()
48 raise
@@ -1,113 +1,117 b''
1 1 from formencode import htmlfill
2 2 from pylons import request, response, session, tmpl_context as c, url, \
3 3 app_globals as g
4 from pylons.i18n.translation import _
5 from pylons_app.lib import helpers as h
4 6 from pylons.controllers.util import abort, redirect
5 7 from pylons_app.lib.auth import LoginRequired
6 8 from pylons_app.lib.base import BaseController, render
7 9 from pylons_app.model.db import User, UserLog
8 10 from pylons_app.model.forms import UserForm
9 11 from pylons_app.model.user_model import UserModel
10 12 import formencode
11 13 import logging
12 14
13 15
14 16
15 17 log = logging.getLogger(__name__)
16 18
17 19 class UsersController(BaseController):
18 20 """REST Controller styled on the Atom Publishing Protocol"""
19 21 # To properly map this controller, ensure your config/routing.py
20 22 # file has a resource setup:
21 23 # map.resource('user', 'users')
22 24 @LoginRequired()
23 25 def __before__(self):
24 26 c.admin_user = session.get('admin_user')
25 27 c.admin_username = session.get('admin_username')
26 28 super(UsersController, self).__before__()
27 29
28 30 def index(self, format='html'):
29 31 """GET /users: All items in the collection"""
30 32 # url('users')
31 33
32 34 c.users_list = self.sa.query(User).all()
33 35 return render('admin/users/users.html')
34 36
35 37 def create(self):
36 38 """POST /users: Create a new item"""
37 39 # url('users')
38 40
39 41 user_model = UserModel()
40 login_form = UserForm()
42 login_form = UserForm()()
41 43 try:
42 44 form_result = login_form.to_python(dict(request.POST))
43 45 user_model.create(form_result)
46 h.flash(_('created user %s') % form_result['username'], category='success')
44 47 return redirect(url('users'))
45 48
46 49 except formencode.Invalid as errors:
47 50 c.form_errors = errors.error_dict
48 51 return htmlfill.render(
49 52 render('admin/users/user_add.html'),
50 53 defaults=errors.value,
51 54 encoding="UTF-8")
52 55
53 56 def new(self, format='html'):
54 57 """GET /users/new: Form to create a new item"""
55 58 # url('new_user')
56 59 return render('admin/users/user_add.html')
57 60
58 61 def update(self, id):
59 62 """PUT /users/id: Update an existing item"""
60 63 # Forms posted to this method should contain a hidden field:
61 64 # <input type="hidden" name="_method" value="PUT" />
62 65 # Or using helpers:
63 66 # h.form(url('user', id=ID),
64 67 # method='put')
65 68 # url('user', id=ID)
66 69 user_model = UserModel()
67 login_form = UserForm()
70 login_form = UserForm(edit=True)()
68 71 try:
69 72 form_result = login_form.to_python(dict(request.POST))
70 73 user_model.update(id, form_result)
74 h.flash(_('User updated succesfully'), category='success')
71 75 return redirect(url('users'))
72 76
73 77 except formencode.Invalid as errors:
74 errors.value
75 78 c.user = user_model.get_user(id)
76 79 c.form_errors = errors.error_dict
77 80 return htmlfill.render(
78 81 render('admin/users/user_edit.html'),
79 82 defaults=errors.value,
80 83 encoding="UTF-8")
81 84
82 85 def delete(self, id):
83 86 """DELETE /users/id: Delete an existing item"""
84 87 # Forms posted to this method should contain a hidden field:
85 88 # <input type="hidden" name="_method" value="DELETE" />
86 89 # Or using helpers:
87 90 # h.form(url('user', id=ID),
88 91 # method='delete')
89 92 # url('user', id=ID)
90 93 try:
91 94 self.sa.delete(self.sa.query(User).get(id))
92 95 self.sa.commit()
96 h.flash(_('sucessfully deleted user'), category='success')
93 97 except:
94 98 self.sa.rollback()
95 99 raise
96 100 return redirect(url('users'))
97 101
98 102 def show(self, id, format='html'):
99 103 """GET /users/id: Show a specific item"""
100 104 # url('user', id=ID)
101 105
102 106
103 107 def edit(self, id, format='html'):
104 108 """GET /users/id/edit: Form to edit an existing item"""
105 109 # url('edit_user', id=ID)
106 110 c.user = self.sa.query(User).get(id)
107 111 defaults = c.user.__dict__
108 112 return htmlfill.render(
109 113 render('admin/users/user_edit.html'),
110 114 defaults=defaults,
111 115 encoding="UTF-8",
112 116 force_defaults=False
113 117 )
@@ -1,127 +1,155 b''
1 1 """ this is forms validation classes
2 2 http://formencode.org/module-formencode.validators.html
3 3 for list off all availible validators
4 4
5 5 we can create our own validators
6 6
7 7 The table below outlines the options which can be used in a schema in addition to the validators themselves
8 8 pre_validators [] These validators will be applied before the schema
9 9 chained_validators [] These validators will be applied after the schema
10 10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
11 11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
12 12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
13 13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
14 14
15 15
16 16 <name> = formencode.validators.<name of validator>
17 17 <name> must equal form name
18 18 list=[1,2,3,4,5]
19 19 for SELECT use formencode.All(OneOf(list), Int())
20 20
21 21 """
22 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex
22 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
23 Email, Bool, StringBoolean
24 from formencode import All
23 25 from pylons import session
24 26 from pylons.i18n.translation import _
25 27 from pylons_app.lib.auth import get_crypt_password
26 28 from pylons_app.model import meta
27 29 from pylons_app.model.db import User
28 30 from sqlalchemy.exc import OperationalError
29 31 from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
30 32 from webhelpers.pylonslib.secure_form import authentication_token
31 33 import formencode
32 34 import logging
33 35 log = logging.getLogger(__name__)
34 36
35 37
36 38 #this is needed to translate the messages using _() in validators
37 39 class State_obj(object):
38 40 _ = staticmethod(_)
39 41
40 42 #===============================================================================
41 43 # VALIDATORS
42 44 #===============================================================================
43 45 class ValidAuthToken(formencode.validators.FancyValidator):
44 46 messages = {'invalid_token':_('Token mismatch')}
45 47
46 48 def validate_python(self, value, state):
47 49
48 50 if value != authentication_token():
49 51 raise formencode.Invalid(self.message('invalid_token', state,
50 52 search_number=value), value, state)
53 class ValidUsername(formencode.validators.FancyValidator):
51 54
55 def validate_python(self, value, state):
56 pass
57
58 class ValidPassword(formencode.validators.FancyValidator):
59
60 def to_python(self, value, state):
61 return get_crypt_password(value)
62
52 63 class ValidAuth(formencode.validators.FancyValidator):
53 64 messages = {
54 65 'invalid_password':_('invalid password'),
55 66 'invalid_login':_('invalid user name'),
56 67 'disabled_account':_('Your acccount is disabled')
57 68
58 69 }
59 70 #error mapping
60 71 e_dict = {'username':messages['invalid_login'],
61 72 'password':messages['invalid_password']}
62 73 e_dict_disable = {'username':messages['disabled_account']}
63 74
64 75 def validate_python(self, value, state):
65 76 sa = meta.Session
66 77 crypted_passwd = get_crypt_password(value['password'])
67 78 username = value['username']
68 79 try:
69 80 user = sa.query(User).filter(User.username == username).one()
70 81 except (NoResultFound, MultipleResultsFound, OperationalError) as e:
71 82 log.error(e)
72 83 user = None
84 raise formencode.Invalid(self.message('invalid_password',
85 state=State_obj), value, state,
86 error_dict=self.e_dict)
73 87 if user:
74 88 if user.active:
75 89 if user.username == username and user.password == crypted_passwd:
76 90 from pylons_app.lib.auth import AuthUser
77 91 auth_user = AuthUser()
78 92 auth_user.username = username
79 93 auth_user.is_authenticated = True
80 94 auth_user.is_admin = user.admin
81 95 session['hg_app_user'] = auth_user
82 96 session.save()
83 97 log.info('user %s is now authenticated', username)
84 98 return value
85 99 else:
86 100 log.warning('user %s not authenticated', username)
87 101 raise formencode.Invalid(self.message('invalid_password',
88 102 state=State_obj), value, state,
89 103 error_dict=self.e_dict)
90 104 else:
91 105 log.warning('user %s is disabled', username)
92 106 raise formencode.Invalid(self.message('disabled_account',
93 107 state=State_obj),
94 108 value, state,
95 109 error_dict=self.e_dict_disable)
96 110
97 111
98 112
99 113 #===============================================================================
100 114 # FORMS
101 115 #===============================================================================
102 116 class LoginForm(formencode.Schema):
103 117 allow_extra_fields = True
104 118 filter_extra_fields = True
105 119 username = UnicodeString(
106 120 strip=True,
107 121 min=3,
108 122 not_empty=True,
109 123 messages={
110 124 'empty':_('Please enter a login'),
111 125 'tooShort':_('Enter a value %(min)i characters long or more')}
112 126 )
113 127
114 128 password = UnicodeString(
115 129 strip=True,
116 130 min=3,
117 131 not_empty=True,
118 132 messages={
119 133 'empty':_('Please enter a password'),
120 134 'tooShort':_('Enter a value %(min)i characters long or more')}
121 135 )
122 136
123 137
124 138 #chained validators have access to all data
125 139 chained_validators = [ValidAuth]
126 140
127
141 def UserForm(edit=False):
142 class _UserForm(formencode.Schema):
143 allow_extra_fields = True
144 filter_extra_fields = True
145 username = All(UnicodeString(strip=True, min=3, not_empty=True), ValidUsername)
146 if edit:
147 new_password = All(UnicodeString(strip=True, min=3, not_empty=False), ValidPassword)
148 else:
149 password = All(UnicodeString(strip=True, min=3, not_empty=False), ValidPassword)
150 active = StringBoolean(if_missing=False)
151 name = UnicodeString(strip=True, min=3, not_empty=True)
152 lastname = UnicodeString(strip=True, min=3, not_empty=True)
153 email = Email(not_empty=True)
154
155 return _UserForm
@@ -1,41 +1,58 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3
4 4 <%def name="title()">
5 5 ${_('User administration')}
6 6 </%def>
7 7 <%def name="breadcrumbs()">
8 8 ${h.link_to(u'Admin',h.url('admin_home'))}
9 9 /
10 ${_('Users')}
10 ${_('Users')}
11 11 </%def>
12 12 <%def name="page_nav()">
13 13 ${self.menu('admin')}
14 14 ${self.submenu('users')}
15 15 </%def>
16 16 <%def name="main()">
17 17 <div>
18 18 <h2>${_('User')} - ${_('add new')}</h2>
19 19 ${h.form(url('users'))}
20 20 <table>
21 21 <tr>
22 22 <td>${_('Username')}</td>
23 23 <td>${h.text('username')}</td>
24 <td>${self.get_form_error('username')}</td>
25 </tr>
26 <tr>
27 <td>${_('Password')}</td>
28 <td>${h.password('password')}</td>
29 <td>${self.get_form_error('password')}</td>
30 </tr>
31 <tr>
32 <td>${_('Name')}</td>
33 <td>${h.text('name')}</td>
34 <td>${self.get_form_error('name')}</td>
24 35 </tr>
25 36 <tr>
26 <td>${_('password')}</td>
27 <td>${h.text('password')}</td>
37 <td>${_('Lastname')}</td>
38 <td>${h.text('lastname')}</td>
39 <td>${self.get_form_error('lastname')}</td>
28 40 </tr>
29 41 <tr>
42 <td>${_('Email')}</td>
43 <td>${h.text('email')}</td>
44 <td>${self.get_form_error('email')}</td>
45 </tr>
46 <tr>
30 47 <td>${_('Active')}</td>
31 <td>${h.checkbox('active')}</td>
48 <td>${h.checkbox('active',value=True)}</td>
49 <td>${self.get_form_error('active')}</td>
32 50 </tr>
33 51 <tr>
34 52 <td></td>
35 <td>${h.submit('add','add')}</td>
53 <td>${h.submit('save','save')}</td>
36 54 </tr>
37
38 55 </table>
39 56 ${h.end_form()}
40 57 </div>
41 58 </%def> No newline at end of file
@@ -1,41 +1,59 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3
4 4 <%def name="title()">
5 5 ${_('User administration')}
6 6 </%def>
7 7 <%def name="breadcrumbs()">
8 8 ${h.link_to(u'Admin',h.url('admin_home'))}
9 9 /
10 10 ${_('Users')}
11 11 </%def>
12 12 <%def name="page_nav()">
13 13 ${self.menu('admin')}
14 14 ${self.submenu('users')}
15 15 </%def>
16 16 <%def name="main()">
17 17 <div>
18 18 <h2>${_('User')} - ${c.user.username}</h2>
19 19 ${h.form(url('user', id=c.user.user_id),method='put')}
20 20 <table>
21 21 <tr>
22 22 <td>${_('Username')}</td>
23 23 <td>${h.text('username')}</td>
24 <td>${self.get_form_error('username')}</td>
24 25 </tr>
25 26 <tr>
26 27 <td>${_('New password')}</td>
27 28 <td>${h.text('new_password')}</td>
29 <td>${self.get_form_error('new_password')}</td>
28 30 </tr>
29 31 <tr>
32 <td>${_('Name')}</td>
33 <td>${h.text('name')}</td>
34 <td>${self.get_form_error('name')}</td>
35 </tr>
36 <tr>
37 <td>${_('Lastname')}</td>
38 <td>${h.text('lastname')}</td>
39 <td>${self.get_form_error('lastname')}</td>
40 </tr>
41 <tr>
42 <td>${_('Email')}</td>
43 <td>${h.text('email')}</td>
44 <td>${self.get_form_error('email')}</td>
45 </tr>
46 <tr>
30 47 <td>${_('Active')}</td>
31 48 <td>${h.checkbox('active',value=True)}</td>
49 <td>${self.get_form_error('active')}</td>
32 50 </tr>
33 51 <tr>
34 52 <td></td>
35 53 <td>${h.submit('save','save')}</td>
36 54 </tr>
37 55
38 56 </table>
39 57 ${h.end_form()}
40 58 </div>
41 59 </%def> No newline at end of file
@@ -1,43 +1,45 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3
4 4 <%def name="title()">
5 5 ${_('Users administration')}
6 6 </%def>
7 7 <%def name="breadcrumbs()">
8 8 ${h.link_to(u'Admin',h.url('admin_home'))}
9 9 /
10 10 ${_('Users')}
11 11 </%def>
12 12 <%def name="page_nav()">
13 13 ${self.menu('admin')}
14 14 ${self.submenu('users')}
15 15 </%def>
16 16 <%def name="main()">
17 17 <div>
18 18 <h2>${_('Mercurial users')}</h2>
19 19 <table class="table_disp">
20 20 <tr class="header">
21 <td>${_('id')}</td>
22 21 <td>${_('username')}</td>
22 <td>${_('name')}</td>
23 <td>${_('lastname')}</td>
23 24 <td>${_('active')}</td>
24 25 <td>${_('admin')}</td>
25 26 <td>${_('action')}</td>
26 27 </tr>
27 28 %for user in c.users_list:
28 29 <tr>
29 <td>${user.user_id}</td>
30 30 <td>${h.link_to(user.username,h.url('edit_user', id=user.user_id))}</td>
31 <td>${user.name}</td>
32 <td>${user.lastname}</td>
31 33 <td>${user.active}</td>
32 34 <td>${user.admin}</td>
33 35 <td>
34 36 ${h.form(url('user', id=user.user_id),method='delete')}
35 37 ${h.submit('remove','delete',class_="delete_icon action_button")}
36 38 ${h.end_form()}
37 39 </td>
38 40 </tr>
39 41 %endfor
40 42 </table>
41 43 <span class="add_icon">${h.link_to(u'add user',h.url('new_user'))}</span>
42 44 </div>
43 45 </%def>
@@ -1,42 +1,40 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%!
3 3 from pylons_app.lib import filters
4 4 %>
5 5 <%inherit file="base/base.html"/>
6 6 <%def name="title()">
7 7 ${c.repos_prefix} Mercurial Repositories
8 8 </%def>
9 9 <%def name="breadcrumbs()">
10 10 ${c.repos_prefix} Mercurial Repositories
11 11 </%def>
12 12 <%def name="page_nav()">
13 13 ${self.menu('home')}
14 14 </%def>
15 15 <%def name="main()">
16 16 <div>
17 17 <br />
18 18 <h2>${_('Login')}</h2>
19 19 ${h.form(h.url.current())}
20 20 <table>
21 21 <tr>
22 22 <td>${_('Username')}</td>
23 23 <td>${h.text('username')}</td>
24 <td>${self.get_form_error('username')}
25
26 </td>
24 <td>${self.get_form_error('username')}</td>
27 25 </tr>
28 26 <tr>
29 27 <td>${_('Password')}</td>
30 28 <td>${h.password('password')}</td>
31 29 <td>${self.get_form_error('password')}</td>
32 30 </tr>
33 31 <tr>
34 32 <td></td>
35 33 <td>${h.submit('login','login')}</td>
36 34 </tr>
37 35 </table>
38 36 ${h.end_form()}
39 37 </div>
40 38 </%def>
41 39
42 40
General Comments 0
You need to be logged in to leave comments. Login now