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