##// END OF EJS Templates
users: properly handle null values when sorting columns in user grid.
marcink -
r1548:4f9855e2 default
parent child Browse files
Show More
@@ -1,237 +1,237 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22
23 23 from pyramid.httpexceptions import HTTPFound
24 24 from pyramid.view import view_config
25 25
26 26 from rhodecode.apps._base import BaseAppView
27 27 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 31 from rhodecode.lib.utils2 import safe_int, safe_unicode
32 32 from rhodecode.model.auth_token import AuthTokenModel
33 33 from rhodecode.model.db import User, or_
34 34 from rhodecode.model.meta import Session
35 35
36 36 log = logging.getLogger(__name__)
37 37
38 38
39 39 class AdminUsersView(BaseAppView):
40 40 ALLOW_SCOPED_TOKENS = False
41 41 """
42 42 This view has alternative version inside EE, if modified please take a look
43 43 in there as well.
44 44 """
45 45
46 46 def load_default_context(self):
47 47 c = self._get_local_tmpl_context()
48 48 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
49 49 self._register_global_c(c)
50 50 return c
51 51
52 52 def _redirect_for_default_user(self, username):
53 53 _ = self.request.translate
54 54 if username == User.DEFAULT_USER:
55 55 h.flash(_("You can't edit this user"), category='warning')
56 56 # TODO(marcink): redirect to 'users' admin panel once this
57 57 # is a pyramid view
58 58 raise HTTPFound('/')
59 59
60 60 def _extract_ordering(self, request):
61 61 column_index = safe_int(request.GET.get('order[0][column]'))
62 62 order_dir = request.GET.get(
63 63 'order[0][dir]', 'desc')
64 64 order_by = request.GET.get(
65 65 'columns[%s][data][sort]' % column_index, 'name_raw')
66 66
67 67 # translate datatable to DB columns
68 68 order_by = {
69 69 'first_name': 'name',
70 70 'last_name': 'lastname',
71 71 }.get(order_by) or order_by
72 72
73 73 search_q = request.GET.get('search[value]')
74 74 return search_q, order_by, order_dir
75 75
76 76 def _extract_chunk(self, request):
77 77 start = safe_int(request.GET.get('start'), 0)
78 78 length = safe_int(request.GET.get('length'), 25)
79 79 draw = safe_int(request.GET.get('draw'))
80 80 return draw, start, length
81 81
82 82 @HasPermissionAllDecorator('hg.admin')
83 83 @view_config(
84 84 route_name='users', request_method='GET',
85 85 renderer='rhodecode:templates/admin/users/users.mako')
86 86 def users_list(self):
87 87 c = self.load_default_context()
88 88 return self._get_template_context(c)
89 89
90 90 @HasPermissionAllDecorator('hg.admin')
91 91 @view_config(
92 92 # renderer defined below
93 93 route_name='users_data', request_method='GET', renderer='json',
94 94 xhr=True)
95 95 def users_list_data(self):
96 96 draw, start, limit = self._extract_chunk(self.request)
97 97 search_q, order_by, order_dir = self._extract_ordering(self.request)
98 98
99 99 _render = PartialRenderer('data_table/_dt_elements.mako')
100 100
101 101 def user_actions(user_id, username):
102 102 return _render("user_actions", user_id, username)
103 103
104 104 users_data_total_count = User.query()\
105 105 .filter(User.username != User.DEFAULT_USER) \
106 106 .count()
107 107
108 108 # json generate
109 109 base_q = User.query().filter(User.username != User.DEFAULT_USER)
110 110
111 111 if search_q:
112 112 like_expression = u'%{}%'.format(safe_unicode(search_q))
113 113 base_q = base_q.filter(or_(
114 114 User.username.ilike(like_expression),
115 115 User._email.ilike(like_expression),
116 116 User.name.ilike(like_expression),
117 117 User.lastname.ilike(like_expression),
118 118 ))
119 119
120 120 users_data_total_filtered_count = base_q.count()
121 121
122 122 sort_col = getattr(User, order_by, None)
123 123 if sort_col and order_dir == 'asc':
124 base_q = base_q.order_by(sort_col.asc())
124 base_q = base_q.order_by(sort_col.asc().nullslast())
125 125 elif sort_col:
126 base_q = base_q.order_by(sort_col.desc())
126 base_q = base_q.order_by(sort_col.desc().nullslast())
127 127
128 128 base_q = base_q.offset(start).limit(limit)
129 129 users_list = base_q.all()
130 130
131 131 users_data = []
132 132 for user in users_list:
133 133 users_data.append({
134 134 "username": h.gravatar_with_user(user.username),
135 135 "email": user.email,
136 136 "first_name": h.escape(user.name),
137 137 "last_name": h.escape(user.lastname),
138 138 "last_login": h.format_date(user.last_login),
139 139 "last_activity": h.format_date(user.last_activity),
140 140 "active": h.bool2icon(user.active),
141 141 "active_raw": user.active,
142 142 "admin": h.bool2icon(user.admin),
143 143 "extern_type": user.extern_type,
144 144 "extern_name": user.extern_name,
145 145 "action": user_actions(user.user_id, user.username),
146 146 })
147 147
148 148 data = ({
149 149 'draw': draw,
150 150 'data': users_data,
151 151 'recordsTotal': users_data_total_count,
152 152 'recordsFiltered': users_data_total_filtered_count,
153 153 })
154 154
155 155 return data
156 156
157 157 @LoginRequired()
158 158 @HasPermissionAllDecorator('hg.admin')
159 159 @view_config(
160 160 route_name='edit_user_auth_tokens', request_method='GET',
161 161 renderer='rhodecode:templates/admin/users/user_edit.mako')
162 162 def auth_tokens(self):
163 163 _ = self.request.translate
164 164 c = self.load_default_context()
165 165
166 166 user_id = self.request.matchdict.get('user_id')
167 167 c.user = User.get_or_404(user_id, pyramid_exc=True)
168 168 self._redirect_for_default_user(c.user.username)
169 169
170 170 c.active = 'auth_tokens'
171 171
172 172 c.lifetime_values = [
173 173 (str(-1), _('forever')),
174 174 (str(5), _('5 minutes')),
175 175 (str(60), _('1 hour')),
176 176 (str(60 * 24), _('1 day')),
177 177 (str(60 * 24 * 30), _('1 month')),
178 178 ]
179 179 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
180 180 c.role_values = [
181 181 (x, AuthTokenModel.cls._get_role_name(x))
182 182 for x in AuthTokenModel.cls.ROLES]
183 183 c.role_options = [(c.role_values, _("Role"))]
184 184 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
185 185 c.user.user_id, show_expired=True)
186 186 return self._get_template_context(c)
187 187
188 188 def maybe_attach_token_scope(self, token):
189 189 # implemented in EE edition
190 190 pass
191 191
192 192 @LoginRequired()
193 193 @HasPermissionAllDecorator('hg.admin')
194 194 @CSRFRequired()
195 195 @view_config(
196 196 route_name='edit_user_auth_tokens_add', request_method='POST')
197 197 def auth_tokens_add(self):
198 198 _ = self.request.translate
199 199 c = self.load_default_context()
200 200
201 201 user_id = self.request.matchdict.get('user_id')
202 202 c.user = User.get_or_404(user_id, pyramid_exc=True)
203 203 self._redirect_for_default_user(c.user.username)
204 204
205 205 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
206 206 description = self.request.POST.get('description')
207 207 role = self.request.POST.get('role')
208 208
209 209 token = AuthTokenModel().create(
210 210 c.user.user_id, description, lifetime, role)
211 211 self.maybe_attach_token_scope(token)
212 212 Session().commit()
213 213
214 214 h.flash(_("Auth token successfully created"), category='success')
215 215 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
216 216
217 217 @LoginRequired()
218 218 @HasPermissionAllDecorator('hg.admin')
219 219 @CSRFRequired()
220 220 @view_config(
221 221 route_name='edit_user_auth_tokens_delete', request_method='POST')
222 222 def auth_tokens_delete(self):
223 223 _ = self.request.translate
224 224 c = self.load_default_context()
225 225
226 226 user_id = self.request.matchdict.get('user_id')
227 227 c.user = User.get_or_404(user_id, pyramid_exc=True)
228 228 self._redirect_for_default_user(c.user.username)
229 229
230 230 del_auth_token = self.request.POST.get('del_auth_token')
231 231
232 232 if del_auth_token:
233 233 AuthTokenModel().delete(del_auth_token, c.user.user_id)
234 234 Session().commit()
235 235 h.flash(_("Auth token successfully deleted"), category='success')
236 236
237 237 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
General Comments 0
You need to be logged in to leave comments. Login now