##// END OF EJS Templates
data-grid: create a common re-usable app with datagrid rendering helpers
marcink -
r1646:4bd0052b default
parent child Browse files
Show More
@@ -1,163 +1,191 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import time
21 import time
22 import logging
22 import logging
23 from pylons import tmpl_context as c
23 from pylons import tmpl_context as c
24 from pyramid.httpexceptions import HTTPFound
24 from pyramid.httpexceptions import HTTPFound
25
25
26 from rhodecode.lib import helpers as h
26 from rhodecode.lib import helpers as h
27 from rhodecode.lib.utils2 import StrictAttributeDict, safe_int
27 from rhodecode.lib.utils2 import StrictAttributeDict, safe_int
28 from rhodecode.model import repo
28 from rhodecode.model import repo
29 from rhodecode.model.db import User
29 from rhodecode.model.db import User
30 from rhodecode.model.scm import ScmModel
30 from rhodecode.model.scm import ScmModel
31
31
32 log = logging.getLogger(__name__)
32 log = logging.getLogger(__name__)
33
33
34
34
35 ADMIN_PREFIX = '/_admin'
35 ADMIN_PREFIX = '/_admin'
36 STATIC_FILE_PREFIX = '/_static'
36 STATIC_FILE_PREFIX = '/_static'
37
37
38
38
39 class TemplateArgs(StrictAttributeDict):
39 class TemplateArgs(StrictAttributeDict):
40 pass
40 pass
41
41
42
42
43 class BaseAppView(object):
43 class BaseAppView(object):
44
44
45 def __init__(self, context, request):
45 def __init__(self, context, request):
46 self.request = request
46 self.request = request
47 self.context = context
47 self.context = context
48 self.session = request.session
48 self.session = request.session
49 self._rhodecode_user = request.user # auth user
49 self._rhodecode_user = request.user # auth user
50 self._rhodecode_db_user = self._rhodecode_user.get_instance()
50 self._rhodecode_db_user = self._rhodecode_user.get_instance()
51 self._maybe_needs_password_change(
51 self._maybe_needs_password_change(
52 request.matched_route.name, self._rhodecode_db_user)
52 request.matched_route.name, self._rhodecode_db_user)
53
53
54 def _maybe_needs_password_change(self, view_name, user_obj):
54 def _maybe_needs_password_change(self, view_name, user_obj):
55 log.debug('Checking if user %s needs password change on view %s',
55 log.debug('Checking if user %s needs password change on view %s',
56 user_obj, view_name)
56 user_obj, view_name)
57 skip_user_views = [
57 skip_user_views = [
58 'logout', 'login',
58 'logout', 'login',
59 'my_account_password', 'my_account_password_update'
59 'my_account_password', 'my_account_password_update'
60 ]
60 ]
61
61
62 if not user_obj:
62 if not user_obj:
63 return
63 return
64
64
65 if user_obj.username == User.DEFAULT_USER:
65 if user_obj.username == User.DEFAULT_USER:
66 return
66 return
67
67
68 now = time.time()
68 now = time.time()
69 should_change = user_obj.user_data.get('force_password_change')
69 should_change = user_obj.user_data.get('force_password_change')
70 change_after = safe_int(should_change) or 0
70 change_after = safe_int(should_change) or 0
71 if should_change and now > change_after:
71 if should_change and now > change_after:
72 log.debug('User %s requires password change', user_obj)
72 log.debug('User %s requires password change', user_obj)
73 h.flash('You are required to change your password', 'warning',
73 h.flash('You are required to change your password', 'warning',
74 ignore_duplicate=True)
74 ignore_duplicate=True)
75
75
76 if view_name not in skip_user_views:
76 if view_name not in skip_user_views:
77 raise HTTPFound(
77 raise HTTPFound(
78 self.request.route_path('my_account_password'))
78 self.request.route_path('my_account_password'))
79
79
80 def _get_local_tmpl_context(self):
80 def _get_local_tmpl_context(self):
81 c = TemplateArgs()
81 c = TemplateArgs()
82 c.auth_user = self.request.user
82 c.auth_user = self.request.user
83 return c
83 return c
84
84
85 def _register_global_c(self, tmpl_args):
85 def _register_global_c(self, tmpl_args):
86 """
86 """
87 Registers attributes to pylons global `c`
87 Registers attributes to pylons global `c`
88 """
88 """
89 # TODO(marcink): remove once pyramid migration is finished
89 # TODO(marcink): remove once pyramid migration is finished
90 for k, v in tmpl_args.items():
90 for k, v in tmpl_args.items():
91 setattr(c, k, v)
91 setattr(c, k, v)
92
92
93 def _get_template_context(self, tmpl_args):
93 def _get_template_context(self, tmpl_args):
94 self._register_global_c(tmpl_args)
94 self._register_global_c(tmpl_args)
95
95
96 local_tmpl_args = {
96 local_tmpl_args = {
97 'defaults': {},
97 'defaults': {},
98 'errors': {},
98 'errors': {},
99 }
99 }
100 local_tmpl_args.update(tmpl_args)
100 local_tmpl_args.update(tmpl_args)
101 return local_tmpl_args
101 return local_tmpl_args
102
102
103 def load_default_context(self):
103 def load_default_context(self):
104 """
104 """
105 example:
105 example:
106
106
107 def load_default_context(self):
107 def load_default_context(self):
108 c = self._get_local_tmpl_context()
108 c = self._get_local_tmpl_context()
109 c.custom_var = 'foobar'
109 c.custom_var = 'foobar'
110 self._register_global_c(c)
110 self._register_global_c(c)
111 return c
111 return c
112 """
112 """
113 raise NotImplementedError('Needs implementation in view class')
113 raise NotImplementedError('Needs implementation in view class')
114
114
115
115
116 class RepoAppView(BaseAppView):
116 class RepoAppView(BaseAppView):
117
117
118 def __init__(self, context, request):
118 def __init__(self, context, request):
119 super(RepoAppView, self).__init__(context, request)
119 super(RepoAppView, self).__init__(context, request)
120 self.db_repo = request.db_repo
120 self.db_repo = request.db_repo
121 self.db_repo_name = self.db_repo.repo_name
121 self.db_repo_name = self.db_repo.repo_name
122 self.db_repo_pull_requests = ScmModel().get_pull_requests(self.db_repo)
122 self.db_repo_pull_requests = ScmModel().get_pull_requests(self.db_repo)
123
123
124 def _get_local_tmpl_context(self):
124 def _get_local_tmpl_context(self):
125 c = super(RepoAppView, self)._get_local_tmpl_context()
125 c = super(RepoAppView, self)._get_local_tmpl_context()
126 # register common vars for this type of view
126 # register common vars for this type of view
127 c.rhodecode_db_repo = self.db_repo
127 c.rhodecode_db_repo = self.db_repo
128 c.repo_name = self.db_repo_name
128 c.repo_name = self.db_repo_name
129 c.repository_pull_requests = self.db_repo_pull_requests
129 c.repository_pull_requests = self.db_repo_pull_requests
130 return c
130 return c
131
131
132
132
133 class DataGridAppView(object):
134 """
135 Common class to have re-usable grid rendering components
136 """
137
138 def _extract_ordering(self, request):
139 column_index = safe_int(request.GET.get('order[0][column]'))
140 order_dir = request.GET.get(
141 'order[0][dir]', 'desc')
142 order_by = request.GET.get(
143 'columns[%s][data][sort]' % column_index, 'name_raw')
144
145 # translate datatable to DB columns
146 order_by = {
147 'first_name': 'name',
148 'last_name': 'lastname',
149 }.get(order_by) or order_by
150
151 search_q = request.GET.get('search[value]')
152 return search_q, order_by, order_dir
153
154 def _extract_chunk(self, request):
155 start = safe_int(request.GET.get('start'), 0)
156 length = safe_int(request.GET.get('length'), 25)
157 draw = safe_int(request.GET.get('draw'))
158 return draw, start, length
159
160
133 class RepoRoutePredicate(object):
161 class RepoRoutePredicate(object):
134 def __init__(self, val, config):
162 def __init__(self, val, config):
135 self.val = val
163 self.val = val
136
164
137 def text(self):
165 def text(self):
138 return 'repo_route = %s' % self.val
166 return 'repo_route = %s' % self.val
139
167
140 phash = text
168 phash = text
141
169
142 def __call__(self, info, request):
170 def __call__(self, info, request):
143 repo_name = info['match']['repo_name']
171 repo_name = info['match']['repo_name']
144 repo_model = repo.RepoModel()
172 repo_model = repo.RepoModel()
145 by_name_match = repo_model.get_by_repo_name(repo_name, cache=True)
173 by_name_match = repo_model.get_by_repo_name(repo_name, cache=True)
146 # if we match quickly from database, short circuit the operation,
174 # if we match quickly from database, short circuit the operation,
147 # and validate repo based on the type.
175 # and validate repo based on the type.
148 if by_name_match:
176 if by_name_match:
149 # register this as request object we can re-use later
177 # register this as request object we can re-use later
150 request.db_repo = by_name_match
178 request.db_repo = by_name_match
151 return True
179 return True
152
180
153 by_id_match = repo_model.get_repo_by_id(repo_name)
181 by_id_match = repo_model.get_repo_by_id(repo_name)
154 if by_id_match:
182 if by_id_match:
155 request.db_repo = by_id_match
183 request.db_repo = by_id_match
156 return True
184 return True
157
185
158 return False
186 return False
159
187
160
188
161 def includeme(config):
189 def includeme(config):
162 config.add_route_predicate(
190 config.add_route_predicate(
163 'repo_route', RepoRoutePredicate)
191 'repo_route', RepoRoutePredicate)
@@ -1,328 +1,306 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22 import datetime
22 import datetime
23
23
24 from pyramid.httpexceptions import HTTPFound
24 from pyramid.httpexceptions import HTTPFound
25 from pyramid.view import view_config
25 from pyramid.view import view_config
26 from sqlalchemy.sql.functions import coalesce
26 from sqlalchemy.sql.functions import coalesce
27
27
28 from rhodecode.lib.helpers import Page
28 from rhodecode.lib.helpers import Page
29 from rhodecode_tools.lib.ext_json import json
29 from rhodecode_tools.lib.ext_json import json
30
30
31 from rhodecode.apps._base import BaseAppView
31 from rhodecode.apps._base import BaseAppView, DataGridAppView
32 from rhodecode.lib.auth import (
32 from rhodecode.lib.auth import (
33 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
33 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
34 from rhodecode.lib import helpers as h
34 from rhodecode.lib import helpers as h
35 from rhodecode.lib.utils import PartialRenderer
35 from rhodecode.lib.utils import PartialRenderer
36 from rhodecode.lib.utils2 import safe_int, safe_unicode
36 from rhodecode.lib.utils2 import safe_int, safe_unicode
37 from rhodecode.model.auth_token import AuthTokenModel
37 from rhodecode.model.auth_token import AuthTokenModel
38 from rhodecode.model.user import UserModel
38 from rhodecode.model.user import UserModel
39 from rhodecode.model.user_group import UserGroupModel
39 from rhodecode.model.user_group import UserGroupModel
40 from rhodecode.model.db import User, or_
40 from rhodecode.model.db import User, or_
41 from rhodecode.model.meta import Session
41 from rhodecode.model.meta import Session
42
42
43 log = logging.getLogger(__name__)
43 log = logging.getLogger(__name__)
44
44
45
45
46 class AdminUsersView(BaseAppView):
46 class AdminUsersViewData(BaseAppView, DataGridAppView):
47 ALLOW_SCOPED_TOKENS = False
47 ALLOW_SCOPED_TOKENS = False
48 """
48 """
49 This view has alternative version inside EE, if modified please take a look
49 This view has alternative version inside EE, if modified please take a look
50 in there as well.
50 in there as well.
51 """
51 """
52
52
53 def load_default_context(self):
53 def load_default_context(self):
54 c = self._get_local_tmpl_context()
54 c = self._get_local_tmpl_context()
55 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
55 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
56 self._register_global_c(c)
56 self._register_global_c(c)
57 return c
57 return c
58
58
59 def _redirect_for_default_user(self, username):
59 def _redirect_for_default_user(self, username):
60 _ = self.request.translate
60 _ = self.request.translate
61 if username == User.DEFAULT_USER:
61 if username == User.DEFAULT_USER:
62 h.flash(_("You can't edit this user"), category='warning')
62 h.flash(_("You can't edit this user"), category='warning')
63 # TODO(marcink): redirect to 'users' admin panel once this
63 # TODO(marcink): redirect to 'users' admin panel once this
64 # is a pyramid view
64 # is a pyramid view
65 raise HTTPFound('/')
65 raise HTTPFound('/')
66
66
67 def _extract_ordering(self, request):
68 column_index = safe_int(request.GET.get('order[0][column]'))
69 order_dir = request.GET.get(
70 'order[0][dir]', 'desc')
71 order_by = request.GET.get(
72 'columns[%s][data][sort]' % column_index, 'name_raw')
73
74 # translate datatable to DB columns
75 order_by = {
76 'first_name': 'name',
77 'last_name': 'lastname',
78 }.get(order_by) or order_by
79
80 search_q = request.GET.get('search[value]')
81 return search_q, order_by, order_dir
82
83 def _extract_chunk(self, request):
84 start = safe_int(request.GET.get('start'), 0)
85 length = safe_int(request.GET.get('length'), 25)
86 draw = safe_int(request.GET.get('draw'))
87 return draw, start, length
88
89 @HasPermissionAllDecorator('hg.admin')
67 @HasPermissionAllDecorator('hg.admin')
90 @view_config(
68 @view_config(
91 route_name='users', request_method='GET',
69 route_name='users', request_method='GET',
92 renderer='rhodecode:templates/admin/users/users.mako')
70 renderer='rhodecode:templates/admin/users/users.mako')
93 def users_list(self):
71 def users_list(self):
94 c = self.load_default_context()
72 c = self.load_default_context()
95 return self._get_template_context(c)
73 return self._get_template_context(c)
96
74
97 @HasPermissionAllDecorator('hg.admin')
75 @HasPermissionAllDecorator('hg.admin')
98 @view_config(
76 @view_config(
99 # renderer defined below
77 # renderer defined below
100 route_name='users_data', request_method='GET', renderer='json',
78 route_name='users_data', request_method='GET', renderer='json',
101 xhr=True)
79 xhr=True)
102 def users_list_data(self):
80 def users_list_data(self):
103 draw, start, limit = self._extract_chunk(self.request)
81 draw, start, limit = self._extract_chunk(self.request)
104 search_q, order_by, order_dir = self._extract_ordering(self.request)
82 search_q, order_by, order_dir = self._extract_ordering(self.request)
105
83
106 _render = PartialRenderer('data_table/_dt_elements.mako')
84 _render = PartialRenderer('data_table/_dt_elements.mako')
107
85
108 def user_actions(user_id, username):
86 def user_actions(user_id, username):
109 return _render("user_actions", user_id, username)
87 return _render("user_actions", user_id, username)
110
88
111 users_data_total_count = User.query()\
89 users_data_total_count = User.query()\
112 .filter(User.username != User.DEFAULT_USER) \
90 .filter(User.username != User.DEFAULT_USER) \
113 .count()
91 .count()
114
92
115 # json generate
93 # json generate
116 base_q = User.query().filter(User.username != User.DEFAULT_USER)
94 base_q = User.query().filter(User.username != User.DEFAULT_USER)
117
95
118 if search_q:
96 if search_q:
119 like_expression = u'%{}%'.format(safe_unicode(search_q))
97 like_expression = u'%{}%'.format(safe_unicode(search_q))
120 base_q = base_q.filter(or_(
98 base_q = base_q.filter(or_(
121 User.username.ilike(like_expression),
99 User.username.ilike(like_expression),
122 User._email.ilike(like_expression),
100 User._email.ilike(like_expression),
123 User.name.ilike(like_expression),
101 User.name.ilike(like_expression),
124 User.lastname.ilike(like_expression),
102 User.lastname.ilike(like_expression),
125 ))
103 ))
126
104
127 users_data_total_filtered_count = base_q.count()
105 users_data_total_filtered_count = base_q.count()
128
106
129 sort_col = getattr(User, order_by, None)
107 sort_col = getattr(User, order_by, None)
130 if sort_col:
108 if sort_col:
131 if order_dir == 'asc':
109 if order_dir == 'asc':
132 # handle null values properly to order by NULL last
110 # handle null values properly to order by NULL last
133 if order_by in ['last_activity']:
111 if order_by in ['last_activity']:
134 sort_col = coalesce(sort_col, datetime.date.max)
112 sort_col = coalesce(sort_col, datetime.date.max)
135 sort_col = sort_col.asc()
113 sort_col = sort_col.asc()
136 else:
114 else:
137 # handle null values properly to order by NULL last
115 # handle null values properly to order by NULL last
138 if order_by in ['last_activity']:
116 if order_by in ['last_activity']:
139 sort_col = coalesce(sort_col, datetime.date.min)
117 sort_col = coalesce(sort_col, datetime.date.min)
140 sort_col = sort_col.desc()
118 sort_col = sort_col.desc()
141
119
142 base_q = base_q.order_by(sort_col)
120 base_q = base_q.order_by(sort_col)
143 base_q = base_q.offset(start).limit(limit)
121 base_q = base_q.offset(start).limit(limit)
144
122
145 users_list = base_q.all()
123 users_list = base_q.all()
146
124
147 users_data = []
125 users_data = []
148 for user in users_list:
126 for user in users_list:
149 users_data.append({
127 users_data.append({
150 "username": h.gravatar_with_user(user.username),
128 "username": h.gravatar_with_user(user.username),
151 "email": user.email,
129 "email": user.email,
152 "first_name": h.escape(user.name),
130 "first_name": h.escape(user.name),
153 "last_name": h.escape(user.lastname),
131 "last_name": h.escape(user.lastname),
154 "last_login": h.format_date(user.last_login),
132 "last_login": h.format_date(user.last_login),
155 "last_activity": h.format_date(user.last_activity),
133 "last_activity": h.format_date(user.last_activity),
156 "active": h.bool2icon(user.active),
134 "active": h.bool2icon(user.active),
157 "active_raw": user.active,
135 "active_raw": user.active,
158 "admin": h.bool2icon(user.admin),
136 "admin": h.bool2icon(user.admin),
159 "extern_type": user.extern_type,
137 "extern_type": user.extern_type,
160 "extern_name": user.extern_name,
138 "extern_name": user.extern_name,
161 "action": user_actions(user.user_id, user.username),
139 "action": user_actions(user.user_id, user.username),
162 })
140 })
163
141
164 data = ({
142 data = ({
165 'draw': draw,
143 'draw': draw,
166 'data': users_data,
144 'data': users_data,
167 'recordsTotal': users_data_total_count,
145 'recordsTotal': users_data_total_count,
168 'recordsFiltered': users_data_total_filtered_count,
146 'recordsFiltered': users_data_total_filtered_count,
169 })
147 })
170
148
171 return data
149 return data
172
150
173 @LoginRequired()
151 @LoginRequired()
174 @HasPermissionAllDecorator('hg.admin')
152 @HasPermissionAllDecorator('hg.admin')
175 @view_config(
153 @view_config(
176 route_name='edit_user_auth_tokens', request_method='GET',
154 route_name='edit_user_auth_tokens', request_method='GET',
177 renderer='rhodecode:templates/admin/users/user_edit.mako')
155 renderer='rhodecode:templates/admin/users/user_edit.mako')
178 def auth_tokens(self):
156 def auth_tokens(self):
179 _ = self.request.translate
157 _ = self.request.translate
180 c = self.load_default_context()
158 c = self.load_default_context()
181
159
182 user_id = self.request.matchdict.get('user_id')
160 user_id = self.request.matchdict.get('user_id')
183 c.user = User.get_or_404(user_id, pyramid_exc=True)
161 c.user = User.get_or_404(user_id, pyramid_exc=True)
184 self._redirect_for_default_user(c.user.username)
162 self._redirect_for_default_user(c.user.username)
185
163
186 c.active = 'auth_tokens'
164 c.active = 'auth_tokens'
187
165
188 c.lifetime_values = [
166 c.lifetime_values = [
189 (str(-1), _('forever')),
167 (str(-1), _('forever')),
190 (str(5), _('5 minutes')),
168 (str(5), _('5 minutes')),
191 (str(60), _('1 hour')),
169 (str(60), _('1 hour')),
192 (str(60 * 24), _('1 day')),
170 (str(60 * 24), _('1 day')),
193 (str(60 * 24 * 30), _('1 month')),
171 (str(60 * 24 * 30), _('1 month')),
194 ]
172 ]
195 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
173 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
196 c.role_values = [
174 c.role_values = [
197 (x, AuthTokenModel.cls._get_role_name(x))
175 (x, AuthTokenModel.cls._get_role_name(x))
198 for x in AuthTokenModel.cls.ROLES]
176 for x in AuthTokenModel.cls.ROLES]
199 c.role_options = [(c.role_values, _("Role"))]
177 c.role_options = [(c.role_values, _("Role"))]
200 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
178 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
201 c.user.user_id, show_expired=True)
179 c.user.user_id, show_expired=True)
202 return self._get_template_context(c)
180 return self._get_template_context(c)
203
181
204 def maybe_attach_token_scope(self, token):
182 def maybe_attach_token_scope(self, token):
205 # implemented in EE edition
183 # implemented in EE edition
206 pass
184 pass
207
185
208 @LoginRequired()
186 @LoginRequired()
209 @HasPermissionAllDecorator('hg.admin')
187 @HasPermissionAllDecorator('hg.admin')
210 @CSRFRequired()
188 @CSRFRequired()
211 @view_config(
189 @view_config(
212 route_name='edit_user_auth_tokens_add', request_method='POST')
190 route_name='edit_user_auth_tokens_add', request_method='POST')
213 def auth_tokens_add(self):
191 def auth_tokens_add(self):
214 _ = self.request.translate
192 _ = self.request.translate
215 c = self.load_default_context()
193 c = self.load_default_context()
216
194
217 user_id = self.request.matchdict.get('user_id')
195 user_id = self.request.matchdict.get('user_id')
218 c.user = User.get_or_404(user_id, pyramid_exc=True)
196 c.user = User.get_or_404(user_id, pyramid_exc=True)
219 self._redirect_for_default_user(c.user.username)
197 self._redirect_for_default_user(c.user.username)
220
198
221 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
199 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
222 description = self.request.POST.get('description')
200 description = self.request.POST.get('description')
223 role = self.request.POST.get('role')
201 role = self.request.POST.get('role')
224
202
225 token = AuthTokenModel().create(
203 token = AuthTokenModel().create(
226 c.user.user_id, description, lifetime, role)
204 c.user.user_id, description, lifetime, role)
227 self.maybe_attach_token_scope(token)
205 self.maybe_attach_token_scope(token)
228 Session().commit()
206 Session().commit()
229
207
230 h.flash(_("Auth token successfully created"), category='success')
208 h.flash(_("Auth token successfully created"), category='success')
231 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
209 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
232
210
233 @LoginRequired()
211 @LoginRequired()
234 @HasPermissionAllDecorator('hg.admin')
212 @HasPermissionAllDecorator('hg.admin')
235 @CSRFRequired()
213 @CSRFRequired()
236 @view_config(
214 @view_config(
237 route_name='edit_user_auth_tokens_delete', request_method='POST')
215 route_name='edit_user_auth_tokens_delete', request_method='POST')
238 def auth_tokens_delete(self):
216 def auth_tokens_delete(self):
239 _ = self.request.translate
217 _ = self.request.translate
240 c = self.load_default_context()
218 c = self.load_default_context()
241
219
242 user_id = self.request.matchdict.get('user_id')
220 user_id = self.request.matchdict.get('user_id')
243 c.user = User.get_or_404(user_id, pyramid_exc=True)
221 c.user = User.get_or_404(user_id, pyramid_exc=True)
244 self._redirect_for_default_user(c.user.username)
222 self._redirect_for_default_user(c.user.username)
245
223
246 del_auth_token = self.request.POST.get('del_auth_token')
224 del_auth_token = self.request.POST.get('del_auth_token')
247
225
248 if del_auth_token:
226 if del_auth_token:
249 AuthTokenModel().delete(del_auth_token, c.user.user_id)
227 AuthTokenModel().delete(del_auth_token, c.user.user_id)
250 Session().commit()
228 Session().commit()
251 h.flash(_("Auth token successfully deleted"), category='success')
229 h.flash(_("Auth token successfully deleted"), category='success')
252
230
253 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
231 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
254
232
255 @LoginRequired()
233 @LoginRequired()
256 @HasPermissionAllDecorator('hg.admin')
234 @HasPermissionAllDecorator('hg.admin')
257 @view_config(
235 @view_config(
258 route_name='edit_user_groups_management', request_method='GET',
236 route_name='edit_user_groups_management', request_method='GET',
259 renderer='rhodecode:templates/admin/users/user_edit.mako')
237 renderer='rhodecode:templates/admin/users/user_edit.mako')
260 def groups_management(self):
238 def groups_management(self):
261 c = self.load_default_context()
239 c = self.load_default_context()
262
240
263 user_id = self.request.matchdict.get('user_id')
241 user_id = self.request.matchdict.get('user_id')
264 c.user = User.get_or_404(user_id, pyramid_exc=True)
242 c.user = User.get_or_404(user_id, pyramid_exc=True)
265 c.data = c.user.group_member
243 c.data = c.user.group_member
266 self._redirect_for_default_user(c.user.username)
244 self._redirect_for_default_user(c.user.username)
267 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group) for group in c.user.group_member]
245 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group) for group in c.user.group_member]
268 c.groups = json.dumps(groups)
246 c.groups = json.dumps(groups)
269 c.active = 'groups'
247 c.active = 'groups'
270
248
271 return self._get_template_context(c)
249 return self._get_template_context(c)
272
250
273 @LoginRequired()
251 @LoginRequired()
274 @HasPermissionAllDecorator('hg.admin')
252 @HasPermissionAllDecorator('hg.admin')
275 @view_config(
253 @view_config(
276 route_name='edit_user_groups_management_updates', request_method='POST')
254 route_name='edit_user_groups_management_updates', request_method='POST')
277 def groups_management_updates(self):
255 def groups_management_updates(self):
278 _ = self.request.translate
256 _ = self.request.translate
279 c = self.load_default_context()
257 c = self.load_default_context()
280
258
281 user_id = self.request.matchdict.get('user_id')
259 user_id = self.request.matchdict.get('user_id')
282 c.user = User.get_or_404(user_id, pyramid_exc=True)
260 c.user = User.get_or_404(user_id, pyramid_exc=True)
283 self._redirect_for_default_user(c.user.username)
261 self._redirect_for_default_user(c.user.username)
284
262
285 users_groups = set(self.request.POST.getall('users_group_id'))
263 users_groups = set(self.request.POST.getall('users_group_id'))
286 users_groups_model = []
264 users_groups_model = []
287
265
288 for ugid in users_groups:
266 for ugid in users_groups:
289 users_groups_model.append(UserGroupModel().get_group(safe_int(ugid)))
267 users_groups_model.append(UserGroupModel().get_group(safe_int(ugid)))
290 user_group_model = UserGroupModel()
268 user_group_model = UserGroupModel()
291 user_group_model.change_groups(c.user, users_groups_model)
269 user_group_model.change_groups(c.user, users_groups_model)
292
270
293 Session().commit()
271 Session().commit()
294 c.active = 'user_groups_management'
272 c.active = 'user_groups_management'
295 h.flash(_("Groups successfully changed"), category='success')
273 h.flash(_("Groups successfully changed"), category='success')
296
274
297 return HTTPFound(h.route_path(
275 return HTTPFound(h.route_path(
298 'edit_user_groups_management', user_id=user_id))
276 'edit_user_groups_management', user_id=user_id))
299
277
300 @LoginRequired()
278 @LoginRequired()
301 @HasPermissionAllDecorator('hg.admin')
279 @HasPermissionAllDecorator('hg.admin')
302 @view_config(
280 @view_config(
303 route_name='edit_user_audit_logs', request_method='GET',
281 route_name='edit_user_audit_logs', request_method='GET',
304 renderer='rhodecode:templates/admin/users/user_edit.mako')
282 renderer='rhodecode:templates/admin/users/user_edit.mako')
305 def user_audit_logs(self):
283 def user_audit_logs(self):
306 _ = self.request.translate
284 _ = self.request.translate
307 c = self.load_default_context()
285 c = self.load_default_context()
308
286
309 user_id = self.request.matchdict.get('user_id')
287 user_id = self.request.matchdict.get('user_id')
310 c.user = User.get_or_404(user_id, pyramid_exc=True)
288 c.user = User.get_or_404(user_id, pyramid_exc=True)
311 self._redirect_for_default_user(c.user.username)
289 self._redirect_for_default_user(c.user.username)
312 c.active = 'audit'
290 c.active = 'audit'
313
291
314 p = safe_int(self.request.GET.get('page', 1), 1)
292 p = safe_int(self.request.GET.get('page', 1), 1)
315
293
316 filter_term = self.request.GET.get('filter')
294 filter_term = self.request.GET.get('filter')
317 c.user_log = UserModel().get_user_log(c.user, filter_term)
295 c.user_log = UserModel().get_user_log(c.user, filter_term)
318
296
319 def url_generator(**kw):
297 def url_generator(**kw):
320 if filter_term:
298 if filter_term:
321 kw['filter'] = filter_term
299 kw['filter'] = filter_term
322 return self.request.current_route_path(_query=kw)
300 return self.request.current_route_path(_query=kw)
323
301
324 c.user_log = Page(c.user_log, page=p, items_per_page=10,
302 c.user_log = Page(c.user_log, page=p, items_per_page=10,
325 url=url_generator)
303 url=url_generator)
326 c.filter_term = filter_term
304 c.filter_term = filter_term
327 return self._get_template_context(c)
305 return self._get_template_context(c)
328
306
General Comments 0
You need to be logged in to leave comments. Login now