##// END OF EJS Templates
views: load template context for XHR views....
marcink -
r2308:323842c2 default
parent child Browse files
Show More
@@ -1,247 +1,248 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
22
23 import formencode
23 import formencode
24 import formencode.htmlfill
24 import formencode.htmlfill
25
25
26 from pyramid.httpexceptions import HTTPFound
26 from pyramid.httpexceptions import HTTPFound
27 from pyramid.view import view_config
27 from pyramid.view import view_config
28 from pyramid.response import Response
28 from pyramid.response import Response
29 from pyramid.renderers import render
29 from pyramid.renderers import render
30
30
31 from rhodecode.apps._base import BaseAppView, DataGridAppView
31 from rhodecode.apps._base import BaseAppView, DataGridAppView
32 from rhodecode.lib.auth import (
32 from rhodecode.lib.auth import (
33 LoginRequired, NotAnonymous, CSRFRequired, HasPermissionAnyDecorator)
33 LoginRequired, NotAnonymous, CSRFRequired, HasPermissionAnyDecorator)
34 from rhodecode.lib import helpers as h, audit_logger
34 from rhodecode.lib import helpers as h, audit_logger
35 from rhodecode.lib.utils2 import safe_unicode
35 from rhodecode.lib.utils2 import safe_unicode
36
36
37 from rhodecode.model.forms import UserGroupForm
37 from rhodecode.model.forms import UserGroupForm
38 from rhodecode.model.permission import PermissionModel
38 from rhodecode.model.permission import PermissionModel
39 from rhodecode.model.scm import UserGroupList
39 from rhodecode.model.scm import UserGroupList
40 from rhodecode.model.db import (
40 from rhodecode.model.db import (
41 or_, count, User, UserGroup, UserGroupMember)
41 or_, count, User, UserGroup, UserGroupMember)
42 from rhodecode.model.meta import Session
42 from rhodecode.model.meta import Session
43 from rhodecode.model.user_group import UserGroupModel
43 from rhodecode.model.user_group import UserGroupModel
44
44
45 log = logging.getLogger(__name__)
45 log = logging.getLogger(__name__)
46
46
47
47
48 class AdminUserGroupsView(BaseAppView, DataGridAppView):
48 class AdminUserGroupsView(BaseAppView, DataGridAppView):
49
49
50 def load_default_context(self):
50 def load_default_context(self):
51 c = self._get_local_tmpl_context()
51 c = self._get_local_tmpl_context()
52
52
53 PermissionModel().set_global_permission_choices(
53 PermissionModel().set_global_permission_choices(
54 c, gettext_translator=self.request.translate)
54 c, gettext_translator=self.request.translate)
55
55
56 self._register_global_c(c)
56 self._register_global_c(c)
57 return c
57 return c
58
58
59 # permission check in data loading of
59 # permission check in data loading of
60 # `user_groups_list_data` via UserGroupList
60 # `user_groups_list_data` via UserGroupList
61 @LoginRequired()
61 @LoginRequired()
62 @NotAnonymous()
62 @NotAnonymous()
63 @view_config(
63 @view_config(
64 route_name='user_groups', request_method='GET',
64 route_name='user_groups', request_method='GET',
65 renderer='rhodecode:templates/admin/user_groups/user_groups.mako')
65 renderer='rhodecode:templates/admin/user_groups/user_groups.mako')
66 def user_groups_list(self):
66 def user_groups_list(self):
67 c = self.load_default_context()
67 c = self.load_default_context()
68 return self._get_template_context(c)
68 return self._get_template_context(c)
69
69
70 # permission check inside
70 # permission check inside
71 @LoginRequired()
71 @LoginRequired()
72 @NotAnonymous()
72 @NotAnonymous()
73 @view_config(
73 @view_config(
74 route_name='user_groups_data', request_method='GET',
74 route_name='user_groups_data', request_method='GET',
75 renderer='json_ext', xhr=True)
75 renderer='json_ext', xhr=True)
76 def user_groups_list_data(self):
76 def user_groups_list_data(self):
77 self.load_default_context()
77 column_map = {
78 column_map = {
78 'active': 'users_group_active',
79 'active': 'users_group_active',
79 'description': 'user_group_description',
80 'description': 'user_group_description',
80 'members': 'members_total',
81 'members': 'members_total',
81 'owner': 'user_username',
82 'owner': 'user_username',
82 'sync': 'group_data'
83 'sync': 'group_data'
83 }
84 }
84 draw, start, limit = self._extract_chunk(self.request)
85 draw, start, limit = self._extract_chunk(self.request)
85 search_q, order_by, order_dir = self._extract_ordering(
86 search_q, order_by, order_dir = self._extract_ordering(
86 self.request, column_map=column_map)
87 self.request, column_map=column_map)
87
88
88 _render = self.request.get_partial_renderer(
89 _render = self.request.get_partial_renderer(
89 'data_table/_dt_elements.mako')
90 'data_table/_dt_elements.mako')
90
91
91 def user_group_name(user_group_id, user_group_name):
92 def user_group_name(user_group_id, user_group_name):
92 return _render("user_group_name", user_group_id, user_group_name)
93 return _render("user_group_name", user_group_id, user_group_name)
93
94
94 def user_group_actions(user_group_id, user_group_name):
95 def user_group_actions(user_group_id, user_group_name):
95 return _render("user_group_actions", user_group_id, user_group_name)
96 return _render("user_group_actions", user_group_id, user_group_name)
96
97
97 def user_profile(username):
98 def user_profile(username):
98 return _render('user_profile', username)
99 return _render('user_profile', username)
99
100
100 auth_user_group_list = UserGroupList(
101 auth_user_group_list = UserGroupList(
101 UserGroup.query().all(), perm_set=['usergroup.admin'])
102 UserGroup.query().all(), perm_set=['usergroup.admin'])
102
103
103 allowed_ids = [-1]
104 allowed_ids = [-1]
104 for user_group in auth_user_group_list:
105 for user_group in auth_user_group_list:
105 allowed_ids.append(user_group.users_group_id)
106 allowed_ids.append(user_group.users_group_id)
106
107
107 user_groups_data_total_count = UserGroup.query()\
108 user_groups_data_total_count = UserGroup.query()\
108 .filter(UserGroup.users_group_id.in_(allowed_ids))\
109 .filter(UserGroup.users_group_id.in_(allowed_ids))\
109 .count()
110 .count()
110
111
111 member_count = count(UserGroupMember.user_id)
112 member_count = count(UserGroupMember.user_id)
112 base_q = Session.query(
113 base_q = Session.query(
113 UserGroup.users_group_name,
114 UserGroup.users_group_name,
114 UserGroup.user_group_description,
115 UserGroup.user_group_description,
115 UserGroup.users_group_active,
116 UserGroup.users_group_active,
116 UserGroup.users_group_id,
117 UserGroup.users_group_id,
117 UserGroup.group_data,
118 UserGroup.group_data,
118 User,
119 User,
119 member_count.label('member_count')
120 member_count.label('member_count')
120 ) \
121 ) \
121 .filter(UserGroup.users_group_id.in_(allowed_ids)) \
122 .filter(UserGroup.users_group_id.in_(allowed_ids)) \
122 .outerjoin(UserGroupMember) \
123 .outerjoin(UserGroupMember) \
123 .join(User, User.user_id == UserGroup.user_id) \
124 .join(User, User.user_id == UserGroup.user_id) \
124 .group_by(UserGroup, User)
125 .group_by(UserGroup, User)
125
126
126 if search_q:
127 if search_q:
127 like_expression = u'%{}%'.format(safe_unicode(search_q))
128 like_expression = u'%{}%'.format(safe_unicode(search_q))
128 base_q = base_q.filter(or_(
129 base_q = base_q.filter(or_(
129 UserGroup.users_group_name.ilike(like_expression),
130 UserGroup.users_group_name.ilike(like_expression),
130 ))
131 ))
131
132
132 user_groups_data_total_filtered_count = base_q.count()
133 user_groups_data_total_filtered_count = base_q.count()
133
134
134 if order_by == 'members_total':
135 if order_by == 'members_total':
135 sort_col = member_count
136 sort_col = member_count
136 elif order_by == 'user_username':
137 elif order_by == 'user_username':
137 sort_col = User.username
138 sort_col = User.username
138 else:
139 else:
139 sort_col = getattr(UserGroup, order_by, None)
140 sort_col = getattr(UserGroup, order_by, None)
140
141
141 if isinstance(sort_col, count) or sort_col:
142 if isinstance(sort_col, count) or sort_col:
142 if order_dir == 'asc':
143 if order_dir == 'asc':
143 sort_col = sort_col.asc()
144 sort_col = sort_col.asc()
144 else:
145 else:
145 sort_col = sort_col.desc()
146 sort_col = sort_col.desc()
146
147
147 base_q = base_q.order_by(sort_col)
148 base_q = base_q.order_by(sort_col)
148 base_q = base_q.offset(start).limit(limit)
149 base_q = base_q.offset(start).limit(limit)
149
150
150 # authenticated access to user groups
151 # authenticated access to user groups
151 auth_user_group_list = base_q.all()
152 auth_user_group_list = base_q.all()
152
153
153 user_groups_data = []
154 user_groups_data = []
154 for user_gr in auth_user_group_list:
155 for user_gr in auth_user_group_list:
155 user_groups_data.append({
156 user_groups_data.append({
156 "users_group_name": user_group_name(
157 "users_group_name": user_group_name(
157 user_gr.users_group_id, h.escape(user_gr.users_group_name)),
158 user_gr.users_group_id, h.escape(user_gr.users_group_name)),
158 "name_raw": h.escape(user_gr.users_group_name),
159 "name_raw": h.escape(user_gr.users_group_name),
159 "description": h.escape(user_gr.user_group_description),
160 "description": h.escape(user_gr.user_group_description),
160 "members": user_gr.member_count,
161 "members": user_gr.member_count,
161 # NOTE(marcink): because of advanced query we
162 # NOTE(marcink): because of advanced query we
162 # need to load it like that
163 # need to load it like that
163 "sync": UserGroup._load_group_data(
164 "sync": UserGroup._load_group_data(
164 user_gr.group_data).get('extern_type'),
165 user_gr.group_data).get('extern_type'),
165 "active": h.bool2icon(user_gr.users_group_active),
166 "active": h.bool2icon(user_gr.users_group_active),
166 "owner": user_profile(user_gr.User.username),
167 "owner": user_profile(user_gr.User.username),
167 "action": user_group_actions(
168 "action": user_group_actions(
168 user_gr.users_group_id, user_gr.users_group_name)
169 user_gr.users_group_id, user_gr.users_group_name)
169 })
170 })
170
171
171 data = ({
172 data = ({
172 'draw': draw,
173 'draw': draw,
173 'data': user_groups_data,
174 'data': user_groups_data,
174 'recordsTotal': user_groups_data_total_count,
175 'recordsTotal': user_groups_data_total_count,
175 'recordsFiltered': user_groups_data_total_filtered_count,
176 'recordsFiltered': user_groups_data_total_filtered_count,
176 })
177 })
177
178
178 return data
179 return data
179
180
180 @LoginRequired()
181 @LoginRequired()
181 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
182 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
182 @view_config(
183 @view_config(
183 route_name='user_groups_new', request_method='GET',
184 route_name='user_groups_new', request_method='GET',
184 renderer='rhodecode:templates/admin/user_groups/user_group_add.mako')
185 renderer='rhodecode:templates/admin/user_groups/user_group_add.mako')
185 def user_groups_new(self):
186 def user_groups_new(self):
186 c = self.load_default_context()
187 c = self.load_default_context()
187 return self._get_template_context(c)
188 return self._get_template_context(c)
188
189
189 @LoginRequired()
190 @LoginRequired()
190 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
191 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
191 @CSRFRequired()
192 @CSRFRequired()
192 @view_config(
193 @view_config(
193 route_name='user_groups_create', request_method='POST',
194 route_name='user_groups_create', request_method='POST',
194 renderer='rhodecode:templates/admin/user_groups/user_group_add.mako')
195 renderer='rhodecode:templates/admin/user_groups/user_group_add.mako')
195 def user_groups_create(self):
196 def user_groups_create(self):
196 _ = self.request.translate
197 _ = self.request.translate
197 c = self.load_default_context()
198 c = self.load_default_context()
198 users_group_form = UserGroupForm()()
199 users_group_form = UserGroupForm()()
199
200
200 user_group_name = self.request.POST.get('users_group_name')
201 user_group_name = self.request.POST.get('users_group_name')
201 try:
202 try:
202 form_result = users_group_form.to_python(dict(self.request.POST))
203 form_result = users_group_form.to_python(dict(self.request.POST))
203 user_group = UserGroupModel().create(
204 user_group = UserGroupModel().create(
204 name=form_result['users_group_name'],
205 name=form_result['users_group_name'],
205 description=form_result['user_group_description'],
206 description=form_result['user_group_description'],
206 owner=self._rhodecode_user.user_id,
207 owner=self._rhodecode_user.user_id,
207 active=form_result['users_group_active'])
208 active=form_result['users_group_active'])
208 Session().flush()
209 Session().flush()
209 creation_data = user_group.get_api_data()
210 creation_data = user_group.get_api_data()
210 user_group_name = form_result['users_group_name']
211 user_group_name = form_result['users_group_name']
211
212
212 audit_logger.store_web(
213 audit_logger.store_web(
213 'user_group.create', action_data={'data': creation_data},
214 'user_group.create', action_data={'data': creation_data},
214 user=self._rhodecode_user)
215 user=self._rhodecode_user)
215
216
216 user_group_link = h.link_to(
217 user_group_link = h.link_to(
217 h.escape(user_group_name),
218 h.escape(user_group_name),
218 h.route_path(
219 h.route_path(
219 'edit_user_group', user_group_id=user_group.users_group_id))
220 'edit_user_group', user_group_id=user_group.users_group_id))
220 h.flash(h.literal(_('Created user group %(user_group_link)s')
221 h.flash(h.literal(_('Created user group %(user_group_link)s')
221 % {'user_group_link': user_group_link}),
222 % {'user_group_link': user_group_link}),
222 category='success')
223 category='success')
223 Session().commit()
224 Session().commit()
224 user_group_id = user_group.users_group_id
225 user_group_id = user_group.users_group_id
225 except formencode.Invalid as errors:
226 except formencode.Invalid as errors:
226
227
227 data = render(
228 data = render(
228 'rhodecode:templates/admin/user_groups/user_group_add.mako',
229 'rhodecode:templates/admin/user_groups/user_group_add.mako',
229 self._get_template_context(c), self.request)
230 self._get_template_context(c), self.request)
230 html = formencode.htmlfill.render(
231 html = formencode.htmlfill.render(
231 data,
232 data,
232 defaults=errors.value,
233 defaults=errors.value,
233 errors=errors.error_dict or {},
234 errors=errors.error_dict or {},
234 prefix_error=False,
235 prefix_error=False,
235 encoding="UTF-8",
236 encoding="UTF-8",
236 force_defaults=False
237 force_defaults=False
237 )
238 )
238 return Response(html)
239 return Response(html)
239
240
240 except Exception:
241 except Exception:
241 log.exception("Exception creating user group")
242 log.exception("Exception creating user group")
242 h.flash(_('Error occurred during creation of user group %s') \
243 h.flash(_('Error occurred during creation of user group %s') \
243 % user_group_name, category='error')
244 % user_group_name, category='error')
244 raise HTTPFound(h.route_path('user_groups_new'))
245 raise HTTPFound(h.route_path('user_groups_new'))
245
246
246 raise HTTPFound(
247 raise HTTPFound(
247 h.route_path('edit_user_group', user_group_id=user_group_id))
248 h.route_path('edit_user_group', user_group_id=user_group_id))
@@ -1,1178 +1,1179 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 import formencode
23 import formencode
24 import formencode.htmlfill
24 import formencode.htmlfill
25
25
26 from pyramid.httpexceptions import HTTPFound
26 from pyramid.httpexceptions import HTTPFound
27 from pyramid.view import view_config
27 from pyramid.view import view_config
28 from pyramid.renderers import render
28 from pyramid.renderers import render
29 from pyramid.response import Response
29 from pyramid.response import Response
30
30
31 from rhodecode.apps._base import BaseAppView, DataGridAppView, UserAppView
31 from rhodecode.apps._base import BaseAppView, DataGridAppView, UserAppView
32 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
32 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
33 from rhodecode.authentication.plugins import auth_rhodecode
33 from rhodecode.authentication.plugins import auth_rhodecode
34 from rhodecode.events import trigger
34 from rhodecode.events import trigger
35
35
36 from rhodecode.lib import audit_logger
36 from rhodecode.lib import audit_logger
37 from rhodecode.lib.exceptions import (
37 from rhodecode.lib.exceptions import (
38 UserCreationError, UserOwnsReposException, UserOwnsRepoGroupsException,
38 UserCreationError, UserOwnsReposException, UserOwnsRepoGroupsException,
39 UserOwnsUserGroupsException, DefaultUserException)
39 UserOwnsUserGroupsException, DefaultUserException)
40 from rhodecode.lib.ext_json import json
40 from rhodecode.lib.ext_json import json
41 from rhodecode.lib.auth import (
41 from rhodecode.lib.auth import (
42 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
42 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
43 from rhodecode.lib import helpers as h
43 from rhodecode.lib import helpers as h
44 from rhodecode.lib.utils2 import safe_int, safe_unicode, AttributeDict
44 from rhodecode.lib.utils2 import safe_int, safe_unicode, AttributeDict
45 from rhodecode.model.auth_token import AuthTokenModel
45 from rhodecode.model.auth_token import AuthTokenModel
46 from rhodecode.model.forms import (
46 from rhodecode.model.forms import (
47 UserForm, UserIndividualPermissionsForm, UserPermissionsForm)
47 UserForm, UserIndividualPermissionsForm, UserPermissionsForm)
48 from rhodecode.model.permission import PermissionModel
48 from rhodecode.model.permission import PermissionModel
49 from rhodecode.model.repo_group import RepoGroupModel
49 from rhodecode.model.repo_group import RepoGroupModel
50 from rhodecode.model.ssh_key import SshKeyModel
50 from rhodecode.model.ssh_key import SshKeyModel
51 from rhodecode.model.user import UserModel
51 from rhodecode.model.user import UserModel
52 from rhodecode.model.user_group import UserGroupModel
52 from rhodecode.model.user_group import UserGroupModel
53 from rhodecode.model.db import (
53 from rhodecode.model.db import (
54 or_, coalesce,IntegrityError, User, UserGroup, UserIpMap, UserEmailMap,
54 or_, coalesce,IntegrityError, User, UserGroup, UserIpMap, UserEmailMap,
55 UserApiKeys, UserSshKeys, RepoGroup)
55 UserApiKeys, UserSshKeys, RepoGroup)
56 from rhodecode.model.meta import Session
56 from rhodecode.model.meta import Session
57
57
58 log = logging.getLogger(__name__)
58 log = logging.getLogger(__name__)
59
59
60
60
61 class AdminUsersView(BaseAppView, DataGridAppView):
61 class AdminUsersView(BaseAppView, DataGridAppView):
62
62
63 def load_default_context(self):
63 def load_default_context(self):
64 c = self._get_local_tmpl_context()
64 c = self._get_local_tmpl_context()
65 self._register_global_c(c)
65 self._register_global_c(c)
66 return c
66 return c
67
67
68 @LoginRequired()
68 @LoginRequired()
69 @HasPermissionAllDecorator('hg.admin')
69 @HasPermissionAllDecorator('hg.admin')
70 @view_config(
70 @view_config(
71 route_name='users', request_method='GET',
71 route_name='users', request_method='GET',
72 renderer='rhodecode:templates/admin/users/users.mako')
72 renderer='rhodecode:templates/admin/users/users.mako')
73 def users_list(self):
73 def users_list(self):
74 c = self.load_default_context()
74 c = self.load_default_context()
75 return self._get_template_context(c)
75 return self._get_template_context(c)
76
76
77 @LoginRequired()
77 @LoginRequired()
78 @HasPermissionAllDecorator('hg.admin')
78 @HasPermissionAllDecorator('hg.admin')
79 @view_config(
79 @view_config(
80 # renderer defined below
80 # renderer defined below
81 route_name='users_data', request_method='GET',
81 route_name='users_data', request_method='GET',
82 renderer='json_ext', xhr=True)
82 renderer='json_ext', xhr=True)
83 def users_list_data(self):
83 def users_list_data(self):
84 self.load_default_context()
84 column_map = {
85 column_map = {
85 'first_name': 'name',
86 'first_name': 'name',
86 'last_name': 'lastname',
87 'last_name': 'lastname',
87 }
88 }
88 draw, start, limit = self._extract_chunk(self.request)
89 draw, start, limit = self._extract_chunk(self.request)
89 search_q, order_by, order_dir = self._extract_ordering(
90 search_q, order_by, order_dir = self._extract_ordering(
90 self.request, column_map=column_map)
91 self.request, column_map=column_map)
91
92
92 _render = self.request.get_partial_renderer(
93 _render = self.request.get_partial_renderer(
93 'data_table/_dt_elements.mako')
94 'data_table/_dt_elements.mako')
94
95
95 def user_actions(user_id, username):
96 def user_actions(user_id, username):
96 return _render("user_actions", user_id, username)
97 return _render("user_actions", user_id, username)
97
98
98 users_data_total_count = User.query()\
99 users_data_total_count = User.query()\
99 .filter(User.username != User.DEFAULT_USER) \
100 .filter(User.username != User.DEFAULT_USER) \
100 .count()
101 .count()
101
102
102 # json generate
103 # json generate
103 base_q = User.query().filter(User.username != User.DEFAULT_USER)
104 base_q = User.query().filter(User.username != User.DEFAULT_USER)
104
105
105 if search_q:
106 if search_q:
106 like_expression = u'%{}%'.format(safe_unicode(search_q))
107 like_expression = u'%{}%'.format(safe_unicode(search_q))
107 base_q = base_q.filter(or_(
108 base_q = base_q.filter(or_(
108 User.username.ilike(like_expression),
109 User.username.ilike(like_expression),
109 User._email.ilike(like_expression),
110 User._email.ilike(like_expression),
110 User.name.ilike(like_expression),
111 User.name.ilike(like_expression),
111 User.lastname.ilike(like_expression),
112 User.lastname.ilike(like_expression),
112 ))
113 ))
113
114
114 users_data_total_filtered_count = base_q.count()
115 users_data_total_filtered_count = base_q.count()
115
116
116 sort_col = getattr(User, order_by, None)
117 sort_col = getattr(User, order_by, None)
117 if sort_col:
118 if sort_col:
118 if order_dir == 'asc':
119 if order_dir == 'asc':
119 # handle null values properly to order by NULL last
120 # handle null values properly to order by NULL last
120 if order_by in ['last_activity']:
121 if order_by in ['last_activity']:
121 sort_col = coalesce(sort_col, datetime.date.max)
122 sort_col = coalesce(sort_col, datetime.date.max)
122 sort_col = sort_col.asc()
123 sort_col = sort_col.asc()
123 else:
124 else:
124 # handle null values properly to order by NULL last
125 # handle null values properly to order by NULL last
125 if order_by in ['last_activity']:
126 if order_by in ['last_activity']:
126 sort_col = coalesce(sort_col, datetime.date.min)
127 sort_col = coalesce(sort_col, datetime.date.min)
127 sort_col = sort_col.desc()
128 sort_col = sort_col.desc()
128
129
129 base_q = base_q.order_by(sort_col)
130 base_q = base_q.order_by(sort_col)
130 base_q = base_q.offset(start).limit(limit)
131 base_q = base_q.offset(start).limit(limit)
131
132
132 users_list = base_q.all()
133 users_list = base_q.all()
133
134
134 users_data = []
135 users_data = []
135 for user in users_list:
136 for user in users_list:
136 users_data.append({
137 users_data.append({
137 "username": h.gravatar_with_user(self.request, user.username),
138 "username": h.gravatar_with_user(self.request, user.username),
138 "email": user.email,
139 "email": user.email,
139 "first_name": user.first_name,
140 "first_name": user.first_name,
140 "last_name": user.last_name,
141 "last_name": user.last_name,
141 "last_login": h.format_date(user.last_login),
142 "last_login": h.format_date(user.last_login),
142 "last_activity": h.format_date(user.last_activity),
143 "last_activity": h.format_date(user.last_activity),
143 "active": h.bool2icon(user.active),
144 "active": h.bool2icon(user.active),
144 "active_raw": user.active,
145 "active_raw": user.active,
145 "admin": h.bool2icon(user.admin),
146 "admin": h.bool2icon(user.admin),
146 "extern_type": user.extern_type,
147 "extern_type": user.extern_type,
147 "extern_name": user.extern_name,
148 "extern_name": user.extern_name,
148 "action": user_actions(user.user_id, user.username),
149 "action": user_actions(user.user_id, user.username),
149 })
150 })
150
151
151 data = ({
152 data = ({
152 'draw': draw,
153 'draw': draw,
153 'data': users_data,
154 'data': users_data,
154 'recordsTotal': users_data_total_count,
155 'recordsTotal': users_data_total_count,
155 'recordsFiltered': users_data_total_filtered_count,
156 'recordsFiltered': users_data_total_filtered_count,
156 })
157 })
157
158
158 return data
159 return data
159
160
160 def _set_personal_repo_group_template_vars(self, c_obj):
161 def _set_personal_repo_group_template_vars(self, c_obj):
161 DummyUser = AttributeDict({
162 DummyUser = AttributeDict({
162 'username': '${username}',
163 'username': '${username}',
163 'user_id': '${user_id}',
164 'user_id': '${user_id}',
164 })
165 })
165 c_obj.default_create_repo_group = RepoGroupModel() \
166 c_obj.default_create_repo_group = RepoGroupModel() \
166 .get_default_create_personal_repo_group()
167 .get_default_create_personal_repo_group()
167 c_obj.personal_repo_group_name = RepoGroupModel() \
168 c_obj.personal_repo_group_name = RepoGroupModel() \
168 .get_personal_group_name(DummyUser)
169 .get_personal_group_name(DummyUser)
169
170
170 @LoginRequired()
171 @LoginRequired()
171 @HasPermissionAllDecorator('hg.admin')
172 @HasPermissionAllDecorator('hg.admin')
172 @view_config(
173 @view_config(
173 route_name='users_new', request_method='GET',
174 route_name='users_new', request_method='GET',
174 renderer='rhodecode:templates/admin/users/user_add.mako')
175 renderer='rhodecode:templates/admin/users/user_add.mako')
175 def users_new(self):
176 def users_new(self):
176 _ = self.request.translate
177 _ = self.request.translate
177 c = self.load_default_context()
178 c = self.load_default_context()
178 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.name
179 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.name
179 self._set_personal_repo_group_template_vars(c)
180 self._set_personal_repo_group_template_vars(c)
180 return self._get_template_context(c)
181 return self._get_template_context(c)
181
182
182 @LoginRequired()
183 @LoginRequired()
183 @HasPermissionAllDecorator('hg.admin')
184 @HasPermissionAllDecorator('hg.admin')
184 @CSRFRequired()
185 @CSRFRequired()
185 @view_config(
186 @view_config(
186 route_name='users_create', request_method='POST',
187 route_name='users_create', request_method='POST',
187 renderer='rhodecode:templates/admin/users/user_add.mako')
188 renderer='rhodecode:templates/admin/users/user_add.mako')
188 def users_create(self):
189 def users_create(self):
189 _ = self.request.translate
190 _ = self.request.translate
190 c = self.load_default_context()
191 c = self.load_default_context()
191 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.name
192 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.name
192 user_model = UserModel()
193 user_model = UserModel()
193 user_form = UserForm()()
194 user_form = UserForm()()
194 try:
195 try:
195 form_result = user_form.to_python(dict(self.request.POST))
196 form_result = user_form.to_python(dict(self.request.POST))
196 user = user_model.create(form_result)
197 user = user_model.create(form_result)
197 Session().flush()
198 Session().flush()
198 creation_data = user.get_api_data()
199 creation_data = user.get_api_data()
199 username = form_result['username']
200 username = form_result['username']
200
201
201 audit_logger.store_web(
202 audit_logger.store_web(
202 'user.create', action_data={'data': creation_data},
203 'user.create', action_data={'data': creation_data},
203 user=c.rhodecode_user)
204 user=c.rhodecode_user)
204
205
205 user_link = h.link_to(
206 user_link = h.link_to(
206 h.escape(username),
207 h.escape(username),
207 h.route_path('user_edit', user_id=user.user_id))
208 h.route_path('user_edit', user_id=user.user_id))
208 h.flash(h.literal(_('Created user %(user_link)s')
209 h.flash(h.literal(_('Created user %(user_link)s')
209 % {'user_link': user_link}), category='success')
210 % {'user_link': user_link}), category='success')
210 Session().commit()
211 Session().commit()
211 except formencode.Invalid as errors:
212 except formencode.Invalid as errors:
212 self._set_personal_repo_group_template_vars(c)
213 self._set_personal_repo_group_template_vars(c)
213 data = render(
214 data = render(
214 'rhodecode:templates/admin/users/user_add.mako',
215 'rhodecode:templates/admin/users/user_add.mako',
215 self._get_template_context(c), self.request)
216 self._get_template_context(c), self.request)
216 html = formencode.htmlfill.render(
217 html = formencode.htmlfill.render(
217 data,
218 data,
218 defaults=errors.value,
219 defaults=errors.value,
219 errors=errors.error_dict or {},
220 errors=errors.error_dict or {},
220 prefix_error=False,
221 prefix_error=False,
221 encoding="UTF-8",
222 encoding="UTF-8",
222 force_defaults=False
223 force_defaults=False
223 )
224 )
224 return Response(html)
225 return Response(html)
225 except UserCreationError as e:
226 except UserCreationError as e:
226 h.flash(e, 'error')
227 h.flash(e, 'error')
227 except Exception:
228 except Exception:
228 log.exception("Exception creation of user")
229 log.exception("Exception creation of user")
229 h.flash(_('Error occurred during creation of user %s')
230 h.flash(_('Error occurred during creation of user %s')
230 % self.request.POST.get('username'), category='error')
231 % self.request.POST.get('username'), category='error')
231 raise HTTPFound(h.route_path('users'))
232 raise HTTPFound(h.route_path('users'))
232
233
233
234
234 class UsersView(UserAppView):
235 class UsersView(UserAppView):
235 ALLOW_SCOPED_TOKENS = False
236 ALLOW_SCOPED_TOKENS = False
236 """
237 """
237 This view has alternative version inside EE, if modified please take a look
238 This view has alternative version inside EE, if modified please take a look
238 in there as well.
239 in there as well.
239 """
240 """
240
241
241 def load_default_context(self):
242 def load_default_context(self):
242 c = self._get_local_tmpl_context()
243 c = self._get_local_tmpl_context()
243 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
244 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
244 c.allowed_languages = [
245 c.allowed_languages = [
245 ('en', 'English (en)'),
246 ('en', 'English (en)'),
246 ('de', 'German (de)'),
247 ('de', 'German (de)'),
247 ('fr', 'French (fr)'),
248 ('fr', 'French (fr)'),
248 ('it', 'Italian (it)'),
249 ('it', 'Italian (it)'),
249 ('ja', 'Japanese (ja)'),
250 ('ja', 'Japanese (ja)'),
250 ('pl', 'Polish (pl)'),
251 ('pl', 'Polish (pl)'),
251 ('pt', 'Portuguese (pt)'),
252 ('pt', 'Portuguese (pt)'),
252 ('ru', 'Russian (ru)'),
253 ('ru', 'Russian (ru)'),
253 ('zh', 'Chinese (zh)'),
254 ('zh', 'Chinese (zh)'),
254 ]
255 ]
255 req = self.request
256 req = self.request
256
257
257 c.available_permissions = req.registry.settings['available_permissions']
258 c.available_permissions = req.registry.settings['available_permissions']
258 PermissionModel().set_global_permission_choices(
259 PermissionModel().set_global_permission_choices(
259 c, gettext_translator=req.translate)
260 c, gettext_translator=req.translate)
260
261
261 self._register_global_c(c)
262 self._register_global_c(c)
262 return c
263 return c
263
264
264 @LoginRequired()
265 @LoginRequired()
265 @HasPermissionAllDecorator('hg.admin')
266 @HasPermissionAllDecorator('hg.admin')
266 @CSRFRequired()
267 @CSRFRequired()
267 @view_config(
268 @view_config(
268 route_name='user_update', request_method='POST',
269 route_name='user_update', request_method='POST',
269 renderer='rhodecode:templates/admin/users/user_edit.mako')
270 renderer='rhodecode:templates/admin/users/user_edit.mako')
270 def user_update(self):
271 def user_update(self):
271 _ = self.request.translate
272 _ = self.request.translate
272 c = self.load_default_context()
273 c = self.load_default_context()
273
274
274 user_id = self.db_user_id
275 user_id = self.db_user_id
275 c.user = self.db_user
276 c.user = self.db_user
276
277
277 c.active = 'profile'
278 c.active = 'profile'
278 c.extern_type = c.user.extern_type
279 c.extern_type = c.user.extern_type
279 c.extern_name = c.user.extern_name
280 c.extern_name = c.user.extern_name
280 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
281 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
281 available_languages = [x[0] for x in c.allowed_languages]
282 available_languages = [x[0] for x in c.allowed_languages]
282 _form = UserForm(edit=True, available_languages=available_languages,
283 _form = UserForm(edit=True, available_languages=available_languages,
283 old_data={'user_id': user_id,
284 old_data={'user_id': user_id,
284 'email': c.user.email})()
285 'email': c.user.email})()
285 form_result = {}
286 form_result = {}
286 old_values = c.user.get_api_data()
287 old_values = c.user.get_api_data()
287 try:
288 try:
288 form_result = _form.to_python(dict(self.request.POST))
289 form_result = _form.to_python(dict(self.request.POST))
289 skip_attrs = ['extern_type', 'extern_name']
290 skip_attrs = ['extern_type', 'extern_name']
290 # TODO: plugin should define if username can be updated
291 # TODO: plugin should define if username can be updated
291 if c.extern_type != "rhodecode":
292 if c.extern_type != "rhodecode":
292 # forbid updating username for external accounts
293 # forbid updating username for external accounts
293 skip_attrs.append('username')
294 skip_attrs.append('username')
294
295
295 UserModel().update_user(
296 UserModel().update_user(
296 user_id, skip_attrs=skip_attrs, **form_result)
297 user_id, skip_attrs=skip_attrs, **form_result)
297
298
298 audit_logger.store_web(
299 audit_logger.store_web(
299 'user.edit', action_data={'old_data': old_values},
300 'user.edit', action_data={'old_data': old_values},
300 user=c.rhodecode_user)
301 user=c.rhodecode_user)
301
302
302 Session().commit()
303 Session().commit()
303 h.flash(_('User updated successfully'), category='success')
304 h.flash(_('User updated successfully'), category='success')
304 except formencode.Invalid as errors:
305 except formencode.Invalid as errors:
305 data = render(
306 data = render(
306 'rhodecode:templates/admin/users/user_edit.mako',
307 'rhodecode:templates/admin/users/user_edit.mako',
307 self._get_template_context(c), self.request)
308 self._get_template_context(c), self.request)
308 html = formencode.htmlfill.render(
309 html = formencode.htmlfill.render(
309 data,
310 data,
310 defaults=errors.value,
311 defaults=errors.value,
311 errors=errors.error_dict or {},
312 errors=errors.error_dict or {},
312 prefix_error=False,
313 prefix_error=False,
313 encoding="UTF-8",
314 encoding="UTF-8",
314 force_defaults=False
315 force_defaults=False
315 )
316 )
316 return Response(html)
317 return Response(html)
317 except UserCreationError as e:
318 except UserCreationError as e:
318 h.flash(e, 'error')
319 h.flash(e, 'error')
319 except Exception:
320 except Exception:
320 log.exception("Exception updating user")
321 log.exception("Exception updating user")
321 h.flash(_('Error occurred during update of user %s')
322 h.flash(_('Error occurred during update of user %s')
322 % form_result.get('username'), category='error')
323 % form_result.get('username'), category='error')
323 raise HTTPFound(h.route_path('user_edit', user_id=user_id))
324 raise HTTPFound(h.route_path('user_edit', user_id=user_id))
324
325
325 @LoginRequired()
326 @LoginRequired()
326 @HasPermissionAllDecorator('hg.admin')
327 @HasPermissionAllDecorator('hg.admin')
327 @CSRFRequired()
328 @CSRFRequired()
328 @view_config(
329 @view_config(
329 route_name='user_delete', request_method='POST',
330 route_name='user_delete', request_method='POST',
330 renderer='rhodecode:templates/admin/users/user_edit.mako')
331 renderer='rhodecode:templates/admin/users/user_edit.mako')
331 def user_delete(self):
332 def user_delete(self):
332 _ = self.request.translate
333 _ = self.request.translate
333 c = self.load_default_context()
334 c = self.load_default_context()
334 c.user = self.db_user
335 c.user = self.db_user
335
336
336 _repos = c.user.repositories
337 _repos = c.user.repositories
337 _repo_groups = c.user.repository_groups
338 _repo_groups = c.user.repository_groups
338 _user_groups = c.user.user_groups
339 _user_groups = c.user.user_groups
339
340
340 handle_repos = None
341 handle_repos = None
341 handle_repo_groups = None
342 handle_repo_groups = None
342 handle_user_groups = None
343 handle_user_groups = None
343 # dummy call for flash of handle
344 # dummy call for flash of handle
344 set_handle_flash_repos = lambda: None
345 set_handle_flash_repos = lambda: None
345 set_handle_flash_repo_groups = lambda: None
346 set_handle_flash_repo_groups = lambda: None
346 set_handle_flash_user_groups = lambda: None
347 set_handle_flash_user_groups = lambda: None
347
348
348 if _repos and self.request.POST.get('user_repos'):
349 if _repos and self.request.POST.get('user_repos'):
349 do = self.request.POST['user_repos']
350 do = self.request.POST['user_repos']
350 if do == 'detach':
351 if do == 'detach':
351 handle_repos = 'detach'
352 handle_repos = 'detach'
352 set_handle_flash_repos = lambda: h.flash(
353 set_handle_flash_repos = lambda: h.flash(
353 _('Detached %s repositories') % len(_repos),
354 _('Detached %s repositories') % len(_repos),
354 category='success')
355 category='success')
355 elif do == 'delete':
356 elif do == 'delete':
356 handle_repos = 'delete'
357 handle_repos = 'delete'
357 set_handle_flash_repos = lambda: h.flash(
358 set_handle_flash_repos = lambda: h.flash(
358 _('Deleted %s repositories') % len(_repos),
359 _('Deleted %s repositories') % len(_repos),
359 category='success')
360 category='success')
360
361
361 if _repo_groups and self.request.POST.get('user_repo_groups'):
362 if _repo_groups and self.request.POST.get('user_repo_groups'):
362 do = self.request.POST['user_repo_groups']
363 do = self.request.POST['user_repo_groups']
363 if do == 'detach':
364 if do == 'detach':
364 handle_repo_groups = 'detach'
365 handle_repo_groups = 'detach'
365 set_handle_flash_repo_groups = lambda: h.flash(
366 set_handle_flash_repo_groups = lambda: h.flash(
366 _('Detached %s repository groups') % len(_repo_groups),
367 _('Detached %s repository groups') % len(_repo_groups),
367 category='success')
368 category='success')
368 elif do == 'delete':
369 elif do == 'delete':
369 handle_repo_groups = 'delete'
370 handle_repo_groups = 'delete'
370 set_handle_flash_repo_groups = lambda: h.flash(
371 set_handle_flash_repo_groups = lambda: h.flash(
371 _('Deleted %s repository groups') % len(_repo_groups),
372 _('Deleted %s repository groups') % len(_repo_groups),
372 category='success')
373 category='success')
373
374
374 if _user_groups and self.request.POST.get('user_user_groups'):
375 if _user_groups and self.request.POST.get('user_user_groups'):
375 do = self.request.POST['user_user_groups']
376 do = self.request.POST['user_user_groups']
376 if do == 'detach':
377 if do == 'detach':
377 handle_user_groups = 'detach'
378 handle_user_groups = 'detach'
378 set_handle_flash_user_groups = lambda: h.flash(
379 set_handle_flash_user_groups = lambda: h.flash(
379 _('Detached %s user groups') % len(_user_groups),
380 _('Detached %s user groups') % len(_user_groups),
380 category='success')
381 category='success')
381 elif do == 'delete':
382 elif do == 'delete':
382 handle_user_groups = 'delete'
383 handle_user_groups = 'delete'
383 set_handle_flash_user_groups = lambda: h.flash(
384 set_handle_flash_user_groups = lambda: h.flash(
384 _('Deleted %s user groups') % len(_user_groups),
385 _('Deleted %s user groups') % len(_user_groups),
385 category='success')
386 category='success')
386
387
387 old_values = c.user.get_api_data()
388 old_values = c.user.get_api_data()
388 try:
389 try:
389 UserModel().delete(c.user, handle_repos=handle_repos,
390 UserModel().delete(c.user, handle_repos=handle_repos,
390 handle_repo_groups=handle_repo_groups,
391 handle_repo_groups=handle_repo_groups,
391 handle_user_groups=handle_user_groups)
392 handle_user_groups=handle_user_groups)
392
393
393 audit_logger.store_web(
394 audit_logger.store_web(
394 'user.delete', action_data={'old_data': old_values},
395 'user.delete', action_data={'old_data': old_values},
395 user=c.rhodecode_user)
396 user=c.rhodecode_user)
396
397
397 Session().commit()
398 Session().commit()
398 set_handle_flash_repos()
399 set_handle_flash_repos()
399 set_handle_flash_repo_groups()
400 set_handle_flash_repo_groups()
400 set_handle_flash_user_groups()
401 set_handle_flash_user_groups()
401 h.flash(_('Successfully deleted user'), category='success')
402 h.flash(_('Successfully deleted user'), category='success')
402 except (UserOwnsReposException, UserOwnsRepoGroupsException,
403 except (UserOwnsReposException, UserOwnsRepoGroupsException,
403 UserOwnsUserGroupsException, DefaultUserException) as e:
404 UserOwnsUserGroupsException, DefaultUserException) as e:
404 h.flash(e, category='warning')
405 h.flash(e, category='warning')
405 except Exception:
406 except Exception:
406 log.exception("Exception during deletion of user")
407 log.exception("Exception during deletion of user")
407 h.flash(_('An error occurred during deletion of user'),
408 h.flash(_('An error occurred during deletion of user'),
408 category='error')
409 category='error')
409 raise HTTPFound(h.route_path('users'))
410 raise HTTPFound(h.route_path('users'))
410
411
411 @LoginRequired()
412 @LoginRequired()
412 @HasPermissionAllDecorator('hg.admin')
413 @HasPermissionAllDecorator('hg.admin')
413 @view_config(
414 @view_config(
414 route_name='user_edit', request_method='GET',
415 route_name='user_edit', request_method='GET',
415 renderer='rhodecode:templates/admin/users/user_edit.mako')
416 renderer='rhodecode:templates/admin/users/user_edit.mako')
416 def user_edit(self):
417 def user_edit(self):
417 _ = self.request.translate
418 _ = self.request.translate
418 c = self.load_default_context()
419 c = self.load_default_context()
419 c.user = self.db_user
420 c.user = self.db_user
420
421
421 c.active = 'profile'
422 c.active = 'profile'
422 c.extern_type = c.user.extern_type
423 c.extern_type = c.user.extern_type
423 c.extern_name = c.user.extern_name
424 c.extern_name = c.user.extern_name
424 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
425 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
425
426
426 defaults = c.user.get_dict()
427 defaults = c.user.get_dict()
427 defaults.update({'language': c.user.user_data.get('language')})
428 defaults.update({'language': c.user.user_data.get('language')})
428
429
429 data = render(
430 data = render(
430 'rhodecode:templates/admin/users/user_edit.mako',
431 'rhodecode:templates/admin/users/user_edit.mako',
431 self._get_template_context(c), self.request)
432 self._get_template_context(c), self.request)
432 html = formencode.htmlfill.render(
433 html = formencode.htmlfill.render(
433 data,
434 data,
434 defaults=defaults,
435 defaults=defaults,
435 encoding="UTF-8",
436 encoding="UTF-8",
436 force_defaults=False
437 force_defaults=False
437 )
438 )
438 return Response(html)
439 return Response(html)
439
440
440 @LoginRequired()
441 @LoginRequired()
441 @HasPermissionAllDecorator('hg.admin')
442 @HasPermissionAllDecorator('hg.admin')
442 @view_config(
443 @view_config(
443 route_name='user_edit_advanced', request_method='GET',
444 route_name='user_edit_advanced', request_method='GET',
444 renderer='rhodecode:templates/admin/users/user_edit.mako')
445 renderer='rhodecode:templates/admin/users/user_edit.mako')
445 def user_edit_advanced(self):
446 def user_edit_advanced(self):
446 _ = self.request.translate
447 _ = self.request.translate
447 c = self.load_default_context()
448 c = self.load_default_context()
448
449
449 user_id = self.db_user_id
450 user_id = self.db_user_id
450 c.user = self.db_user
451 c.user = self.db_user
451
452
452 c.active = 'advanced'
453 c.active = 'advanced'
453 c.personal_repo_group = RepoGroup.get_user_personal_repo_group(user_id)
454 c.personal_repo_group = RepoGroup.get_user_personal_repo_group(user_id)
454 c.personal_repo_group_name = RepoGroupModel()\
455 c.personal_repo_group_name = RepoGroupModel()\
455 .get_personal_group_name(c.user)
456 .get_personal_group_name(c.user)
456
457
457 c.user_to_review_rules = sorted(
458 c.user_to_review_rules = sorted(
458 (x.user for x in c.user.user_review_rules),
459 (x.user for x in c.user.user_review_rules),
459 key=lambda u: u.username.lower())
460 key=lambda u: u.username.lower())
460
461
461 c.first_admin = User.get_first_super_admin()
462 c.first_admin = User.get_first_super_admin()
462 defaults = c.user.get_dict()
463 defaults = c.user.get_dict()
463
464
464 # Interim workaround if the user participated on any pull requests as a
465 # Interim workaround if the user participated on any pull requests as a
465 # reviewer.
466 # reviewer.
466 has_review = len(c.user.reviewer_pull_requests)
467 has_review = len(c.user.reviewer_pull_requests)
467 c.can_delete_user = not has_review
468 c.can_delete_user = not has_review
468 c.can_delete_user_message = ''
469 c.can_delete_user_message = ''
469 inactive_link = h.link_to(
470 inactive_link = h.link_to(
470 'inactive', h.route_path('user_edit', user_id=user_id, _anchor='active'))
471 'inactive', h.route_path('user_edit', user_id=user_id, _anchor='active'))
471 if has_review == 1:
472 if has_review == 1:
472 c.can_delete_user_message = h.literal(_(
473 c.can_delete_user_message = h.literal(_(
473 'The user participates as reviewer in {} pull request and '
474 'The user participates as reviewer in {} pull request and '
474 'cannot be deleted. \nYou can set the user to '
475 'cannot be deleted. \nYou can set the user to '
475 '"{}" instead of deleting it.').format(
476 '"{}" instead of deleting it.').format(
476 has_review, inactive_link))
477 has_review, inactive_link))
477 elif has_review:
478 elif has_review:
478 c.can_delete_user_message = h.literal(_(
479 c.can_delete_user_message = h.literal(_(
479 'The user participates as reviewer in {} pull requests and '
480 'The user participates as reviewer in {} pull requests and '
480 'cannot be deleted. \nYou can set the user to '
481 'cannot be deleted. \nYou can set the user to '
481 '"{}" instead of deleting it.').format(
482 '"{}" instead of deleting it.').format(
482 has_review, inactive_link))
483 has_review, inactive_link))
483
484
484 data = render(
485 data = render(
485 'rhodecode:templates/admin/users/user_edit.mako',
486 'rhodecode:templates/admin/users/user_edit.mako',
486 self._get_template_context(c), self.request)
487 self._get_template_context(c), self.request)
487 html = formencode.htmlfill.render(
488 html = formencode.htmlfill.render(
488 data,
489 data,
489 defaults=defaults,
490 defaults=defaults,
490 encoding="UTF-8",
491 encoding="UTF-8",
491 force_defaults=False
492 force_defaults=False
492 )
493 )
493 return Response(html)
494 return Response(html)
494
495
495 @LoginRequired()
496 @LoginRequired()
496 @HasPermissionAllDecorator('hg.admin')
497 @HasPermissionAllDecorator('hg.admin')
497 @view_config(
498 @view_config(
498 route_name='user_edit_global_perms', request_method='GET',
499 route_name='user_edit_global_perms', request_method='GET',
499 renderer='rhodecode:templates/admin/users/user_edit.mako')
500 renderer='rhodecode:templates/admin/users/user_edit.mako')
500 def user_edit_global_perms(self):
501 def user_edit_global_perms(self):
501 _ = self.request.translate
502 _ = self.request.translate
502 c = self.load_default_context()
503 c = self.load_default_context()
503 c.user = self.db_user
504 c.user = self.db_user
504
505
505 c.active = 'global_perms'
506 c.active = 'global_perms'
506
507
507 c.default_user = User.get_default_user()
508 c.default_user = User.get_default_user()
508 defaults = c.user.get_dict()
509 defaults = c.user.get_dict()
509 defaults.update(c.default_user.get_default_perms(suffix='_inherited'))
510 defaults.update(c.default_user.get_default_perms(suffix='_inherited'))
510 defaults.update(c.default_user.get_default_perms())
511 defaults.update(c.default_user.get_default_perms())
511 defaults.update(c.user.get_default_perms())
512 defaults.update(c.user.get_default_perms())
512
513
513 data = render(
514 data = render(
514 'rhodecode:templates/admin/users/user_edit.mako',
515 'rhodecode:templates/admin/users/user_edit.mako',
515 self._get_template_context(c), self.request)
516 self._get_template_context(c), self.request)
516 html = formencode.htmlfill.render(
517 html = formencode.htmlfill.render(
517 data,
518 data,
518 defaults=defaults,
519 defaults=defaults,
519 encoding="UTF-8",
520 encoding="UTF-8",
520 force_defaults=False
521 force_defaults=False
521 )
522 )
522 return Response(html)
523 return Response(html)
523
524
524 @LoginRequired()
525 @LoginRequired()
525 @HasPermissionAllDecorator('hg.admin')
526 @HasPermissionAllDecorator('hg.admin')
526 @CSRFRequired()
527 @CSRFRequired()
527 @view_config(
528 @view_config(
528 route_name='user_edit_global_perms_update', request_method='POST',
529 route_name='user_edit_global_perms_update', request_method='POST',
529 renderer='rhodecode:templates/admin/users/user_edit.mako')
530 renderer='rhodecode:templates/admin/users/user_edit.mako')
530 def user_edit_global_perms_update(self):
531 def user_edit_global_perms_update(self):
531 _ = self.request.translate
532 _ = self.request.translate
532 c = self.load_default_context()
533 c = self.load_default_context()
533
534
534 user_id = self.db_user_id
535 user_id = self.db_user_id
535 c.user = self.db_user
536 c.user = self.db_user
536
537
537 c.active = 'global_perms'
538 c.active = 'global_perms'
538 try:
539 try:
539 # first stage that verifies the checkbox
540 # first stage that verifies the checkbox
540 _form = UserIndividualPermissionsForm()
541 _form = UserIndividualPermissionsForm()
541 form_result = _form.to_python(dict(self.request.POST))
542 form_result = _form.to_python(dict(self.request.POST))
542 inherit_perms = form_result['inherit_default_permissions']
543 inherit_perms = form_result['inherit_default_permissions']
543 c.user.inherit_default_permissions = inherit_perms
544 c.user.inherit_default_permissions = inherit_perms
544 Session().add(c.user)
545 Session().add(c.user)
545
546
546 if not inherit_perms:
547 if not inherit_perms:
547 # only update the individual ones if we un check the flag
548 # only update the individual ones if we un check the flag
548 _form = UserPermissionsForm(
549 _form = UserPermissionsForm(
549 [x[0] for x in c.repo_create_choices],
550 [x[0] for x in c.repo_create_choices],
550 [x[0] for x in c.repo_create_on_write_choices],
551 [x[0] for x in c.repo_create_on_write_choices],
551 [x[0] for x in c.repo_group_create_choices],
552 [x[0] for x in c.repo_group_create_choices],
552 [x[0] for x in c.user_group_create_choices],
553 [x[0] for x in c.user_group_create_choices],
553 [x[0] for x in c.fork_choices],
554 [x[0] for x in c.fork_choices],
554 [x[0] for x in c.inherit_default_permission_choices])()
555 [x[0] for x in c.inherit_default_permission_choices])()
555
556
556 form_result = _form.to_python(dict(self.request.POST))
557 form_result = _form.to_python(dict(self.request.POST))
557 form_result.update({'perm_user_id': c.user.user_id})
558 form_result.update({'perm_user_id': c.user.user_id})
558
559
559 PermissionModel().update_user_permissions(form_result)
560 PermissionModel().update_user_permissions(form_result)
560
561
561 # TODO(marcink): implement global permissions
562 # TODO(marcink): implement global permissions
562 # audit_log.store_web('user.edit.permissions')
563 # audit_log.store_web('user.edit.permissions')
563
564
564 Session().commit()
565 Session().commit()
565 h.flash(_('User global permissions updated successfully'),
566 h.flash(_('User global permissions updated successfully'),
566 category='success')
567 category='success')
567
568
568 except formencode.Invalid as errors:
569 except formencode.Invalid as errors:
569 data = render(
570 data = render(
570 'rhodecode:templates/admin/users/user_edit.mako',
571 'rhodecode:templates/admin/users/user_edit.mako',
571 self._get_template_context(c), self.request)
572 self._get_template_context(c), self.request)
572 html = formencode.htmlfill.render(
573 html = formencode.htmlfill.render(
573 data,
574 data,
574 defaults=errors.value,
575 defaults=errors.value,
575 errors=errors.error_dict or {},
576 errors=errors.error_dict or {},
576 prefix_error=False,
577 prefix_error=False,
577 encoding="UTF-8",
578 encoding="UTF-8",
578 force_defaults=False
579 force_defaults=False
579 )
580 )
580 return Response(html)
581 return Response(html)
581 except Exception:
582 except Exception:
582 log.exception("Exception during permissions saving")
583 log.exception("Exception during permissions saving")
583 h.flash(_('An error occurred during permissions saving'),
584 h.flash(_('An error occurred during permissions saving'),
584 category='error')
585 category='error')
585 raise HTTPFound(h.route_path('user_edit_global_perms', user_id=user_id))
586 raise HTTPFound(h.route_path('user_edit_global_perms', user_id=user_id))
586
587
587 @LoginRequired()
588 @LoginRequired()
588 @HasPermissionAllDecorator('hg.admin')
589 @HasPermissionAllDecorator('hg.admin')
589 @CSRFRequired()
590 @CSRFRequired()
590 @view_config(
591 @view_config(
591 route_name='user_force_password_reset', request_method='POST',
592 route_name='user_force_password_reset', request_method='POST',
592 renderer='rhodecode:templates/admin/users/user_edit.mako')
593 renderer='rhodecode:templates/admin/users/user_edit.mako')
593 def user_force_password_reset(self):
594 def user_force_password_reset(self):
594 """
595 """
595 toggle reset password flag for this user
596 toggle reset password flag for this user
596 """
597 """
597 _ = self.request.translate
598 _ = self.request.translate
598 c = self.load_default_context()
599 c = self.load_default_context()
599
600
600 user_id = self.db_user_id
601 user_id = self.db_user_id
601 c.user = self.db_user
602 c.user = self.db_user
602
603
603 try:
604 try:
604 old_value = c.user.user_data.get('force_password_change')
605 old_value = c.user.user_data.get('force_password_change')
605 c.user.update_userdata(force_password_change=not old_value)
606 c.user.update_userdata(force_password_change=not old_value)
606
607
607 if old_value:
608 if old_value:
608 msg = _('Force password change disabled for user')
609 msg = _('Force password change disabled for user')
609 audit_logger.store_web(
610 audit_logger.store_web(
610 'user.edit.password_reset.disabled',
611 'user.edit.password_reset.disabled',
611 user=c.rhodecode_user)
612 user=c.rhodecode_user)
612 else:
613 else:
613 msg = _('Force password change enabled for user')
614 msg = _('Force password change enabled for user')
614 audit_logger.store_web(
615 audit_logger.store_web(
615 'user.edit.password_reset.enabled',
616 'user.edit.password_reset.enabled',
616 user=c.rhodecode_user)
617 user=c.rhodecode_user)
617
618
618 Session().commit()
619 Session().commit()
619 h.flash(msg, category='success')
620 h.flash(msg, category='success')
620 except Exception:
621 except Exception:
621 log.exception("Exception during password reset for user")
622 log.exception("Exception during password reset for user")
622 h.flash(_('An error occurred during password reset for user'),
623 h.flash(_('An error occurred during password reset for user'),
623 category='error')
624 category='error')
624
625
625 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
626 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
626
627
627 @LoginRequired()
628 @LoginRequired()
628 @HasPermissionAllDecorator('hg.admin')
629 @HasPermissionAllDecorator('hg.admin')
629 @CSRFRequired()
630 @CSRFRequired()
630 @view_config(
631 @view_config(
631 route_name='user_create_personal_repo_group', request_method='POST',
632 route_name='user_create_personal_repo_group', request_method='POST',
632 renderer='rhodecode:templates/admin/users/user_edit.mako')
633 renderer='rhodecode:templates/admin/users/user_edit.mako')
633 def user_create_personal_repo_group(self):
634 def user_create_personal_repo_group(self):
634 """
635 """
635 Create personal repository group for this user
636 Create personal repository group for this user
636 """
637 """
637 from rhodecode.model.repo_group import RepoGroupModel
638 from rhodecode.model.repo_group import RepoGroupModel
638
639
639 _ = self.request.translate
640 _ = self.request.translate
640 c = self.load_default_context()
641 c = self.load_default_context()
641
642
642 user_id = self.db_user_id
643 user_id = self.db_user_id
643 c.user = self.db_user
644 c.user = self.db_user
644
645
645 personal_repo_group = RepoGroup.get_user_personal_repo_group(
646 personal_repo_group = RepoGroup.get_user_personal_repo_group(
646 c.user.user_id)
647 c.user.user_id)
647 if personal_repo_group:
648 if personal_repo_group:
648 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
649 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
649
650
650 personal_repo_group_name = RepoGroupModel().get_personal_group_name(
651 personal_repo_group_name = RepoGroupModel().get_personal_group_name(
651 c.user)
652 c.user)
652 named_personal_group = RepoGroup.get_by_group_name(
653 named_personal_group = RepoGroup.get_by_group_name(
653 personal_repo_group_name)
654 personal_repo_group_name)
654 try:
655 try:
655
656
656 if named_personal_group and named_personal_group.user_id == c.user.user_id:
657 if named_personal_group and named_personal_group.user_id == c.user.user_id:
657 # migrate the same named group, and mark it as personal
658 # migrate the same named group, and mark it as personal
658 named_personal_group.personal = True
659 named_personal_group.personal = True
659 Session().add(named_personal_group)
660 Session().add(named_personal_group)
660 Session().commit()
661 Session().commit()
661 msg = _('Linked repository group `%s` as personal' % (
662 msg = _('Linked repository group `%s` as personal' % (
662 personal_repo_group_name,))
663 personal_repo_group_name,))
663 h.flash(msg, category='success')
664 h.flash(msg, category='success')
664 elif not named_personal_group:
665 elif not named_personal_group:
665 RepoGroupModel().create_personal_repo_group(c.user)
666 RepoGroupModel().create_personal_repo_group(c.user)
666
667
667 msg = _('Created repository group `%s`' % (
668 msg = _('Created repository group `%s`' % (
668 personal_repo_group_name,))
669 personal_repo_group_name,))
669 h.flash(msg, category='success')
670 h.flash(msg, category='success')
670 else:
671 else:
671 msg = _('Repository group `%s` is already taken' % (
672 msg = _('Repository group `%s` is already taken' % (
672 personal_repo_group_name,))
673 personal_repo_group_name,))
673 h.flash(msg, category='warning')
674 h.flash(msg, category='warning')
674 except Exception:
675 except Exception:
675 log.exception("Exception during repository group creation")
676 log.exception("Exception during repository group creation")
676 msg = _(
677 msg = _(
677 'An error occurred during repository group creation for user')
678 'An error occurred during repository group creation for user')
678 h.flash(msg, category='error')
679 h.flash(msg, category='error')
679 Session().rollback()
680 Session().rollback()
680
681
681 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
682 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
682
683
683 @LoginRequired()
684 @LoginRequired()
684 @HasPermissionAllDecorator('hg.admin')
685 @HasPermissionAllDecorator('hg.admin')
685 @view_config(
686 @view_config(
686 route_name='edit_user_auth_tokens', request_method='GET',
687 route_name='edit_user_auth_tokens', request_method='GET',
687 renderer='rhodecode:templates/admin/users/user_edit.mako')
688 renderer='rhodecode:templates/admin/users/user_edit.mako')
688 def auth_tokens(self):
689 def auth_tokens(self):
689 _ = self.request.translate
690 _ = self.request.translate
690 c = self.load_default_context()
691 c = self.load_default_context()
691 c.user = self.db_user
692 c.user = self.db_user
692
693
693 c.active = 'auth_tokens'
694 c.active = 'auth_tokens'
694
695
695 c.lifetime_values = AuthTokenModel.get_lifetime_values(translator=_)
696 c.lifetime_values = AuthTokenModel.get_lifetime_values(translator=_)
696 c.role_values = [
697 c.role_values = [
697 (x, AuthTokenModel.cls._get_role_name(x))
698 (x, AuthTokenModel.cls._get_role_name(x))
698 for x in AuthTokenModel.cls.ROLES]
699 for x in AuthTokenModel.cls.ROLES]
699 c.role_options = [(c.role_values, _("Role"))]
700 c.role_options = [(c.role_values, _("Role"))]
700 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
701 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
701 c.user.user_id, show_expired=True)
702 c.user.user_id, show_expired=True)
702 c.role_vcs = AuthTokenModel.cls.ROLE_VCS
703 c.role_vcs = AuthTokenModel.cls.ROLE_VCS
703 return self._get_template_context(c)
704 return self._get_template_context(c)
704
705
705 def maybe_attach_token_scope(self, token):
706 def maybe_attach_token_scope(self, token):
706 # implemented in EE edition
707 # implemented in EE edition
707 pass
708 pass
708
709
709 @LoginRequired()
710 @LoginRequired()
710 @HasPermissionAllDecorator('hg.admin')
711 @HasPermissionAllDecorator('hg.admin')
711 @CSRFRequired()
712 @CSRFRequired()
712 @view_config(
713 @view_config(
713 route_name='edit_user_auth_tokens_add', request_method='POST')
714 route_name='edit_user_auth_tokens_add', request_method='POST')
714 def auth_tokens_add(self):
715 def auth_tokens_add(self):
715 _ = self.request.translate
716 _ = self.request.translate
716 c = self.load_default_context()
717 c = self.load_default_context()
717
718
718 user_id = self.db_user_id
719 user_id = self.db_user_id
719 c.user = self.db_user
720 c.user = self.db_user
720
721
721 user_data = c.user.get_api_data()
722 user_data = c.user.get_api_data()
722 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
723 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
723 description = self.request.POST.get('description')
724 description = self.request.POST.get('description')
724 role = self.request.POST.get('role')
725 role = self.request.POST.get('role')
725
726
726 token = AuthTokenModel().create(
727 token = AuthTokenModel().create(
727 c.user.user_id, description, lifetime, role)
728 c.user.user_id, description, lifetime, role)
728 token_data = token.get_api_data()
729 token_data = token.get_api_data()
729
730
730 self.maybe_attach_token_scope(token)
731 self.maybe_attach_token_scope(token)
731 audit_logger.store_web(
732 audit_logger.store_web(
732 'user.edit.token.add', action_data={
733 'user.edit.token.add', action_data={
733 'data': {'token': token_data, 'user': user_data}},
734 'data': {'token': token_data, 'user': user_data}},
734 user=self._rhodecode_user, )
735 user=self._rhodecode_user, )
735 Session().commit()
736 Session().commit()
736
737
737 h.flash(_("Auth token successfully created"), category='success')
738 h.flash(_("Auth token successfully created"), category='success')
738 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
739 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
739
740
740 @LoginRequired()
741 @LoginRequired()
741 @HasPermissionAllDecorator('hg.admin')
742 @HasPermissionAllDecorator('hg.admin')
742 @CSRFRequired()
743 @CSRFRequired()
743 @view_config(
744 @view_config(
744 route_name='edit_user_auth_tokens_delete', request_method='POST')
745 route_name='edit_user_auth_tokens_delete', request_method='POST')
745 def auth_tokens_delete(self):
746 def auth_tokens_delete(self):
746 _ = self.request.translate
747 _ = self.request.translate
747 c = self.load_default_context()
748 c = self.load_default_context()
748
749
749 user_id = self.db_user_id
750 user_id = self.db_user_id
750 c.user = self.db_user
751 c.user = self.db_user
751
752
752 user_data = c.user.get_api_data()
753 user_data = c.user.get_api_data()
753
754
754 del_auth_token = self.request.POST.get('del_auth_token')
755 del_auth_token = self.request.POST.get('del_auth_token')
755
756
756 if del_auth_token:
757 if del_auth_token:
757 token = UserApiKeys.get_or_404(del_auth_token)
758 token = UserApiKeys.get_or_404(del_auth_token)
758 token_data = token.get_api_data()
759 token_data = token.get_api_data()
759
760
760 AuthTokenModel().delete(del_auth_token, c.user.user_id)
761 AuthTokenModel().delete(del_auth_token, c.user.user_id)
761 audit_logger.store_web(
762 audit_logger.store_web(
762 'user.edit.token.delete', action_data={
763 'user.edit.token.delete', action_data={
763 'data': {'token': token_data, 'user': user_data}},
764 'data': {'token': token_data, 'user': user_data}},
764 user=self._rhodecode_user,)
765 user=self._rhodecode_user,)
765 Session().commit()
766 Session().commit()
766 h.flash(_("Auth token successfully deleted"), category='success')
767 h.flash(_("Auth token successfully deleted"), category='success')
767
768
768 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
769 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
769
770
770 @LoginRequired()
771 @LoginRequired()
771 @HasPermissionAllDecorator('hg.admin')
772 @HasPermissionAllDecorator('hg.admin')
772 @view_config(
773 @view_config(
773 route_name='edit_user_ssh_keys', request_method='GET',
774 route_name='edit_user_ssh_keys', request_method='GET',
774 renderer='rhodecode:templates/admin/users/user_edit.mako')
775 renderer='rhodecode:templates/admin/users/user_edit.mako')
775 def ssh_keys(self):
776 def ssh_keys(self):
776 _ = self.request.translate
777 _ = self.request.translate
777 c = self.load_default_context()
778 c = self.load_default_context()
778 c.user = self.db_user
779 c.user = self.db_user
779
780
780 c.active = 'ssh_keys'
781 c.active = 'ssh_keys'
781 c.default_key = self.request.GET.get('default_key')
782 c.default_key = self.request.GET.get('default_key')
782 c.user_ssh_keys = SshKeyModel().get_ssh_keys(c.user.user_id)
783 c.user_ssh_keys = SshKeyModel().get_ssh_keys(c.user.user_id)
783 return self._get_template_context(c)
784 return self._get_template_context(c)
784
785
785 @LoginRequired()
786 @LoginRequired()
786 @HasPermissionAllDecorator('hg.admin')
787 @HasPermissionAllDecorator('hg.admin')
787 @view_config(
788 @view_config(
788 route_name='edit_user_ssh_keys_generate_keypair', request_method='GET',
789 route_name='edit_user_ssh_keys_generate_keypair', request_method='GET',
789 renderer='rhodecode:templates/admin/users/user_edit.mako')
790 renderer='rhodecode:templates/admin/users/user_edit.mako')
790 def ssh_keys_generate_keypair(self):
791 def ssh_keys_generate_keypair(self):
791 _ = self.request.translate
792 _ = self.request.translate
792 c = self.load_default_context()
793 c = self.load_default_context()
793
794
794 c.user = self.db_user
795 c.user = self.db_user
795
796
796 c.active = 'ssh_keys_generate'
797 c.active = 'ssh_keys_generate'
797 comment = 'RhodeCode-SSH {}'.format(c.user.email or '')
798 comment = 'RhodeCode-SSH {}'.format(c.user.email or '')
798 c.private, c.public = SshKeyModel().generate_keypair(comment=comment)
799 c.private, c.public = SshKeyModel().generate_keypair(comment=comment)
799
800
800 return self._get_template_context(c)
801 return self._get_template_context(c)
801
802
802 @LoginRequired()
803 @LoginRequired()
803 @HasPermissionAllDecorator('hg.admin')
804 @HasPermissionAllDecorator('hg.admin')
804 @CSRFRequired()
805 @CSRFRequired()
805 @view_config(
806 @view_config(
806 route_name='edit_user_ssh_keys_add', request_method='POST')
807 route_name='edit_user_ssh_keys_add', request_method='POST')
807 def ssh_keys_add(self):
808 def ssh_keys_add(self):
808 _ = self.request.translate
809 _ = self.request.translate
809 c = self.load_default_context()
810 c = self.load_default_context()
810
811
811 user_id = self.db_user_id
812 user_id = self.db_user_id
812 c.user = self.db_user
813 c.user = self.db_user
813
814
814 user_data = c.user.get_api_data()
815 user_data = c.user.get_api_data()
815 key_data = self.request.POST.get('key_data')
816 key_data = self.request.POST.get('key_data')
816 description = self.request.POST.get('description')
817 description = self.request.POST.get('description')
817
818
818 try:
819 try:
819 if not key_data:
820 if not key_data:
820 raise ValueError('Please add a valid public key')
821 raise ValueError('Please add a valid public key')
821
822
822 key = SshKeyModel().parse_key(key_data.strip())
823 key = SshKeyModel().parse_key(key_data.strip())
823 fingerprint = key.hash_md5()
824 fingerprint = key.hash_md5()
824
825
825 ssh_key = SshKeyModel().create(
826 ssh_key = SshKeyModel().create(
826 c.user.user_id, fingerprint, key_data, description)
827 c.user.user_id, fingerprint, key_data, description)
827 ssh_key_data = ssh_key.get_api_data()
828 ssh_key_data = ssh_key.get_api_data()
828
829
829 audit_logger.store_web(
830 audit_logger.store_web(
830 'user.edit.ssh_key.add', action_data={
831 'user.edit.ssh_key.add', action_data={
831 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
832 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
832 user=self._rhodecode_user, )
833 user=self._rhodecode_user, )
833 Session().commit()
834 Session().commit()
834
835
835 # Trigger an event on change of keys.
836 # Trigger an event on change of keys.
836 trigger(SshKeyFileChangeEvent(), self.request.registry)
837 trigger(SshKeyFileChangeEvent(), self.request.registry)
837
838
838 h.flash(_("Ssh Key successfully created"), category='success')
839 h.flash(_("Ssh Key successfully created"), category='success')
839
840
840 except IntegrityError:
841 except IntegrityError:
841 log.exception("Exception during ssh key saving")
842 log.exception("Exception during ssh key saving")
842 h.flash(_('An error occurred during ssh key saving: {}').format(
843 h.flash(_('An error occurred during ssh key saving: {}').format(
843 'Such key already exists, please use a different one'),
844 'Such key already exists, please use a different one'),
844 category='error')
845 category='error')
845 except Exception as e:
846 except Exception as e:
846 log.exception("Exception during ssh key saving")
847 log.exception("Exception during ssh key saving")
847 h.flash(_('An error occurred during ssh key saving: {}').format(e),
848 h.flash(_('An error occurred during ssh key saving: {}').format(e),
848 category='error')
849 category='error')
849
850
850 return HTTPFound(
851 return HTTPFound(
851 h.route_path('edit_user_ssh_keys', user_id=user_id))
852 h.route_path('edit_user_ssh_keys', user_id=user_id))
852
853
853 @LoginRequired()
854 @LoginRequired()
854 @HasPermissionAllDecorator('hg.admin')
855 @HasPermissionAllDecorator('hg.admin')
855 @CSRFRequired()
856 @CSRFRequired()
856 @view_config(
857 @view_config(
857 route_name='edit_user_ssh_keys_delete', request_method='POST')
858 route_name='edit_user_ssh_keys_delete', request_method='POST')
858 def ssh_keys_delete(self):
859 def ssh_keys_delete(self):
859 _ = self.request.translate
860 _ = self.request.translate
860 c = self.load_default_context()
861 c = self.load_default_context()
861
862
862 user_id = self.db_user_id
863 user_id = self.db_user_id
863 c.user = self.db_user
864 c.user = self.db_user
864
865
865 user_data = c.user.get_api_data()
866 user_data = c.user.get_api_data()
866
867
867 del_ssh_key = self.request.POST.get('del_ssh_key')
868 del_ssh_key = self.request.POST.get('del_ssh_key')
868
869
869 if del_ssh_key:
870 if del_ssh_key:
870 ssh_key = UserSshKeys.get_or_404(del_ssh_key)
871 ssh_key = UserSshKeys.get_or_404(del_ssh_key)
871 ssh_key_data = ssh_key.get_api_data()
872 ssh_key_data = ssh_key.get_api_data()
872
873
873 SshKeyModel().delete(del_ssh_key, c.user.user_id)
874 SshKeyModel().delete(del_ssh_key, c.user.user_id)
874 audit_logger.store_web(
875 audit_logger.store_web(
875 'user.edit.ssh_key.delete', action_data={
876 'user.edit.ssh_key.delete', action_data={
876 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
877 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
877 user=self._rhodecode_user,)
878 user=self._rhodecode_user,)
878 Session().commit()
879 Session().commit()
879 # Trigger an event on change of keys.
880 # Trigger an event on change of keys.
880 trigger(SshKeyFileChangeEvent(), self.request.registry)
881 trigger(SshKeyFileChangeEvent(), self.request.registry)
881 h.flash(_("Ssh key successfully deleted"), category='success')
882 h.flash(_("Ssh key successfully deleted"), category='success')
882
883
883 return HTTPFound(h.route_path('edit_user_ssh_keys', user_id=user_id))
884 return HTTPFound(h.route_path('edit_user_ssh_keys', user_id=user_id))
884
885
885 @LoginRequired()
886 @LoginRequired()
886 @HasPermissionAllDecorator('hg.admin')
887 @HasPermissionAllDecorator('hg.admin')
887 @view_config(
888 @view_config(
888 route_name='edit_user_emails', request_method='GET',
889 route_name='edit_user_emails', request_method='GET',
889 renderer='rhodecode:templates/admin/users/user_edit.mako')
890 renderer='rhodecode:templates/admin/users/user_edit.mako')
890 def emails(self):
891 def emails(self):
891 _ = self.request.translate
892 _ = self.request.translate
892 c = self.load_default_context()
893 c = self.load_default_context()
893 c.user = self.db_user
894 c.user = self.db_user
894
895
895 c.active = 'emails'
896 c.active = 'emails'
896 c.user_email_map = UserEmailMap.query() \
897 c.user_email_map = UserEmailMap.query() \
897 .filter(UserEmailMap.user == c.user).all()
898 .filter(UserEmailMap.user == c.user).all()
898
899
899 return self._get_template_context(c)
900 return self._get_template_context(c)
900
901
901 @LoginRequired()
902 @LoginRequired()
902 @HasPermissionAllDecorator('hg.admin')
903 @HasPermissionAllDecorator('hg.admin')
903 @CSRFRequired()
904 @CSRFRequired()
904 @view_config(
905 @view_config(
905 route_name='edit_user_emails_add', request_method='POST')
906 route_name='edit_user_emails_add', request_method='POST')
906 def emails_add(self):
907 def emails_add(self):
907 _ = self.request.translate
908 _ = self.request.translate
908 c = self.load_default_context()
909 c = self.load_default_context()
909
910
910 user_id = self.db_user_id
911 user_id = self.db_user_id
911 c.user = self.db_user
912 c.user = self.db_user
912
913
913 email = self.request.POST.get('new_email')
914 email = self.request.POST.get('new_email')
914 user_data = c.user.get_api_data()
915 user_data = c.user.get_api_data()
915 try:
916 try:
916 UserModel().add_extra_email(c.user.user_id, email)
917 UserModel().add_extra_email(c.user.user_id, email)
917 audit_logger.store_web(
918 audit_logger.store_web(
918 'user.edit.email.add',
919 'user.edit.email.add',
919 action_data={'email': email, 'user': user_data},
920 action_data={'email': email, 'user': user_data},
920 user=self._rhodecode_user)
921 user=self._rhodecode_user)
921 Session().commit()
922 Session().commit()
922 h.flash(_("Added new email address `%s` for user account") % email,
923 h.flash(_("Added new email address `%s` for user account") % email,
923 category='success')
924 category='success')
924 except formencode.Invalid as error:
925 except formencode.Invalid as error:
925 h.flash(h.escape(error.error_dict['email']), category='error')
926 h.flash(h.escape(error.error_dict['email']), category='error')
926 except IntegrityError:
927 except IntegrityError:
927 log.warning("Email %s already exists", email)
928 log.warning("Email %s already exists", email)
928 h.flash(_('Email `{}` is already registered for another user.').format(email),
929 h.flash(_('Email `{}` is already registered for another user.').format(email),
929 category='error')
930 category='error')
930 except Exception:
931 except Exception:
931 log.exception("Exception during email saving")
932 log.exception("Exception during email saving")
932 h.flash(_('An error occurred during email saving'),
933 h.flash(_('An error occurred during email saving'),
933 category='error')
934 category='error')
934 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
935 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
935
936
936 @LoginRequired()
937 @LoginRequired()
937 @HasPermissionAllDecorator('hg.admin')
938 @HasPermissionAllDecorator('hg.admin')
938 @CSRFRequired()
939 @CSRFRequired()
939 @view_config(
940 @view_config(
940 route_name='edit_user_emails_delete', request_method='POST')
941 route_name='edit_user_emails_delete', request_method='POST')
941 def emails_delete(self):
942 def emails_delete(self):
942 _ = self.request.translate
943 _ = self.request.translate
943 c = self.load_default_context()
944 c = self.load_default_context()
944
945
945 user_id = self.db_user_id
946 user_id = self.db_user_id
946 c.user = self.db_user
947 c.user = self.db_user
947
948
948 email_id = self.request.POST.get('del_email_id')
949 email_id = self.request.POST.get('del_email_id')
949 user_model = UserModel()
950 user_model = UserModel()
950
951
951 email = UserEmailMap.query().get(email_id).email
952 email = UserEmailMap.query().get(email_id).email
952 user_data = c.user.get_api_data()
953 user_data = c.user.get_api_data()
953 user_model.delete_extra_email(c.user.user_id, email_id)
954 user_model.delete_extra_email(c.user.user_id, email_id)
954 audit_logger.store_web(
955 audit_logger.store_web(
955 'user.edit.email.delete',
956 'user.edit.email.delete',
956 action_data={'email': email, 'user': user_data},
957 action_data={'email': email, 'user': user_data},
957 user=self._rhodecode_user)
958 user=self._rhodecode_user)
958 Session().commit()
959 Session().commit()
959 h.flash(_("Removed email address from user account"),
960 h.flash(_("Removed email address from user account"),
960 category='success')
961 category='success')
961 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
962 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
962
963
963 @LoginRequired()
964 @LoginRequired()
964 @HasPermissionAllDecorator('hg.admin')
965 @HasPermissionAllDecorator('hg.admin')
965 @view_config(
966 @view_config(
966 route_name='edit_user_ips', request_method='GET',
967 route_name='edit_user_ips', request_method='GET',
967 renderer='rhodecode:templates/admin/users/user_edit.mako')
968 renderer='rhodecode:templates/admin/users/user_edit.mako')
968 def ips(self):
969 def ips(self):
969 _ = self.request.translate
970 _ = self.request.translate
970 c = self.load_default_context()
971 c = self.load_default_context()
971 c.user = self.db_user
972 c.user = self.db_user
972
973
973 c.active = 'ips'
974 c.active = 'ips'
974 c.user_ip_map = UserIpMap.query() \
975 c.user_ip_map = UserIpMap.query() \
975 .filter(UserIpMap.user == c.user).all()
976 .filter(UserIpMap.user == c.user).all()
976
977
977 c.inherit_default_ips = c.user.inherit_default_permissions
978 c.inherit_default_ips = c.user.inherit_default_permissions
978 c.default_user_ip_map = UserIpMap.query() \
979 c.default_user_ip_map = UserIpMap.query() \
979 .filter(UserIpMap.user == User.get_default_user()).all()
980 .filter(UserIpMap.user == User.get_default_user()).all()
980
981
981 return self._get_template_context(c)
982 return self._get_template_context(c)
982
983
983 @LoginRequired()
984 @LoginRequired()
984 @HasPermissionAllDecorator('hg.admin')
985 @HasPermissionAllDecorator('hg.admin')
985 @CSRFRequired()
986 @CSRFRequired()
986 @view_config(
987 @view_config(
987 route_name='edit_user_ips_add', request_method='POST')
988 route_name='edit_user_ips_add', request_method='POST')
988 # NOTE(marcink): this view is allowed for default users, as we can
989 # NOTE(marcink): this view is allowed for default users, as we can
989 # edit their IP white list
990 # edit their IP white list
990 def ips_add(self):
991 def ips_add(self):
991 _ = self.request.translate
992 _ = self.request.translate
992 c = self.load_default_context()
993 c = self.load_default_context()
993
994
994 user_id = self.db_user_id
995 user_id = self.db_user_id
995 c.user = self.db_user
996 c.user = self.db_user
996
997
997 user_model = UserModel()
998 user_model = UserModel()
998 desc = self.request.POST.get('description')
999 desc = self.request.POST.get('description')
999 try:
1000 try:
1000 ip_list = user_model.parse_ip_range(
1001 ip_list = user_model.parse_ip_range(
1001 self.request.POST.get('new_ip'))
1002 self.request.POST.get('new_ip'))
1002 except Exception as e:
1003 except Exception as e:
1003 ip_list = []
1004 ip_list = []
1004 log.exception("Exception during ip saving")
1005 log.exception("Exception during ip saving")
1005 h.flash(_('An error occurred during ip saving:%s' % (e,)),
1006 h.flash(_('An error occurred during ip saving:%s' % (e,)),
1006 category='error')
1007 category='error')
1007 added = []
1008 added = []
1008 user_data = c.user.get_api_data()
1009 user_data = c.user.get_api_data()
1009 for ip in ip_list:
1010 for ip in ip_list:
1010 try:
1011 try:
1011 user_model.add_extra_ip(c.user.user_id, ip, desc)
1012 user_model.add_extra_ip(c.user.user_id, ip, desc)
1012 audit_logger.store_web(
1013 audit_logger.store_web(
1013 'user.edit.ip.add',
1014 'user.edit.ip.add',
1014 action_data={'ip': ip, 'user': user_data},
1015 action_data={'ip': ip, 'user': user_data},
1015 user=self._rhodecode_user)
1016 user=self._rhodecode_user)
1016 Session().commit()
1017 Session().commit()
1017 added.append(ip)
1018 added.append(ip)
1018 except formencode.Invalid as error:
1019 except formencode.Invalid as error:
1019 msg = error.error_dict['ip']
1020 msg = error.error_dict['ip']
1020 h.flash(msg, category='error')
1021 h.flash(msg, category='error')
1021 except Exception:
1022 except Exception:
1022 log.exception("Exception during ip saving")
1023 log.exception("Exception during ip saving")
1023 h.flash(_('An error occurred during ip saving'),
1024 h.flash(_('An error occurred during ip saving'),
1024 category='error')
1025 category='error')
1025 if added:
1026 if added:
1026 h.flash(
1027 h.flash(
1027 _("Added ips %s to user whitelist") % (', '.join(ip_list), ),
1028 _("Added ips %s to user whitelist") % (', '.join(ip_list), ),
1028 category='success')
1029 category='success')
1029 if 'default_user' in self.request.POST:
1030 if 'default_user' in self.request.POST:
1030 # case for editing global IP list we do it for 'DEFAULT' user
1031 # case for editing global IP list we do it for 'DEFAULT' user
1031 raise HTTPFound(h.route_path('admin_permissions_ips'))
1032 raise HTTPFound(h.route_path('admin_permissions_ips'))
1032 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1033 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1033
1034
1034 @LoginRequired()
1035 @LoginRequired()
1035 @HasPermissionAllDecorator('hg.admin')
1036 @HasPermissionAllDecorator('hg.admin')
1036 @CSRFRequired()
1037 @CSRFRequired()
1037 @view_config(
1038 @view_config(
1038 route_name='edit_user_ips_delete', request_method='POST')
1039 route_name='edit_user_ips_delete', request_method='POST')
1039 # NOTE(marcink): this view is allowed for default users, as we can
1040 # NOTE(marcink): this view is allowed for default users, as we can
1040 # edit their IP white list
1041 # edit their IP white list
1041 def ips_delete(self):
1042 def ips_delete(self):
1042 _ = self.request.translate
1043 _ = self.request.translate
1043 c = self.load_default_context()
1044 c = self.load_default_context()
1044
1045
1045 user_id = self.db_user_id
1046 user_id = self.db_user_id
1046 c.user = self.db_user
1047 c.user = self.db_user
1047
1048
1048 ip_id = self.request.POST.get('del_ip_id')
1049 ip_id = self.request.POST.get('del_ip_id')
1049 user_model = UserModel()
1050 user_model = UserModel()
1050 user_data = c.user.get_api_data()
1051 user_data = c.user.get_api_data()
1051 ip = UserIpMap.query().get(ip_id).ip_addr
1052 ip = UserIpMap.query().get(ip_id).ip_addr
1052 user_model.delete_extra_ip(c.user.user_id, ip_id)
1053 user_model.delete_extra_ip(c.user.user_id, ip_id)
1053 audit_logger.store_web(
1054 audit_logger.store_web(
1054 'user.edit.ip.delete', action_data={'ip': ip, 'user': user_data},
1055 'user.edit.ip.delete', action_data={'ip': ip, 'user': user_data},
1055 user=self._rhodecode_user)
1056 user=self._rhodecode_user)
1056 Session().commit()
1057 Session().commit()
1057 h.flash(_("Removed ip address from user whitelist"), category='success')
1058 h.flash(_("Removed ip address from user whitelist"), category='success')
1058
1059
1059 if 'default_user' in self.request.POST:
1060 if 'default_user' in self.request.POST:
1060 # case for editing global IP list we do it for 'DEFAULT' user
1061 # case for editing global IP list we do it for 'DEFAULT' user
1061 raise HTTPFound(h.route_path('admin_permissions_ips'))
1062 raise HTTPFound(h.route_path('admin_permissions_ips'))
1062 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1063 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1063
1064
1064 @LoginRequired()
1065 @LoginRequired()
1065 @HasPermissionAllDecorator('hg.admin')
1066 @HasPermissionAllDecorator('hg.admin')
1066 @view_config(
1067 @view_config(
1067 route_name='edit_user_groups_management', request_method='GET',
1068 route_name='edit_user_groups_management', request_method='GET',
1068 renderer='rhodecode:templates/admin/users/user_edit.mako')
1069 renderer='rhodecode:templates/admin/users/user_edit.mako')
1069 def groups_management(self):
1070 def groups_management(self):
1070 c = self.load_default_context()
1071 c = self.load_default_context()
1071 c.user = self.db_user
1072 c.user = self.db_user
1072 c.data = c.user.group_member
1073 c.data = c.user.group_member
1073
1074
1074 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
1075 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
1075 for group in c.user.group_member]
1076 for group in c.user.group_member]
1076 c.groups = json.dumps(groups)
1077 c.groups = json.dumps(groups)
1077 c.active = 'groups'
1078 c.active = 'groups'
1078
1079
1079 return self._get_template_context(c)
1080 return self._get_template_context(c)
1080
1081
1081 @LoginRequired()
1082 @LoginRequired()
1082 @HasPermissionAllDecorator('hg.admin')
1083 @HasPermissionAllDecorator('hg.admin')
1083 @CSRFRequired()
1084 @CSRFRequired()
1084 @view_config(
1085 @view_config(
1085 route_name='edit_user_groups_management_updates', request_method='POST')
1086 route_name='edit_user_groups_management_updates', request_method='POST')
1086 def groups_management_updates(self):
1087 def groups_management_updates(self):
1087 _ = self.request.translate
1088 _ = self.request.translate
1088 c = self.load_default_context()
1089 c = self.load_default_context()
1089
1090
1090 user_id = self.db_user_id
1091 user_id = self.db_user_id
1091 c.user = self.db_user
1092 c.user = self.db_user
1092
1093
1093 user_groups = set(self.request.POST.getall('users_group_id'))
1094 user_groups = set(self.request.POST.getall('users_group_id'))
1094 user_groups_objects = []
1095 user_groups_objects = []
1095
1096
1096 for ugid in user_groups:
1097 for ugid in user_groups:
1097 user_groups_objects.append(
1098 user_groups_objects.append(
1098 UserGroupModel().get_group(safe_int(ugid)))
1099 UserGroupModel().get_group(safe_int(ugid)))
1099 user_group_model = UserGroupModel()
1100 user_group_model = UserGroupModel()
1100 added_to_groups, removed_from_groups = \
1101 added_to_groups, removed_from_groups = \
1101 user_group_model.change_groups(c.user, user_groups_objects)
1102 user_group_model.change_groups(c.user, user_groups_objects)
1102
1103
1103 user_data = c.user.get_api_data()
1104 user_data = c.user.get_api_data()
1104 for user_group_id in added_to_groups:
1105 for user_group_id in added_to_groups:
1105 user_group = UserGroup.get(user_group_id)
1106 user_group = UserGroup.get(user_group_id)
1106 old_values = user_group.get_api_data()
1107 old_values = user_group.get_api_data()
1107 audit_logger.store_web(
1108 audit_logger.store_web(
1108 'user_group.edit.member.add',
1109 'user_group.edit.member.add',
1109 action_data={'user': user_data, 'old_data': old_values},
1110 action_data={'user': user_data, 'old_data': old_values},
1110 user=self._rhodecode_user)
1111 user=self._rhodecode_user)
1111
1112
1112 for user_group_id in removed_from_groups:
1113 for user_group_id in removed_from_groups:
1113 user_group = UserGroup.get(user_group_id)
1114 user_group = UserGroup.get(user_group_id)
1114 old_values = user_group.get_api_data()
1115 old_values = user_group.get_api_data()
1115 audit_logger.store_web(
1116 audit_logger.store_web(
1116 'user_group.edit.member.delete',
1117 'user_group.edit.member.delete',
1117 action_data={'user': user_data, 'old_data': old_values},
1118 action_data={'user': user_data, 'old_data': old_values},
1118 user=self._rhodecode_user)
1119 user=self._rhodecode_user)
1119
1120
1120 Session().commit()
1121 Session().commit()
1121 c.active = 'user_groups_management'
1122 c.active = 'user_groups_management'
1122 h.flash(_("Groups successfully changed"), category='success')
1123 h.flash(_("Groups successfully changed"), category='success')
1123
1124
1124 return HTTPFound(h.route_path(
1125 return HTTPFound(h.route_path(
1125 'edit_user_groups_management', user_id=user_id))
1126 'edit_user_groups_management', user_id=user_id))
1126
1127
1127 @LoginRequired()
1128 @LoginRequired()
1128 @HasPermissionAllDecorator('hg.admin')
1129 @HasPermissionAllDecorator('hg.admin')
1129 @view_config(
1130 @view_config(
1130 route_name='edit_user_audit_logs', request_method='GET',
1131 route_name='edit_user_audit_logs', request_method='GET',
1131 renderer='rhodecode:templates/admin/users/user_edit.mako')
1132 renderer='rhodecode:templates/admin/users/user_edit.mako')
1132 def user_audit_logs(self):
1133 def user_audit_logs(self):
1133 _ = self.request.translate
1134 _ = self.request.translate
1134 c = self.load_default_context()
1135 c = self.load_default_context()
1135 c.user = self.db_user
1136 c.user = self.db_user
1136
1137
1137 c.active = 'audit'
1138 c.active = 'audit'
1138
1139
1139 p = safe_int(self.request.GET.get('page', 1), 1)
1140 p = safe_int(self.request.GET.get('page', 1), 1)
1140
1141
1141 filter_term = self.request.GET.get('filter')
1142 filter_term = self.request.GET.get('filter')
1142 user_log = UserModel().get_user_log(c.user, filter_term)
1143 user_log = UserModel().get_user_log(c.user, filter_term)
1143
1144
1144 def url_generator(**kw):
1145 def url_generator(**kw):
1145 if filter_term:
1146 if filter_term:
1146 kw['filter'] = filter_term
1147 kw['filter'] = filter_term
1147 return self.request.current_route_path(_query=kw)
1148 return self.request.current_route_path(_query=kw)
1148
1149
1149 c.audit_logs = h.Page(
1150 c.audit_logs = h.Page(
1150 user_log, page=p, items_per_page=10, url=url_generator)
1151 user_log, page=p, items_per_page=10, url=url_generator)
1151 c.filter_term = filter_term
1152 c.filter_term = filter_term
1152 return self._get_template_context(c)
1153 return self._get_template_context(c)
1153
1154
1154 @LoginRequired()
1155 @LoginRequired()
1155 @HasPermissionAllDecorator('hg.admin')
1156 @HasPermissionAllDecorator('hg.admin')
1156 @view_config(
1157 @view_config(
1157 route_name='edit_user_perms_summary', request_method='GET',
1158 route_name='edit_user_perms_summary', request_method='GET',
1158 renderer='rhodecode:templates/admin/users/user_edit.mako')
1159 renderer='rhodecode:templates/admin/users/user_edit.mako')
1159 def user_perms_summary(self):
1160 def user_perms_summary(self):
1160 _ = self.request.translate
1161 _ = self.request.translate
1161 c = self.load_default_context()
1162 c = self.load_default_context()
1162 c.user = self.db_user
1163 c.user = self.db_user
1163
1164
1164 c.active = 'perms_summary'
1165 c.active = 'perms_summary'
1165 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1166 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1166
1167
1167 return self._get_template_context(c)
1168 return self._get_template_context(c)
1168
1169
1169 @LoginRequired()
1170 @LoginRequired()
1170 @HasPermissionAllDecorator('hg.admin')
1171 @HasPermissionAllDecorator('hg.admin')
1171 @view_config(
1172 @view_config(
1172 route_name='edit_user_perms_summary_json', request_method='GET',
1173 route_name='edit_user_perms_summary_json', request_method='GET',
1173 renderer='json_ext')
1174 renderer='json_ext')
1174 def user_perms_summary_json(self):
1175 def user_perms_summary_json(self):
1175 self.load_default_context()
1176 self.load_default_context()
1176 perm_user = self.db_user.AuthUser(ip_addr=self.request.remote_addr)
1177 perm_user = self.db_user.AuthUser(ip_addr=self.request.remote_addr)
1177
1178
1178 return perm_user.permissions
1179 return perm_user.permissions
@@ -1,322 +1,325 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 re
21 import re
22 import logging
22 import logging
23
23
24 from pyramid.view import view_config
24 from pyramid.view import view_config
25
25
26 from rhodecode.apps._base import BaseAppView
26 from rhodecode.apps._base import BaseAppView
27 from rhodecode.lib import helpers as h
27 from rhodecode.lib import helpers as h
28 from rhodecode.lib.auth import (
28 from rhodecode.lib.auth import (
29 LoginRequired, NotAnonymous, HasRepoGroupPermissionAnyDecorator)
29 LoginRequired, NotAnonymous, HasRepoGroupPermissionAnyDecorator)
30 from rhodecode.lib.index import searcher_from_config
30 from rhodecode.lib.index import searcher_from_config
31 from rhodecode.lib.utils2 import safe_unicode, str2bool
31 from rhodecode.lib.utils2 import safe_unicode, str2bool
32 from rhodecode.lib.ext_json import json
32 from rhodecode.lib.ext_json import json
33 from rhodecode.model.db import (
33 from rhodecode.model.db import (
34 func, or_, in_filter_generator, Repository, RepoGroup)
34 func, or_, in_filter_generator, Repository, RepoGroup)
35 from rhodecode.model.repo import RepoModel
35 from rhodecode.model.repo import RepoModel
36 from rhodecode.model.repo_group import RepoGroupModel
36 from rhodecode.model.repo_group import RepoGroupModel
37 from rhodecode.model.scm import RepoGroupList, RepoList
37 from rhodecode.model.scm import RepoGroupList, RepoList
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
40
41 log = logging.getLogger(__name__)
41 log = logging.getLogger(__name__)
42
42
43
43
44 class HomeView(BaseAppView):
44 class HomeView(BaseAppView):
45
45
46 def load_default_context(self):
46 def load_default_context(self):
47 c = self._get_local_tmpl_context()
47 c = self._get_local_tmpl_context()
48 c.user = c.auth_user.get_instance()
48 c.user = c.auth_user.get_instance()
49 self._register_global_c(c)
49 self._register_global_c(c)
50 return c
50 return c
51
51
52 @LoginRequired()
52 @LoginRequired()
53 @view_config(
53 @view_config(
54 route_name='user_autocomplete_data', request_method='GET',
54 route_name='user_autocomplete_data', request_method='GET',
55 renderer='json_ext', xhr=True)
55 renderer='json_ext', xhr=True)
56 def user_autocomplete_data(self):
56 def user_autocomplete_data(self):
57 self.load_default_context()
57 query = self.request.GET.get('query')
58 query = self.request.GET.get('query')
58 active = str2bool(self.request.GET.get('active') or True)
59 active = str2bool(self.request.GET.get('active') or True)
59 include_groups = str2bool(self.request.GET.get('user_groups'))
60 include_groups = str2bool(self.request.GET.get('user_groups'))
60 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
61 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
61 skip_default_user = str2bool(self.request.GET.get('skip_default_user'))
62 skip_default_user = str2bool(self.request.GET.get('skip_default_user'))
62
63
63 log.debug('generating user list, query:%s, active:%s, with_groups:%s',
64 log.debug('generating user list, query:%s, active:%s, with_groups:%s',
64 query, active, include_groups)
65 query, active, include_groups)
65
66
66 _users = UserModel().get_users(
67 _users = UserModel().get_users(
67 name_contains=query, only_active=active)
68 name_contains=query, only_active=active)
68
69
69 def maybe_skip_default_user(usr):
70 def maybe_skip_default_user(usr):
70 if skip_default_user and usr['username'] == UserModel.cls.DEFAULT_USER:
71 if skip_default_user and usr['username'] == UserModel.cls.DEFAULT_USER:
71 return False
72 return False
72 return True
73 return True
73 _users = filter(maybe_skip_default_user, _users)
74 _users = filter(maybe_skip_default_user, _users)
74
75
75 if include_groups:
76 if include_groups:
76 # extend with user groups
77 # extend with user groups
77 _user_groups = UserGroupModel().get_user_groups(
78 _user_groups = UserGroupModel().get_user_groups(
78 name_contains=query, only_active=active,
79 name_contains=query, only_active=active,
79 expand_groups=expand_groups)
80 expand_groups=expand_groups)
80 _users = _users + _user_groups
81 _users = _users + _user_groups
81
82
82 return {'suggestions': _users}
83 return {'suggestions': _users}
83
84
84 @LoginRequired()
85 @LoginRequired()
85 @NotAnonymous()
86 @NotAnonymous()
86 @view_config(
87 @view_config(
87 route_name='user_group_autocomplete_data', request_method='GET',
88 route_name='user_group_autocomplete_data', request_method='GET',
88 renderer='json_ext', xhr=True)
89 renderer='json_ext', xhr=True)
89 def user_group_autocomplete_data(self):
90 def user_group_autocomplete_data(self):
91 self.load_default_context()
90 query = self.request.GET.get('query')
92 query = self.request.GET.get('query')
91 active = str2bool(self.request.GET.get('active') or True)
93 active = str2bool(self.request.GET.get('active') or True)
92 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
94 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
93
95
94 log.debug('generating user group list, query:%s, active:%s',
96 log.debug('generating user group list, query:%s, active:%s',
95 query, active)
97 query, active)
96
98
97 _user_groups = UserGroupModel().get_user_groups(
99 _user_groups = UserGroupModel().get_user_groups(
98 name_contains=query, only_active=active,
100 name_contains=query, only_active=active,
99 expand_groups=expand_groups)
101 expand_groups=expand_groups)
100 _user_groups = _user_groups
102 _user_groups = _user_groups
101
103
102 return {'suggestions': _user_groups}
104 return {'suggestions': _user_groups}
103
105
104 def _get_repo_list(self, name_contains=None, repo_type=None, limit=20):
106 def _get_repo_list(self, name_contains=None, repo_type=None, limit=20):
105 allowed_ids = self._rhodecode_user.repo_acl_ids(
107 allowed_ids = self._rhodecode_user.repo_acl_ids(
106 ['repository.read', 'repository.write', 'repository.admin'],
108 ['repository.read', 'repository.write', 'repository.admin'],
107 cache=False, name_filter=name_contains) or [-1]
109 cache=False, name_filter=name_contains) or [-1]
108
110
109 query = Repository.query()\
111 query = Repository.query()\
110 .order_by(func.length(Repository.repo_name))\
112 .order_by(func.length(Repository.repo_name))\
111 .order_by(Repository.repo_name)\
113 .order_by(Repository.repo_name)\
112 .filter(or_(
114 .filter(or_(
113 # generate multiple IN to fix limitation problems
115 # generate multiple IN to fix limitation problems
114 *in_filter_generator(Repository.repo_id, allowed_ids)
116 *in_filter_generator(Repository.repo_id, allowed_ids)
115 ))
117 ))
116
118
117 if repo_type:
119 if repo_type:
118 query = query.filter(Repository.repo_type == repo_type)
120 query = query.filter(Repository.repo_type == repo_type)
119
121
120 if name_contains:
122 if name_contains:
121 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
123 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
122 query = query.filter(
124 query = query.filter(
123 Repository.repo_name.ilike(ilike_expression))
125 Repository.repo_name.ilike(ilike_expression))
124 query = query.limit(limit)
126 query = query.limit(limit)
125
127
126 acl_repo_iter = query
128 acl_repo_iter = query
127
129
128 return [
130 return [
129 {
131 {
130 'id': obj.repo_name,
132 'id': obj.repo_name,
131 'text': obj.repo_name,
133 'text': obj.repo_name,
132 'type': 'repo',
134 'type': 'repo',
133 'obj': {'repo_type': obj.repo_type, 'private': obj.private,
135 'obj': {'repo_type': obj.repo_type, 'private': obj.private,
134 'repo_id': obj.repo_id},
136 'repo_id': obj.repo_id},
135 'url': h.route_path('repo_summary', repo_name=obj.repo_name)
137 'url': h.route_path('repo_summary', repo_name=obj.repo_name)
136 }
138 }
137 for obj in acl_repo_iter]
139 for obj in acl_repo_iter]
138
140
139 def _get_repo_group_list(self, name_contains=None, limit=20):
141 def _get_repo_group_list(self, name_contains=None, limit=20):
140 allowed_ids = self._rhodecode_user.repo_group_acl_ids(
142 allowed_ids = self._rhodecode_user.repo_group_acl_ids(
141 ['group.read', 'group.write', 'group.admin'],
143 ['group.read', 'group.write', 'group.admin'],
142 cache=False, name_filter=name_contains) or [-1]
144 cache=False, name_filter=name_contains) or [-1]
143
145
144 query = RepoGroup.query()\
146 query = RepoGroup.query()\
145 .order_by(func.length(RepoGroup.group_name))\
147 .order_by(func.length(RepoGroup.group_name))\
146 .order_by(RepoGroup.group_name) \
148 .order_by(RepoGroup.group_name) \
147 .filter(or_(
149 .filter(or_(
148 # generate multiple IN to fix limitation problems
150 # generate multiple IN to fix limitation problems
149 *in_filter_generator(RepoGroup.group_id, allowed_ids)
151 *in_filter_generator(RepoGroup.group_id, allowed_ids)
150 ))
152 ))
151
153
152 if name_contains:
154 if name_contains:
153 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
155 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
154 query = query.filter(
156 query = query.filter(
155 RepoGroup.group_name.ilike(ilike_expression))
157 RepoGroup.group_name.ilike(ilike_expression))
156 query = query.limit(limit)
158 query = query.limit(limit)
157
159
158 acl_repo_iter = query
160 acl_repo_iter = query
159
161
160 return [
162 return [
161 {
163 {
162 'id': obj.group_name,
164 'id': obj.group_name,
163 'text': obj.group_name,
165 'text': obj.group_name,
164 'type': 'group',
166 'type': 'group',
165 'obj': {},
167 'obj': {},
166 'url': h.route_path(
168 'url': h.route_path(
167 'repo_group_home', repo_group_name=obj.group_name)
169 'repo_group_home', repo_group_name=obj.group_name)
168 }
170 }
169 for obj in acl_repo_iter]
171 for obj in acl_repo_iter]
170
172
171 def _get_hash_commit_list(self, auth_user, query=None):
173 def _get_hash_commit_list(self, auth_user, query=None):
172 if not query or len(query) < 3:
174 if not query or len(query) < 3:
173 return []
175 return []
174
176
175 commit_hashes = re.compile('(?:commit:)([0-9a-f]{2,40})').findall(query)
177 commit_hashes = re.compile('(?:commit:)([0-9a-f]{2,40})').findall(query)
176
178
177 if len(commit_hashes) != 1:
179 if len(commit_hashes) != 1:
178 return []
180 return []
179
181
180 commit_hash_prefix = commit_hashes[0]
182 commit_hash_prefix = commit_hashes[0]
181
183
182 searcher = searcher_from_config(self.request.registry.settings)
184 searcher = searcher_from_config(self.request.registry.settings)
183 result = searcher.search(
185 result = searcher.search(
184 'commit_id:%s*' % commit_hash_prefix, 'commit', auth_user,
186 'commit_id:%s*' % commit_hash_prefix, 'commit', auth_user,
185 raise_on_exc=False)
187 raise_on_exc=False)
186
188
187 return [
189 return [
188 {
190 {
189 'id': entry['commit_id'],
191 'id': entry['commit_id'],
190 'text': entry['commit_id'],
192 'text': entry['commit_id'],
191 'type': 'commit',
193 'type': 'commit',
192 'obj': {'repo': entry['repository']},
194 'obj': {'repo': entry['repository']},
193 'url': h.route_path(
195 'url': h.route_path(
194 'repo_commit',
196 'repo_commit',
195 repo_name=entry['repository'], commit_id=entry['commit_id'])
197 repo_name=entry['repository'], commit_id=entry['commit_id'])
196 }
198 }
197 for entry in result['results']]
199 for entry in result['results']]
198
200
199 @LoginRequired()
201 @LoginRequired()
200 @view_config(
202 @view_config(
201 route_name='repo_list_data', request_method='GET',
203 route_name='repo_list_data', request_method='GET',
202 renderer='json_ext', xhr=True)
204 renderer='json_ext', xhr=True)
203 def repo_list_data(self):
205 def repo_list_data(self):
204 _ = self.request.translate
206 _ = self.request.translate
207 self.load_default_context()
205
208
206 query = self.request.GET.get('query')
209 query = self.request.GET.get('query')
207 repo_type = self.request.GET.get('repo_type')
210 repo_type = self.request.GET.get('repo_type')
208 log.debug('generating repo list, query:%s, repo_type:%s',
211 log.debug('generating repo list, query:%s, repo_type:%s',
209 query, repo_type)
212 query, repo_type)
210
213
211 res = []
214 res = []
212 repos = self._get_repo_list(query, repo_type=repo_type)
215 repos = self._get_repo_list(query, repo_type=repo_type)
213 if repos:
216 if repos:
214 res.append({
217 res.append({
215 'text': _('Repositories'),
218 'text': _('Repositories'),
216 'children': repos
219 'children': repos
217 })
220 })
218
221
219 data = {
222 data = {
220 'more': False,
223 'more': False,
221 'results': res
224 'results': res
222 }
225 }
223 return data
226 return data
224
227
225 @LoginRequired()
228 @LoginRequired()
226 @view_config(
229 @view_config(
227 route_name='goto_switcher_data', request_method='GET',
230 route_name='goto_switcher_data', request_method='GET',
228 renderer='json_ext', xhr=True)
231 renderer='json_ext', xhr=True)
229 def goto_switcher_data(self):
232 def goto_switcher_data(self):
230 c = self.load_default_context()
233 c = self.load_default_context()
231
234
232 _ = self.request.translate
235 _ = self.request.translate
233
236
234 query = self.request.GET.get('query')
237 query = self.request.GET.get('query')
235 log.debug('generating goto switcher list, query %s', query)
238 log.debug('generating goto switcher list, query %s', query)
236
239
237 res = []
240 res = []
238 repo_groups = self._get_repo_group_list(query)
241 repo_groups = self._get_repo_group_list(query)
239 if repo_groups:
242 if repo_groups:
240 res.append({
243 res.append({
241 'text': _('Groups'),
244 'text': _('Groups'),
242 'children': repo_groups
245 'children': repo_groups
243 })
246 })
244
247
245 repos = self._get_repo_list(query)
248 repos = self._get_repo_list(query)
246 if repos:
249 if repos:
247 res.append({
250 res.append({
248 'text': _('Repositories'),
251 'text': _('Repositories'),
249 'children': repos
252 'children': repos
250 })
253 })
251
254
252 commits = self._get_hash_commit_list(c.auth_user, query)
255 commits = self._get_hash_commit_list(c.auth_user, query)
253 if commits:
256 if commits:
254 unique_repos = {}
257 unique_repos = {}
255 for commit in commits:
258 for commit in commits:
256 unique_repos.setdefault(commit['obj']['repo'], []
259 unique_repos.setdefault(commit['obj']['repo'], []
257 ).append(commit)
260 ).append(commit)
258
261
259 for repo in unique_repos:
262 for repo in unique_repos:
260 res.append({
263 res.append({
261 'text': _('Commits in %(repo)s') % {'repo': repo},
264 'text': _('Commits in %(repo)s') % {'repo': repo},
262 'children': unique_repos[repo]
265 'children': unique_repos[repo]
263 })
266 })
264
267
265 data = {
268 data = {
266 'more': False,
269 'more': False,
267 'results': res
270 'results': res
268 }
271 }
269 return data
272 return data
270
273
271 def _get_groups_and_repos(self, repo_group_id=None):
274 def _get_groups_and_repos(self, repo_group_id=None):
272 # repo groups groups
275 # repo groups groups
273 repo_group_list = RepoGroup.get_all_repo_groups(group_id=repo_group_id)
276 repo_group_list = RepoGroup.get_all_repo_groups(group_id=repo_group_id)
274 _perms = ['group.read', 'group.write', 'group.admin']
277 _perms = ['group.read', 'group.write', 'group.admin']
275 repo_group_list_acl = RepoGroupList(repo_group_list, perm_set=_perms)
278 repo_group_list_acl = RepoGroupList(repo_group_list, perm_set=_perms)
276 repo_group_data = RepoGroupModel().get_repo_groups_as_dict(
279 repo_group_data = RepoGroupModel().get_repo_groups_as_dict(
277 repo_group_list=repo_group_list_acl, admin=False)
280 repo_group_list=repo_group_list_acl, admin=False)
278
281
279 # repositories
282 # repositories
280 repo_list = Repository.get_all_repos(group_id=repo_group_id)
283 repo_list = Repository.get_all_repos(group_id=repo_group_id)
281 _perms = ['repository.read', 'repository.write', 'repository.admin']
284 _perms = ['repository.read', 'repository.write', 'repository.admin']
282 repo_list_acl = RepoList(repo_list, perm_set=_perms)
285 repo_list_acl = RepoList(repo_list, perm_set=_perms)
283 repo_data = RepoModel().get_repos_as_dict(
286 repo_data = RepoModel().get_repos_as_dict(
284 repo_list=repo_list_acl, admin=False)
287 repo_list=repo_list_acl, admin=False)
285
288
286 return repo_data, repo_group_data
289 return repo_data, repo_group_data
287
290
288 @LoginRequired()
291 @LoginRequired()
289 @view_config(
292 @view_config(
290 route_name='home', request_method='GET',
293 route_name='home', request_method='GET',
291 renderer='rhodecode:templates/index.mako')
294 renderer='rhodecode:templates/index.mako')
292 def main_page(self):
295 def main_page(self):
293 c = self.load_default_context()
296 c = self.load_default_context()
294 c.repo_group = None
297 c.repo_group = None
295
298
296 repo_data, repo_group_data = self._get_groups_and_repos()
299 repo_data, repo_group_data = self._get_groups_and_repos()
297 # json used to render the grids
300 # json used to render the grids
298 c.repos_data = json.dumps(repo_data)
301 c.repos_data = json.dumps(repo_data)
299 c.repo_groups_data = json.dumps(repo_group_data)
302 c.repo_groups_data = json.dumps(repo_group_data)
300
303
301 return self._get_template_context(c)
304 return self._get_template_context(c)
302
305
303 @LoginRequired()
306 @LoginRequired()
304 @HasRepoGroupPermissionAnyDecorator(
307 @HasRepoGroupPermissionAnyDecorator(
305 'group.read', 'group.write', 'group.admin')
308 'group.read', 'group.write', 'group.admin')
306 @view_config(
309 @view_config(
307 route_name='repo_group_home', request_method='GET',
310 route_name='repo_group_home', request_method='GET',
308 renderer='rhodecode:templates/index_repo_group.mako')
311 renderer='rhodecode:templates/index_repo_group.mako')
309 @view_config(
312 @view_config(
310 route_name='repo_group_home_slash', request_method='GET',
313 route_name='repo_group_home_slash', request_method='GET',
311 renderer='rhodecode:templates/index_repo_group.mako')
314 renderer='rhodecode:templates/index_repo_group.mako')
312 def repo_group_main_page(self):
315 def repo_group_main_page(self):
313 c = self.load_default_context()
316 c = self.load_default_context()
314 c.repo_group = self.request.db_repo_group
317 c.repo_group = self.request.db_repo_group
315 repo_data, repo_group_data = self._get_groups_and_repos(
318 repo_data, repo_group_data = self._get_groups_and_repos(
316 c.repo_group.group_id)
319 c.repo_group.group_id)
317
320
318 # json used to render the grids
321 # json used to render the grids
319 c.repos_data = json.dumps(repo_data)
322 c.repos_data = json.dumps(repo_data)
320 c.repo_groups_data = json.dumps(repo_group_data)
323 c.repo_groups_data = json.dumps(repo_group_data)
321
324
322 return self._get_template_context(c)
325 return self._get_template_context(c)
General Comments 0
You need to be logged in to leave comments. Login now