Show More
@@ -53,6 +53,15 b' def includeme(config):' | |||
|
53 | 53 | name='admin_settings_sessions_cleanup', |
|
54 | 54 | pattern=ADMIN_PREFIX + '/settings/sessions/cleanup') |
|
55 | 55 | |
|
56 | # users admin | |
|
57 | config.add_route( | |
|
58 | name='users', | |
|
59 | pattern=ADMIN_PREFIX + '/users') | |
|
60 | ||
|
61 | config.add_route( | |
|
62 | name='users_data', | |
|
63 | pattern=ADMIN_PREFIX + '/users_data') | |
|
64 | ||
|
56 | 65 | # user auth tokens |
|
57 | 66 | config.add_route( |
|
58 | 67 | name='edit_user_auth_tokens', |
@@ -22,18 +22,18 b' import pytest' | |||
|
22 | 22 | |
|
23 | 23 | from rhodecode.model.db import User, UserApiKeys |
|
24 | 24 | |
|
25 | from rhodecode.apps._base import ADMIN_PREFIX | |
|
26 | 25 | from rhodecode.tests import ( |
|
27 | 26 | TestController, TEST_USER_REGULAR_LOGIN, assert_session_flash) |
|
28 | 27 | from rhodecode.tests.fixture import Fixture |
|
29 | from rhodecode.tests.utils import AssertResponse | |
|
30 | 28 | |
|
31 | 29 | fixture = Fixture() |
|
32 | 30 | |
|
33 | 31 | |
|
32 | def route_path(name, params=None, **kwargs): | |
|
33 | import urllib | |
|
34 | from rhodecode.apps._base import ADMIN_PREFIX | |
|
34 | 35 | |
|
35 | def route_path(name, **kwargs): | |
|
36 | return { | |
|
36 | base_url = { | |
|
37 | 37 | 'users': |
|
38 | 38 | ADMIN_PREFIX + '/users', |
|
39 | 39 | 'users_data': |
@@ -46,9 +46,37 b' def route_path(name, **kwargs):' | |||
|
46 | 46 | ADMIN_PREFIX + '/users/{user_id}/edit/auth_tokens/delete', |
|
47 | 47 | }[name].format(**kwargs) |
|
48 | 48 | |
|
49 | if params: | |
|
50 | base_url = '{}?{}'.format(base_url, urllib.urlencode(params)) | |
|
51 | return base_url | |
|
52 | ||
|
49 | 53 | |
|
50 | 54 | class TestAdminUsersView(TestController): |
|
51 | 55 | |
|
56 | def test_show_users(self): | |
|
57 | self.log_user() | |
|
58 | self.app.get(route_path('users')) | |
|
59 | ||
|
60 | def test_show_users_data(self, xhr_header): | |
|
61 | self.log_user() | |
|
62 | response = self.app.get(route_path( | |
|
63 | 'users_data'), extra_environ=xhr_header) | |
|
64 | ||
|
65 | all_users = User.query().filter( | |
|
66 | User.username != User.DEFAULT_USER).count() | |
|
67 | assert response.json['recordsTotal'] == all_users | |
|
68 | ||
|
69 | def test_show_users_data_filtered(self, xhr_header): | |
|
70 | self.log_user() | |
|
71 | response = self.app.get(route_path( | |
|
72 | 'users_data', params={'search[value]': 'empty_search'}), | |
|
73 | extra_environ=xhr_header) | |
|
74 | ||
|
75 | all_users = User.query().filter( | |
|
76 | User.username != User.DEFAULT_USER).count() | |
|
77 | assert response.json['recordsTotal'] == all_users | |
|
78 | assert response.json['recordsFiltered'] == 0 | |
|
79 | ||
|
52 | 80 | def test_auth_tokens_default_user(self): |
|
53 | 81 | self.log_user() |
|
54 | 82 | user = User.get_default_user() |
@@ -28,9 +28,9 b' from rhodecode.lib.auth import (' | |||
|
28 | 28 | LoginRequired, HasPermissionAllDecorator, CSRFRequired) |
|
29 | 29 | from rhodecode.lib import helpers as h |
|
30 | 30 | from rhodecode.lib.utils import PartialRenderer |
|
31 | from rhodecode.lib.utils2 import safe_int | |
|
31 | from rhodecode.lib.utils2 import safe_int, safe_unicode | |
|
32 | 32 | from rhodecode.model.auth_token import AuthTokenModel |
|
33 | from rhodecode.model.db import User | |
|
33 | from rhodecode.model.db import User, or_ | |
|
34 | 34 | from rhodecode.model.meta import Session |
|
35 | 35 | |
|
36 | 36 | log = logging.getLogger(__name__) |
@@ -58,6 +58,105 b' class AdminUsersView(BaseAppView):' | |||
|
58 | 58 | # is a pyramid view |
|
59 | 59 | raise HTTPFound('/') |
|
60 | 60 | |
|
61 | def _extract_ordering(self, request): | |
|
62 | column_index = safe_int(request.GET.get('order[0][column]')) | |
|
63 | order_dir = request.GET.get( | |
|
64 | 'order[0][dir]', 'desc') | |
|
65 | order_by = request.GET.get( | |
|
66 | 'columns[%s][data][sort]' % column_index, 'name_raw') | |
|
67 | ||
|
68 | # translate datatable to DB columns | |
|
69 | order_by = { | |
|
70 | 'first_name': 'name', | |
|
71 | 'last_name': 'lastname', | |
|
72 | 'last_activity': '' | |
|
73 | }.get(order_by) or order_by | |
|
74 | ||
|
75 | search_q = request.GET.get('search[value]') | |
|
76 | return search_q, order_by, order_dir | |
|
77 | ||
|
78 | def _extract_chunk(self, request): | |
|
79 | start = safe_int(request.GET.get('start'), 0) | |
|
80 | length = safe_int(request.GET.get('length'), 25) | |
|
81 | draw = safe_int(request.GET.get('draw')) | |
|
82 | return draw, start, length | |
|
83 | ||
|
84 | @HasPermissionAllDecorator('hg.admin') | |
|
85 | @view_config( | |
|
86 | route_name='users', request_method='GET', | |
|
87 | renderer='rhodecode:templates/admin/users/users.mako') | |
|
88 | def users_list(self): | |
|
89 | c = self.load_default_context() | |
|
90 | return self._get_template_context(c) | |
|
91 | ||
|
92 | @HasPermissionAllDecorator('hg.admin') | |
|
93 | @view_config( | |
|
94 | # renderer defined below | |
|
95 | route_name='users_data', request_method='GET', renderer='json', | |
|
96 | xhr=True) | |
|
97 | def users_list_data(self): | |
|
98 | draw, start, limit = self._extract_chunk(self.request) | |
|
99 | search_q, order_by, order_dir = self._extract_ordering(self.request) | |
|
100 | ||
|
101 | _render = PartialRenderer('data_table/_dt_elements.mako') | |
|
102 | ||
|
103 | def user_actions(user_id, username): | |
|
104 | return _render("user_actions", user_id, username) | |
|
105 | ||
|
106 | users_data_total_count = User.query()\ | |
|
107 | .filter(User.username != User.DEFAULT_USER) \ | |
|
108 | .count() | |
|
109 | ||
|
110 | # json generate | |
|
111 | base_q = User.query().filter(User.username != User.DEFAULT_USER) | |
|
112 | ||
|
113 | if search_q: | |
|
114 | like_expression = u'{}%'.format(safe_unicode(search_q)) | |
|
115 | base_q = base_q.filter(or_( | |
|
116 | User.username.ilike(like_expression), | |
|
117 | User._email.ilike(like_expression), | |
|
118 | User.name.ilike(like_expression), | |
|
119 | User.lastname.ilike(like_expression), | |
|
120 | )) | |
|
121 | ||
|
122 | users_data_total_filtered_count = base_q.count() | |
|
123 | ||
|
124 | sort_col = getattr(User, order_by, None) | |
|
125 | if sort_col and order_dir == 'asc': | |
|
126 | base_q = base_q.order_by(sort_col.asc()) | |
|
127 | elif sort_col: | |
|
128 | base_q = base_q.order_by(sort_col.desc()) | |
|
129 | ||
|
130 | base_q = base_q.offset(start).limit(limit) | |
|
131 | users_list = base_q.all() | |
|
132 | ||
|
133 | users_data = [] | |
|
134 | for user in users_list: | |
|
135 | users_data.append({ | |
|
136 | "username": h.gravatar_with_user(user.username), | |
|
137 | "email": user.email, | |
|
138 | "first_name": h.escape(user.name), | |
|
139 | "last_name": h.escape(user.lastname), | |
|
140 | "last_login": h.format_date(user.last_login), | |
|
141 | "last_activity": h.format_date( | |
|
142 | h.time_to_datetime(user.user_data.get('last_activity', 0))), | |
|
143 | "active": h.bool2icon(user.active), | |
|
144 | "active_raw": user.active, | |
|
145 | "admin": h.bool2icon(user.admin), | |
|
146 | "extern_type": user.extern_type, | |
|
147 | "extern_name": user.extern_name, | |
|
148 | "action": user_actions(user.user_id, user.username), | |
|
149 | }) | |
|
150 | ||
|
151 | data = ({ | |
|
152 | 'draw': draw, | |
|
153 | 'data': users_data, | |
|
154 | 'recordsTotal': users_data_total_count, | |
|
155 | 'recordsFiltered': users_data_total_filtered_count, | |
|
156 | }) | |
|
157 | ||
|
158 | return data | |
|
159 | ||
|
61 | 160 | @LoginRequired() |
|
62 | 161 | @HasPermissionAllDecorator('hg.admin') |
|
63 | 162 | @view_config( |
@@ -292,8 +292,6 b' def make_map(config):' | |||
|
292 | 292 | controller='admin/users') as m: |
|
293 | 293 | m.connect('users', '/users', |
|
294 | 294 | action='create', conditions={'method': ['POST']}) |
|
295 | m.connect('users', '/users', | |
|
296 | action='index', conditions={'method': ['GET']}) | |
|
297 | 295 | m.connect('new_user', '/users/new', |
|
298 | 296 | action='new', conditions={'method': ['GET']}) |
|
299 | 297 | m.connect('update_user', '/users/{user_id}', |
@@ -77,7 +77,7 b' class MyAccountController(BaseController' | |||
|
77 | 77 | if c.user.username == User.DEFAULT_USER: |
|
78 | 78 | h.flash(_("You can't edit this user since it's" |
|
79 | 79 | " crucial for entire application"), category='warning') |
|
80 |
return redirect( |
|
|
80 | return redirect(h.route_path('users')) | |
|
81 | 81 | |
|
82 | 82 | c.auth_user = AuthUser( |
|
83 | 83 | user_id=c.rhodecode_user.user_id, ip_addr=self.ip_addr) |
@@ -50,7 +50,6 b' from rhodecode.model.user import UserMod' | |||
|
50 | 50 | from rhodecode.model.meta import Session |
|
51 | 51 | from rhodecode.model.permission import PermissionModel |
|
52 | 52 | from rhodecode.lib.utils import action_logger |
|
53 | from rhodecode.lib.ext_json import json | |
|
54 | 53 | from rhodecode.lib.utils2 import datetime_to_time, safe_int, AttributeDict |
|
55 | 54 | |
|
56 | 55 | log = logging.getLogger(__name__) |
@@ -76,51 +75,6 b' class UsersController(BaseController):' | |||
|
76 | 75 | ] |
|
77 | 76 | PermissionModel().set_global_permission_choices(c, gettext_translator=_) |
|
78 | 77 | |
|
79 | @HasPermissionAllDecorator('hg.admin') | |
|
80 | def index(self): | |
|
81 | """GET /users: All items in the collection""" | |
|
82 | # url('users') | |
|
83 | ||
|
84 | from rhodecode.lib.utils import PartialRenderer | |
|
85 | _render = PartialRenderer('data_table/_dt_elements.mako') | |
|
86 | ||
|
87 | def username(user_id, username): | |
|
88 | return _render("user_name", user_id, username) | |
|
89 | ||
|
90 | def user_actions(user_id, username): | |
|
91 | return _render("user_actions", user_id, username) | |
|
92 | ||
|
93 | # json generate | |
|
94 | c.users_list = User.query()\ | |
|
95 | .filter(User.username != User.DEFAULT_USER) \ | |
|
96 | .all() | |
|
97 | ||
|
98 | users_data = [] | |
|
99 | for user in c.users_list: | |
|
100 | users_data.append({ | |
|
101 | "username": h.gravatar_with_user(user.username), | |
|
102 | "username_raw": user.username, | |
|
103 | "email": user.email, | |
|
104 | "first_name": h.escape(user.name), | |
|
105 | "last_name": h.escape(user.lastname), | |
|
106 | "last_login": h.format_date(user.last_login), | |
|
107 | "last_login_raw": datetime_to_time(user.last_login), | |
|
108 | "last_activity": h.format_date( | |
|
109 | h.time_to_datetime(user.user_data.get('last_activity', 0))), | |
|
110 | "last_activity_raw": user.user_data.get('last_activity', 0), | |
|
111 | "active": h.bool2icon(user.active), | |
|
112 | "active_raw": user.active, | |
|
113 | "admin": h.bool2icon(user.admin), | |
|
114 | "admin_raw": user.admin, | |
|
115 | "extern_type": user.extern_type, | |
|
116 | "extern_name": user.extern_name, | |
|
117 | "action": user_actions(user.user_id, user.username), | |
|
118 | }) | |
|
119 | ||
|
120 | ||
|
121 | c.data = json.dumps(users_data) | |
|
122 | return render('admin/users/users.mako') | |
|
123 | ||
|
124 | 78 | def _get_personal_repo_group_template_vars(self): |
|
125 | 79 | DummyUser = AttributeDict({ |
|
126 | 80 | 'username': '${username}', |
@@ -135,7 +89,6 b' class UsersController(BaseController):' | |||
|
135 | 89 | @auth.CSRFRequired() |
|
136 | 90 | def create(self): |
|
137 | 91 | """POST /users: Create a new item""" |
|
138 | # url('users') | |
|
139 | 92 | c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.name |
|
140 | 93 | user_model = UserModel() |
|
141 | 94 | user_form = UserForm()() |
@@ -168,7 +121,7 b' class UsersController(BaseController):' | |||
|
168 | 121 | log.exception("Exception creation of user") |
|
169 | 122 | h.flash(_('Error occurred during creation of user %s') |
|
170 | 123 | % request.POST.get('username'), category='error') |
|
171 |
return redirect( |
|
|
124 | return redirect(h.route_path('users')) | |
|
172 | 125 | |
|
173 | 126 | @HasPermissionAllDecorator('hg.admin') |
|
174 | 127 | def new(self): |
@@ -312,7 +265,7 b' class UsersController(BaseController):' | |||
|
312 | 265 | log.exception("Exception during deletion of user") |
|
313 | 266 | h.flash(_('An error occurred during deletion of user'), |
|
314 | 267 | category='error') |
|
315 |
return redirect( |
|
|
268 | return redirect(h.route_path('users')) | |
|
316 | 269 | |
|
317 | 270 | @HasPermissionAllDecorator('hg.admin') |
|
318 | 271 | @auth.CSRFRequired() |
@@ -404,7 +357,7 b' class UsersController(BaseController):' | |||
|
404 | 357 | c.user = User.get_or_404(user_id) |
|
405 | 358 | if c.user.username == User.DEFAULT_USER: |
|
406 | 359 | h.flash(_("You can't edit this user"), category='warning') |
|
407 |
return redirect( |
|
|
360 | return redirect(h.route_path('users')) | |
|
408 | 361 | |
|
409 | 362 | c.active = 'profile' |
|
410 | 363 | c.extern_type = c.user.extern_type |
@@ -425,7 +378,7 b' class UsersController(BaseController):' | |||
|
425 | 378 | user = c.user = User.get_or_404(user_id) |
|
426 | 379 | if user.username == User.DEFAULT_USER: |
|
427 | 380 | h.flash(_("You can't edit this user"), category='warning') |
|
428 |
return redirect( |
|
|
381 | return redirect(h.route_path('users')) | |
|
429 | 382 | |
|
430 | 383 | c.active = 'advanced' |
|
431 | 384 | c.perm_user = AuthUser(user_id=user_id, ip_addr=self.ip_addr) |
@@ -457,7 +410,7 b' class UsersController(BaseController):' | |||
|
457 | 410 | c.user = User.get_or_404(user_id) |
|
458 | 411 | if c.user.username == User.DEFAULT_USER: |
|
459 | 412 | h.flash(_("You can't edit this user"), category='warning') |
|
460 |
return redirect( |
|
|
413 | return redirect(h.route_path('users')) | |
|
461 | 414 | |
|
462 | 415 | c.active = 'global_perms' |
|
463 | 416 | |
@@ -531,7 +484,7 b' class UsersController(BaseController):' | |||
|
531 | 484 | c.user = User.get_or_404(user_id) |
|
532 | 485 | if c.user.username == User.DEFAULT_USER: |
|
533 | 486 | h.flash(_("You can't edit this user"), category='warning') |
|
534 |
return redirect( |
|
|
487 | return redirect(h.route_path('users')) | |
|
535 | 488 | |
|
536 | 489 | c.active = 'perms_summary' |
|
537 | 490 | c.perm_user = AuthUser(user_id=user_id, ip_addr=self.ip_addr) |
@@ -544,7 +497,7 b' class UsersController(BaseController):' | |||
|
544 | 497 | c.user = User.get_or_404(user_id) |
|
545 | 498 | if c.user.username == User.DEFAULT_USER: |
|
546 | 499 | h.flash(_("You can't edit this user"), category='warning') |
|
547 |
return redirect( |
|
|
500 | return redirect(h.route_path('users')) | |
|
548 | 501 | |
|
549 | 502 | c.active = 'emails' |
|
550 | 503 | c.user_email_map = UserEmailMap.query() \ |
@@ -602,7 +555,7 b' class UsersController(BaseController):' | |||
|
602 | 555 | c.user = User.get_or_404(user_id) |
|
603 | 556 | if c.user.username == User.DEFAULT_USER: |
|
604 | 557 | h.flash(_("You can't edit this user"), category='warning') |
|
605 |
return redirect( |
|
|
558 | return redirect(h.route_path('users')) | |
|
606 | 559 | |
|
607 | 560 | c.active = 'ips' |
|
608 | 561 | c.user_ip_map = UserIpMap.query() \ |
@@ -10,7 +10,7 b'' | |||
|
10 | 10 | <%def name="breadcrumbs_links()"> |
|
11 | 11 | ${h.link_to(_('Admin'),h.url('admin_home'))} |
|
12 | 12 | » |
|
13 |
${h.link_to(_('Users'),h. |
|
|
13 | ${h.link_to(_('Users'),h.route_path('users'))} | |
|
14 | 14 | » |
|
15 | 15 | ${_('Add User')} |
|
16 | 16 | </%def> |
@@ -11,7 +11,7 b'' | |||
|
11 | 11 | <%def name="breadcrumbs_links()"> |
|
12 | 12 | ${h.link_to(_('Admin'),h.url('admin_home'))} |
|
13 | 13 | » |
|
14 |
${h.link_to(_('Users'),h. |
|
|
14 | ${h.link_to(_('Users'),h.route_path('users'))} | |
|
15 | 15 | » |
|
16 | 16 | ${c.user.username} |
|
17 | 17 | </%def> |
@@ -34,60 +34,31 b'' | |||
|
34 | 34 | </div> |
|
35 | 35 | </div> |
|
36 | 36 | |
|
37 | <script> | |
|
37 | <script type="text/javascript"> | |
|
38 | ||
|
38 | 39 | $(document).ready(function() { |
|
39 | 40 | |
|
40 |
var get |
|
|
41 |
var |
|
|
42 |
var |
|
|
43 |
var |
|
|
44 | var active = datatable.fnGetFilteredData(); | |
|
45 | var _text = _gettext("{0} active out of {1} users").format(active, total); | |
|
46 | $('#user_count').text(_text); | |
|
47 | }; | |
|
48 | ||
|
49 | // custom filter that filters by username OR email | |
|
50 | $.fn.dataTable.ext.search.push( | |
|
51 | function( settings, data, dataIndex ) { | |
|
52 | var query = $('#q_filter').val(); | |
|
53 | ||
|
54 | var username = data[0]; | |
|
55 | var email = data[1]; | |
|
56 | var first_name = data[2]; | |
|
57 | var last_name = data[3]; | |
|
41 | var getDatatableCount = function(){ | |
|
42 | var table = $('#user_list_table').dataTable(); | |
|
43 | var page = table.api().page.info(); | |
|
44 | var active = page.recordsDisplay; | |
|
45 | var total = page.recordsTotal; | |
|
58 | 46 | |
|
59 | var query_str = username + " " + | |
|
60 | email + " " + | |
|
61 | first_name + " " + | |
|
62 | last_name; | |
|
63 | ||
|
64 | if ((query_str).indexOf(query) !== -1) { | |
|
65 | return true; | |
|
66 | } | |
|
67 | return false; | |
|
68 | } | |
|
69 | ); | |
|
70 | // filtered data plugin | |
|
71 | $.fn.dataTableExt.oApi.fnGetFilteredData = function ( oSettings ) { | |
|
72 | var res = []; | |
|
73 | for ( var i=0, iLen=oSettings.fnRecordsDisplay() ; i<iLen ; i++ ) { | |
|
74 | var record = oSettings.aoData[i]._aData; | |
|
75 | if(record['active_raw']){ | |
|
76 | res.push(record); | |
|
77 | } | |
|
78 | } | |
|
79 | return res.length; | |
|
47 | var _text = _gettext("{0} out of {1} users").format(active, total); | |
|
48 | $('#user_count').text(_text); | |
|
80 | 49 | }; |
|
81 | 50 | |
|
82 | 51 | // user list |
|
83 | 52 | $('#user_list_table').DataTable({ |
|
84 | data: ${c.data|n}, | |
|
53 | processing: false, | |
|
54 | serverSide: true, | |
|
55 | ajax: "${h.route_path('users_data')}", | |
|
85 | 56 | dom: 'rtp', |
|
86 | 57 | pageLength: ${c.visual.admin_grid_items}, |
|
87 |
order: [[ |
|
|
58 | order: [[ 0, "asc" ]], | |
|
88 | 59 | columns: [ |
|
89 | 60 | { data: {"_": "username", |
|
90 |
"sort": "username |
|
|
61 | "sort": "username"}, title: "${_('Username')}", className: "td-user" }, | |
|
91 | 62 | { data: {"_": "email", |
|
92 | 63 | "sort": "email"}, title: "${_('Email')}", className: "td-email" }, |
|
93 | 64 | { data: {"_": "first_name", |
@@ -95,12 +66,12 b'' | |||
|
95 | 66 | { data: {"_": "last_name", |
|
96 | 67 | "sort": "last_name"}, title: "${_('Last Name')}", className: "td-user" }, |
|
97 | 68 | { data: {"_": "last_activity", |
|
98 |
"sort": "last_activity |
|
|
99 | "type": Number}, title: "${_('Last activity')}", className: "td-time" }, | |
|
69 | "sort": "last_activity", | |
|
70 | "type": Number}, title: "${_('Last activity')}", className: "td-time", orderable: false }, | |
|
100 | 71 | { data: {"_": "active", |
|
101 |
"sort": "active |
|
|
72 | "sort": "active"}, title: "${_('Active')}", className: "td-active" }, | |
|
102 | 73 | { data: {"_": "admin", |
|
103 |
"sort": "admin |
|
|
74 | "sort": "admin"}, title: "${_('Admin')}", className: "td-admin" }, | |
|
104 | 75 | { data: {"_": "extern_type", |
|
105 | 76 | "sort": "extern_type"}, title: "${_('Auth type')}", className: "td-type" }, |
|
106 | 77 | { data: {"_": "action", |
@@ -110,9 +81,7 b'' | |||
|
110 | 81 | paginate: DEFAULT_GRID_PAGINATION, |
|
111 | 82 | emptyTable: _gettext("No users available yet.") |
|
112 | 83 | }, |
|
113 | "initComplete": function( settings, json ) { | |
|
114 | get_datatable_count(); | |
|
115 | }, | |
|
84 | ||
|
116 | 85 | "createdRow": function ( row, data, index ) { |
|
117 | 86 | if (!data['active_raw']){ |
|
118 | 87 | $(row).addClass('closed') |
@@ -120,23 +89,29 b'' | |||
|
120 | 89 | } |
|
121 | 90 | }); |
|
122 | 91 | |
|
123 | // update the counter when doing search | |
|
124 | $('#user_list_table').on( 'search.dt', function (e,settings) { | |
|
125 | get_datatable_count(); | |
|
92 | $('#user_list_table').on('xhr.dt', function(e, settings, json, xhr){ | |
|
93 | $('#user_list_table').css('opacity', 1); | |
|
94 | }); | |
|
95 | ||
|
96 | $('#user_list_table').on('preXhr.dt', function(e, settings, data){ | |
|
97 | $('#user_list_table').css('opacity', 0.3); | |
|
126 | 98 | }); |
|
127 | 99 | |
|
128 | // filter, filter both grids | |
|
129 |
$('# |
|
|
130 | var user_api = $('#user_list_table').dataTable().api(); | |
|
131 | user_api | |
|
132 | .draw(); | |
|
100 | // refresh counters on draw | |
|
101 | $('#user_list_table').on('draw.dt', function(){ | |
|
102 | getDatatableCount(); | |
|
133 | 103 | }); |
|
134 | 104 | |
|
135 | // refilter table if page load via back button | |
|
136 |
$( |
|
|
105 | // filter | |
|
106 | $('#q_filter').on('keyup', | |
|
107 | $.debounce(250, function() { | |
|
108 | $('#user_list_table').DataTable().search( | |
|
109 | $('#q_filter').val() | |
|
110 | ).draw(); | |
|
111 | }) | |
|
112 | ); | |
|
137 | 113 | |
|
138 | 114 | }); |
|
139 | ||
|
140 | 115 | </script> |
|
141 | 116 | |
|
142 | 117 | </%def> |
@@ -75,7 +75,7 b'' | |||
|
75 | 75 | <li><a href="${h.url('admin_home')}">${_('Admin journal')}</a></li> |
|
76 | 76 | <li><a href="${h.url('repos')}">${_('Repositories')}</a></li> |
|
77 | 77 | <li><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li> |
|
78 |
<li><a href="${h. |
|
|
78 | <li><a href="${h.route_path('users')}">${_('Users')}</a></li> | |
|
79 | 79 | <li><a href="${h.url('users_groups')}">${_('User groups')}</a></li> |
|
80 | 80 | <li><a href="${h.url('admin_permissions_application')}">${_('Permissions')}</a></li> |
|
81 | 81 | <li><a href="${h.route_path('auth_home', traverse='')}">${_('Authentication')}</a></li> |
@@ -36,6 +36,20 b' from rhodecode.tests.utils import Assert' | |||
|
36 | 36 | fixture = Fixture() |
|
37 | 37 | |
|
38 | 38 | |
|
39 | def route_path(name, params=None, **kwargs): | |
|
40 | import urllib | |
|
41 | from rhodecode.apps._base import ADMIN_PREFIX | |
|
42 | ||
|
43 | base_url = { | |
|
44 | 'users_data': | |
|
45 | ADMIN_PREFIX + '/users_data', | |
|
46 | }[name].format(**kwargs) | |
|
47 | ||
|
48 | if params: | |
|
49 | base_url = '{}?{}'.format(base_url, urllib.urlencode(params)) | |
|
50 | return base_url | |
|
51 | ||
|
52 | ||
|
39 | 53 | class TestAdminUsersController(TestController): |
|
40 | 54 | test_user_1 = 'testme' |
|
41 | 55 | destroy_users = set() |
@@ -44,7 +58,7 b' class TestAdminUsersController(TestContr' | |||
|
44 | 58 | def teardown_method(cls, method): |
|
45 | 59 | fixture.destroy_users(cls.destroy_users) |
|
46 | 60 | |
|
47 | def test_create(self): | |
|
61 | def test_create(self, xhr_header): | |
|
48 | 62 | self.log_user() |
|
49 | 63 | username = 'newtestuser' |
|
50 | 64 | password = 'test12' |
@@ -53,7 +67,7 b' class TestAdminUsersController(TestContr' | |||
|
53 | 67 | lastname = 'lastname' |
|
54 | 68 | email = 'mail@mail.com' |
|
55 | 69 | |
|
56 |
|
|
|
70 | self.app.get(url('new_user')) | |
|
57 | 71 | |
|
58 | 72 | response = self.app.post(url('users'), params={ |
|
59 | 73 | 'username': username, |
@@ -81,8 +95,8 b' class TestAdminUsersController(TestContr' | |||
|
81 | 95 | assert new_user.lastname == lastname |
|
82 | 96 | assert new_user.email == email |
|
83 | 97 | |
|
84 | response.follow() | |
|
85 | response = response.follow() | |
|
98 | response = self.app.get(route_path('users_data'), | |
|
99 | extra_environ=xhr_header) | |
|
86 | 100 | response.mustcontain(username) |
|
87 | 101 | |
|
88 | 102 | def test_create_err(self): |
@@ -93,7 +107,7 b' class TestAdminUsersController(TestContr' | |||
|
93 | 107 | lastname = 'lastname' |
|
94 | 108 | email = 'errmail.com' |
|
95 | 109 | |
|
96 |
|
|
|
110 | self.app.get(url('new_user')) | |
|
97 | 111 | |
|
98 | 112 | response = self.app.post(url('users'), params={ |
|
99 | 113 | 'username': username, |
General Comments 0
You need to be logged in to leave comments.
Login now