##// END OF EJS Templates
grids: columns and sorting fixes
marcink -
r4150:554d4203 default
parent child Browse files
Show More
@@ -1,361 +1,360 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2019 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 import datetime
20 import datetime
21 import logging
21 import logging
22 import time
22 import time
23
23
24 import formencode
24 import formencode
25 import formencode.htmlfill
25 import formencode.htmlfill
26
26
27 from pyramid.httpexceptions import HTTPFound, HTTPForbidden
27 from pyramid.httpexceptions import HTTPFound, HTTPForbidden
28 from pyramid.view import view_config
28 from pyramid.view import view_config
29 from pyramid.renderers import render
29 from pyramid.renderers import render
30 from pyramid.response import Response
30 from pyramid.response import Response
31
31
32 from rhodecode import events
32 from rhodecode import events
33 from rhodecode.apps._base import BaseAppView, DataGridAppView
33 from rhodecode.apps._base import BaseAppView, DataGridAppView
34
34
35 from rhodecode.lib.auth import (
35 from rhodecode.lib.auth import (
36 LoginRequired, CSRFRequired, NotAnonymous,
36 LoginRequired, CSRFRequired, NotAnonymous,
37 HasPermissionAny, HasRepoGroupPermissionAny)
37 HasPermissionAny, HasRepoGroupPermissionAny)
38 from rhodecode.lib import helpers as h, audit_logger
38 from rhodecode.lib import helpers as h, audit_logger
39 from rhodecode.lib.utils2 import safe_int, safe_unicode, datetime_to_time
39 from rhodecode.lib.utils2 import safe_int, safe_unicode, datetime_to_time
40 from rhodecode.model.forms import RepoGroupForm
40 from rhodecode.model.forms import RepoGroupForm
41 from rhodecode.model.permission import PermissionModel
41 from rhodecode.model.permission import PermissionModel
42 from rhodecode.model.repo_group import RepoGroupModel
42 from rhodecode.model.repo_group import RepoGroupModel
43 from rhodecode.model.scm import RepoGroupList
43 from rhodecode.model.scm import RepoGroupList
44 from rhodecode.model.db import (
44 from rhodecode.model.db import (
45 or_, count, func, in_filter_generator, Session, RepoGroup, User, Repository)
45 or_, count, func, in_filter_generator, Session, RepoGroup, User, Repository)
46
46
47 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
48
48
49
49
50 class AdminRepoGroupsView(BaseAppView, DataGridAppView):
50 class AdminRepoGroupsView(BaseAppView, DataGridAppView):
51
51
52 def load_default_context(self):
52 def load_default_context(self):
53 c = self._get_local_tmpl_context()
53 c = self._get_local_tmpl_context()
54
54
55 return c
55 return c
56
56
57 def _load_form_data(self, c):
57 def _load_form_data(self, c):
58 allow_empty_group = False
58 allow_empty_group = False
59
59
60 if self._can_create_repo_group():
60 if self._can_create_repo_group():
61 # we're global admin, we're ok and we can create TOP level groups
61 # we're global admin, we're ok and we can create TOP level groups
62 allow_empty_group = True
62 allow_empty_group = True
63
63
64 # override the choices for this form, we need to filter choices
64 # override the choices for this form, we need to filter choices
65 # and display only those we have ADMIN right
65 # and display only those we have ADMIN right
66 groups_with_admin_rights = RepoGroupList(
66 groups_with_admin_rights = RepoGroupList(
67 RepoGroup.query().all(),
67 RepoGroup.query().all(),
68 perm_set=['group.admin'], extra_kwargs=dict(user=self._rhodecode_user))
68 perm_set=['group.admin'], extra_kwargs=dict(user=self._rhodecode_user))
69 c.repo_groups = RepoGroup.groups_choices(
69 c.repo_groups = RepoGroup.groups_choices(
70 groups=groups_with_admin_rights,
70 groups=groups_with_admin_rights,
71 show_empty_group=allow_empty_group)
71 show_empty_group=allow_empty_group)
72
72
73 def _can_create_repo_group(self, parent_group_id=None):
73 def _can_create_repo_group(self, parent_group_id=None):
74 is_admin = HasPermissionAny('hg.admin')('group create controller')
74 is_admin = HasPermissionAny('hg.admin')('group create controller')
75 create_repo_group = HasPermissionAny(
75 create_repo_group = HasPermissionAny(
76 'hg.repogroup.create.true')('group create controller')
76 'hg.repogroup.create.true')('group create controller')
77 if is_admin or (create_repo_group and not parent_group_id):
77 if is_admin or (create_repo_group and not parent_group_id):
78 # we're global admin, or we have global repo group create
78 # we're global admin, or we have global repo group create
79 # permission
79 # permission
80 # we're ok and we can create TOP level groups
80 # we're ok and we can create TOP level groups
81 return True
81 return True
82 elif parent_group_id:
82 elif parent_group_id:
83 # we check the permission if we can write to parent group
83 # we check the permission if we can write to parent group
84 group = RepoGroup.get(parent_group_id)
84 group = RepoGroup.get(parent_group_id)
85 group_name = group.group_name if group else None
85 group_name = group.group_name if group else None
86 if HasRepoGroupPermissionAny('group.admin')(
86 if HasRepoGroupPermissionAny('group.admin')(
87 group_name, 'check if user is an admin of group'):
87 group_name, 'check if user is an admin of group'):
88 # we're an admin of passed in group, we're ok.
88 # we're an admin of passed in group, we're ok.
89 return True
89 return True
90 else:
90 else:
91 return False
91 return False
92 return False
92 return False
93
93
94 # permission check in data loading of
94 # permission check in data loading of
95 # `repo_group_list_data` via RepoGroupList
95 # `repo_group_list_data` via RepoGroupList
96 @LoginRequired()
96 @LoginRequired()
97 @NotAnonymous()
97 @NotAnonymous()
98 @view_config(
98 @view_config(
99 route_name='repo_groups', request_method='GET',
99 route_name='repo_groups', request_method='GET',
100 renderer='rhodecode:templates/admin/repo_groups/repo_groups.mako')
100 renderer='rhodecode:templates/admin/repo_groups/repo_groups.mako')
101 def repo_group_list(self):
101 def repo_group_list(self):
102 c = self.load_default_context()
102 c = self.load_default_context()
103 return self._get_template_context(c)
103 return self._get_template_context(c)
104
104
105 # permission check inside
105 # permission check inside
106 @LoginRequired()
106 @LoginRequired()
107 @NotAnonymous()
107 @NotAnonymous()
108 @view_config(
108 @view_config(
109 route_name='repo_groups_data', request_method='GET',
109 route_name='repo_groups_data', request_method='GET',
110 renderer='json_ext', xhr=True)
110 renderer='json_ext', xhr=True)
111 def repo_group_list_data(self):
111 def repo_group_list_data(self):
112 self.load_default_context()
112 self.load_default_context()
113 column_map = {
113 column_map = {
114 'name_raw': 'group_name_hash',
114 'name': 'group_name_hash',
115 'desc': 'group_description',
115 'desc': 'group_description',
116 'last_change_raw': 'updated_on',
116 'last_change': 'updated_on',
117 'top_level_repos': 'repos_total',
117 'top_level_repos': 'repos_total',
118 'owner': 'user_username',
118 'owner': 'user_username',
119 }
119 }
120 draw, start, limit = self._extract_chunk(self.request)
120 draw, start, limit = self._extract_chunk(self.request)
121 search_q, order_by, order_dir = self._extract_ordering(
121 search_q, order_by, order_dir = self._extract_ordering(
122 self.request, column_map=column_map)
122 self.request, column_map=column_map)
123
123
124 _render = self.request.get_partial_renderer(
124 _render = self.request.get_partial_renderer(
125 'rhodecode:templates/data_table/_dt_elements.mako')
125 'rhodecode:templates/data_table/_dt_elements.mako')
126 c = _render.get_call_context()
126 c = _render.get_call_context()
127
127
128 def quick_menu(repo_group_name):
128 def quick_menu(repo_group_name):
129 return _render('quick_repo_group_menu', repo_group_name)
129 return _render('quick_repo_group_menu', repo_group_name)
130
130
131 def repo_group_lnk(repo_group_name):
131 def repo_group_lnk(repo_group_name):
132 return _render('repo_group_name', repo_group_name)
132 return _render('repo_group_name', repo_group_name)
133
133
134 def last_change(last_change):
134 def last_change(last_change):
135 if isinstance(last_change, datetime.datetime) and not last_change.tzinfo:
135 if isinstance(last_change, datetime.datetime) and not last_change.tzinfo:
136 ts = time.time()
136 ts = time.time()
137 utc_offset = (datetime.datetime.fromtimestamp(ts)
137 utc_offset = (datetime.datetime.fromtimestamp(ts)
138 - datetime.datetime.utcfromtimestamp(ts)).total_seconds()
138 - datetime.datetime.utcfromtimestamp(ts)).total_seconds()
139 last_change = last_change + datetime.timedelta(seconds=utc_offset)
139 last_change = last_change + datetime.timedelta(seconds=utc_offset)
140 return _render("last_change", last_change)
140 return _render("last_change", last_change)
141
141
142 def desc(desc, personal):
142 def desc(desc, personal):
143 return _render(
143 return _render(
144 'repo_group_desc', desc, personal, c.visual.stylify_metatags)
144 'repo_group_desc', desc, personal, c.visual.stylify_metatags)
145
145
146 def repo_group_actions(repo_group_id, repo_group_name, gr_count):
146 def repo_group_actions(repo_group_id, repo_group_name, gr_count):
147 return _render(
147 return _render(
148 'repo_group_actions', repo_group_id, repo_group_name, gr_count)
148 'repo_group_actions', repo_group_id, repo_group_name, gr_count)
149
149
150 def user_profile(username):
150 def user_profile(username):
151 return _render('user_profile', username)
151 return _render('user_profile', username)
152
152
153 _perms = ['group.admin']
153 _perms = ['group.admin']
154 allowed_ids = [-1] + self._rhodecode_user.repo_group_acl_ids_from_stack(_perms)
154 allowed_ids = [-1] + self._rhodecode_user.repo_group_acl_ids_from_stack(_perms)
155
155
156 repo_groups_data_total_count = RepoGroup.query()\
156 repo_groups_data_total_count = RepoGroup.query()\
157 .filter(or_(
157 .filter(or_(
158 # generate multiple IN to fix limitation problems
158 # generate multiple IN to fix limitation problems
159 *in_filter_generator(RepoGroup.group_id, allowed_ids)
159 *in_filter_generator(RepoGroup.group_id, allowed_ids)
160 )) \
160 )) \
161 .count()
161 .count()
162
162
163 repo_groups_data_total_inactive_count = RepoGroup.query()\
163 repo_groups_data_total_inactive_count = RepoGroup.query()\
164 .filter(RepoGroup.group_id.in_(allowed_ids))\
164 .filter(RepoGroup.group_id.in_(allowed_ids))\
165 .count()
165 .count()
166
166
167 repo_count = count(Repository.repo_id)
167 repo_count = count(Repository.repo_id)
168 base_q = Session.query(
168 base_q = Session.query(
169 RepoGroup.group_name,
169 RepoGroup.group_name,
170 RepoGroup.group_name_hash,
170 RepoGroup.group_name_hash,
171 RepoGroup.group_description,
171 RepoGroup.group_description,
172 RepoGroup.group_id,
172 RepoGroup.group_id,
173 RepoGroup.personal,
173 RepoGroup.personal,
174 RepoGroup.updated_on,
174 RepoGroup.updated_on,
175 User,
175 User,
176 repo_count.label('repos_count')
176 repo_count.label('repos_count')
177 ) \
177 ) \
178 .filter(or_(
178 .filter(or_(
179 # generate multiple IN to fix limitation problems
179 # generate multiple IN to fix limitation problems
180 *in_filter_generator(RepoGroup.group_id, allowed_ids)
180 *in_filter_generator(RepoGroup.group_id, allowed_ids)
181 )) \
181 )) \
182 .outerjoin(Repository, Repository.group_id == RepoGroup.group_id) \
182 .outerjoin(Repository, Repository.group_id == RepoGroup.group_id) \
183 .join(User, User.user_id == RepoGroup.user_id) \
183 .join(User, User.user_id == RepoGroup.user_id) \
184 .group_by(RepoGroup, User)
184 .group_by(RepoGroup, User)
185
185
186 if search_q:
186 if search_q:
187 like_expression = u'%{}%'.format(safe_unicode(search_q))
187 like_expression = u'%{}%'.format(safe_unicode(search_q))
188 base_q = base_q.filter(or_(
188 base_q = base_q.filter(or_(
189 RepoGroup.group_name.ilike(like_expression),
189 RepoGroup.group_name.ilike(like_expression),
190 ))
190 ))
191
191
192 repo_groups_data_total_filtered_count = base_q.count()
192 repo_groups_data_total_filtered_count = base_q.count()
193 # the inactive isn't really used, but we still make it same as other data grids
193 # the inactive isn't really used, but we still make it same as other data grids
194 # which use inactive (users,user groups)
194 # which use inactive (users,user groups)
195 repo_groups_data_total_filtered_inactive_count = repo_groups_data_total_filtered_count
195 repo_groups_data_total_filtered_inactive_count = repo_groups_data_total_filtered_count
196
196
197 sort_defined = False
197 sort_defined = False
198 if order_by == 'group_name':
198 if order_by == 'group_name':
199 sort_col = func.lower(RepoGroup.group_name)
199 sort_col = func.lower(RepoGroup.group_name)
200 sort_defined = True
200 sort_defined = True
201 elif order_by == 'repos_total':
201 elif order_by == 'repos_total':
202 sort_col = repo_count
202 sort_col = repo_count
203 sort_defined = True
203 sort_defined = True
204 elif order_by == 'user_username':
204 elif order_by == 'user_username':
205 sort_col = User.username
205 sort_col = User.username
206 else:
206 else:
207 sort_col = getattr(RepoGroup, order_by, None)
207 sort_col = getattr(RepoGroup, order_by, None)
208
208
209 if sort_defined or sort_col:
209 if sort_defined or sort_col:
210 if order_dir == 'asc':
210 if order_dir == 'asc':
211 sort_col = sort_col.asc()
211 sort_col = sort_col.asc()
212 else:
212 else:
213 sort_col = sort_col.desc()
213 sort_col = sort_col.desc()
214
214
215 base_q = base_q.order_by(sort_col)
215 base_q = base_q.order_by(sort_col)
216 base_q = base_q.offset(start).limit(limit)
216 base_q = base_q.offset(start).limit(limit)
217
217
218 # authenticated access to user groups
218 # authenticated access to user groups
219 auth_repo_group_list = base_q.all()
219 auth_repo_group_list = base_q.all()
220
220
221 repo_groups_data = []
221 repo_groups_data = []
222 for repo_gr in auth_repo_group_list:
222 for repo_gr in auth_repo_group_list:
223 row = {
223 row = {
224 "menu": quick_menu(repo_gr.group_name),
224 "menu": quick_menu(repo_gr.group_name),
225 "name": repo_group_lnk(repo_gr.group_name),
225 "name": repo_group_lnk(repo_gr.group_name),
226 "name_raw": repo_gr.group_name,
226
227 "last_change": last_change(repo_gr.updated_on),
227 "last_change": last_change(repo_gr.updated_on),
228 "last_change_raw": datetime_to_time(repo_gr.updated_on),
229
228
230 "last_changeset": "",
229 "last_changeset": "",
231 "last_changeset_raw": "",
230 "last_changeset_raw": "",
232
231
233 "desc": desc(repo_gr.group_description, repo_gr.personal),
232 "desc": desc(repo_gr.group_description, repo_gr.personal),
234 "owner": user_profile(repo_gr.User.username),
233 "owner": user_profile(repo_gr.User.username),
235 "top_level_repos": repo_gr.repos_count,
234 "top_level_repos": repo_gr.repos_count,
236 "action": repo_group_actions(
235 "action": repo_group_actions(
237 repo_gr.group_id, repo_gr.group_name, repo_gr.repos_count),
236 repo_gr.group_id, repo_gr.group_name, repo_gr.repos_count),
238
237
239 }
238 }
240
239
241 repo_groups_data.append(row)
240 repo_groups_data.append(row)
242
241
243 data = ({
242 data = ({
244 'draw': draw,
243 'draw': draw,
245 'data': repo_groups_data,
244 'data': repo_groups_data,
246 'recordsTotal': repo_groups_data_total_count,
245 'recordsTotal': repo_groups_data_total_count,
247 'recordsTotalInactive': repo_groups_data_total_inactive_count,
246 'recordsTotalInactive': repo_groups_data_total_inactive_count,
248 'recordsFiltered': repo_groups_data_total_filtered_count,
247 'recordsFiltered': repo_groups_data_total_filtered_count,
249 'recordsFilteredInactive': repo_groups_data_total_filtered_inactive_count,
248 'recordsFilteredInactive': repo_groups_data_total_filtered_inactive_count,
250 })
249 })
251
250
252 return data
251 return data
253
252
254 @LoginRequired()
253 @LoginRequired()
255 @NotAnonymous()
254 @NotAnonymous()
256 # perm checks inside
255 # perm checks inside
257 @view_config(
256 @view_config(
258 route_name='repo_group_new', request_method='GET',
257 route_name='repo_group_new', request_method='GET',
259 renderer='rhodecode:templates/admin/repo_groups/repo_group_add.mako')
258 renderer='rhodecode:templates/admin/repo_groups/repo_group_add.mako')
260 def repo_group_new(self):
259 def repo_group_new(self):
261 c = self.load_default_context()
260 c = self.load_default_context()
262
261
263 # perm check for admin, create_group perm or admin of parent_group
262 # perm check for admin, create_group perm or admin of parent_group
264 parent_group_id = safe_int(self.request.GET.get('parent_group'))
263 parent_group_id = safe_int(self.request.GET.get('parent_group'))
265 if not self._can_create_repo_group(parent_group_id):
264 if not self._can_create_repo_group(parent_group_id):
266 raise HTTPForbidden()
265 raise HTTPForbidden()
267
266
268 self._load_form_data(c)
267 self._load_form_data(c)
269
268
270 defaults = {} # Future proof for default of repo group
269 defaults = {} # Future proof for default of repo group
271 data = render(
270 data = render(
272 'rhodecode:templates/admin/repo_groups/repo_group_add.mako',
271 'rhodecode:templates/admin/repo_groups/repo_group_add.mako',
273 self._get_template_context(c), self.request)
272 self._get_template_context(c), self.request)
274 html = formencode.htmlfill.render(
273 html = formencode.htmlfill.render(
275 data,
274 data,
276 defaults=defaults,
275 defaults=defaults,
277 encoding="UTF-8",
276 encoding="UTF-8",
278 force_defaults=False
277 force_defaults=False
279 )
278 )
280 return Response(html)
279 return Response(html)
281
280
282 @LoginRequired()
281 @LoginRequired()
283 @NotAnonymous()
282 @NotAnonymous()
284 @CSRFRequired()
283 @CSRFRequired()
285 # perm checks inside
284 # perm checks inside
286 @view_config(
285 @view_config(
287 route_name='repo_group_create', request_method='POST',
286 route_name='repo_group_create', request_method='POST',
288 renderer='rhodecode:templates/admin/repo_groups/repo_group_add.mako')
287 renderer='rhodecode:templates/admin/repo_groups/repo_group_add.mako')
289 def repo_group_create(self):
288 def repo_group_create(self):
290 c = self.load_default_context()
289 c = self.load_default_context()
291 _ = self.request.translate
290 _ = self.request.translate
292
291
293 parent_group_id = safe_int(self.request.POST.get('group_parent_id'))
292 parent_group_id = safe_int(self.request.POST.get('group_parent_id'))
294 can_create = self._can_create_repo_group(parent_group_id)
293 can_create = self._can_create_repo_group(parent_group_id)
295
294
296 self._load_form_data(c)
295 self._load_form_data(c)
297 # permissions for can create group based on parent_id are checked
296 # permissions for can create group based on parent_id are checked
298 # here in the Form
297 # here in the Form
299 available_groups = map(lambda k: safe_unicode(k[0]), c.repo_groups)
298 available_groups = map(lambda k: safe_unicode(k[0]), c.repo_groups)
300 repo_group_form = RepoGroupForm(
299 repo_group_form = RepoGroupForm(
301 self.request.translate, available_groups=available_groups,
300 self.request.translate, available_groups=available_groups,
302 can_create_in_root=can_create)()
301 can_create_in_root=can_create)()
303
302
304 repo_group_name = self.request.POST.get('group_name')
303 repo_group_name = self.request.POST.get('group_name')
305 try:
304 try:
306 owner = self._rhodecode_user
305 owner = self._rhodecode_user
307 form_result = repo_group_form.to_python(dict(self.request.POST))
306 form_result = repo_group_form.to_python(dict(self.request.POST))
308 copy_permissions = form_result.get('group_copy_permissions')
307 copy_permissions = form_result.get('group_copy_permissions')
309 repo_group = RepoGroupModel().create(
308 repo_group = RepoGroupModel().create(
310 group_name=form_result['group_name_full'],
309 group_name=form_result['group_name_full'],
311 group_description=form_result['group_description'],
310 group_description=form_result['group_description'],
312 owner=owner.user_id,
311 owner=owner.user_id,
313 copy_permissions=form_result['group_copy_permissions']
312 copy_permissions=form_result['group_copy_permissions']
314 )
313 )
315 Session().flush()
314 Session().flush()
316
315
317 repo_group_data = repo_group.get_api_data()
316 repo_group_data = repo_group.get_api_data()
318 audit_logger.store_web(
317 audit_logger.store_web(
319 'repo_group.create', action_data={'data': repo_group_data},
318 'repo_group.create', action_data={'data': repo_group_data},
320 user=self._rhodecode_user)
319 user=self._rhodecode_user)
321
320
322 Session().commit()
321 Session().commit()
323
322
324 _new_group_name = form_result['group_name_full']
323 _new_group_name = form_result['group_name_full']
325
324
326 repo_group_url = h.link_to(
325 repo_group_url = h.link_to(
327 _new_group_name,
326 _new_group_name,
328 h.route_path('repo_group_home', repo_group_name=_new_group_name))
327 h.route_path('repo_group_home', repo_group_name=_new_group_name))
329 h.flash(h.literal(_('Created repository group %s')
328 h.flash(h.literal(_('Created repository group %s')
330 % repo_group_url), category='success')
329 % repo_group_url), category='success')
331
330
332 except formencode.Invalid as errors:
331 except formencode.Invalid as errors:
333 data = render(
332 data = render(
334 'rhodecode:templates/admin/repo_groups/repo_group_add.mako',
333 'rhodecode:templates/admin/repo_groups/repo_group_add.mako',
335 self._get_template_context(c), self.request)
334 self._get_template_context(c), self.request)
336 html = formencode.htmlfill.render(
335 html = formencode.htmlfill.render(
337 data,
336 data,
338 defaults=errors.value,
337 defaults=errors.value,
339 errors=errors.error_dict or {},
338 errors=errors.error_dict or {},
340 prefix_error=False,
339 prefix_error=False,
341 encoding="UTF-8",
340 encoding="UTF-8",
342 force_defaults=False
341 force_defaults=False
343 )
342 )
344 return Response(html)
343 return Response(html)
345 except Exception:
344 except Exception:
346 log.exception("Exception during creation of repository group")
345 log.exception("Exception during creation of repository group")
347 h.flash(_('Error occurred during creation of repository group %s')
346 h.flash(_('Error occurred during creation of repository group %s')
348 % repo_group_name, category='error')
347 % repo_group_name, category='error')
349 raise HTTPFound(h.route_path('home'))
348 raise HTTPFound(h.route_path('home'))
350
349
351 affected_user_ids = [self._rhodecode_user.user_id]
350 affected_user_ids = [self._rhodecode_user.user_id]
352 if copy_permissions:
351 if copy_permissions:
353 user_group_perms = repo_group.permissions(expand_from_user_groups=True)
352 user_group_perms = repo_group.permissions(expand_from_user_groups=True)
354 copy_perms = [perm['user_id'] for perm in user_group_perms]
353 copy_perms = [perm['user_id'] for perm in user_group_perms]
355 # also include those newly created by copy
354 # also include those newly created by copy
356 affected_user_ids.extend(copy_perms)
355 affected_user_ids.extend(copy_perms)
357 PermissionModel().trigger_permission_flush(affected_user_ids)
356 PermissionModel().trigger_permission_flush(affected_user_ids)
358
357
359 raise HTTPFound(
358 raise HTTPFound(
360 h.route_path('repo_group_home',
359 h.route_path('repo_group_home',
361 repo_group_name=form_result['group_name_full']))
360 repo_group_name=form_result['group_name_full']))
@@ -1,266 +1,266 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2019 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 formencode
22 import formencode
23 import formencode.htmlfill
23 import formencode.htmlfill
24
24
25 from pyramid.httpexceptions import HTTPFound, HTTPForbidden
25 from pyramid.httpexceptions import HTTPFound, HTTPForbidden
26 from pyramid.view import view_config
26 from pyramid.view import view_config
27 from pyramid.renderers import render
27 from pyramid.renderers import render
28 from pyramid.response import Response
28 from pyramid.response import Response
29
29
30 from rhodecode import events
30 from rhodecode import events
31 from rhodecode.apps._base import BaseAppView, DataGridAppView
31 from rhodecode.apps._base import BaseAppView, DataGridAppView
32 from rhodecode.lib.celerylib.utils import get_task_id
32 from rhodecode.lib.celerylib.utils import get_task_id
33
33
34 from rhodecode.lib.auth import (
34 from rhodecode.lib.auth import (
35 LoginRequired, CSRFRequired, NotAnonymous,
35 LoginRequired, CSRFRequired, NotAnonymous,
36 HasPermissionAny, HasRepoGroupPermissionAny)
36 HasPermissionAny, HasRepoGroupPermissionAny)
37 from rhodecode.lib import helpers as h
37 from rhodecode.lib import helpers as h
38 from rhodecode.lib.utils import repo_name_slug
38 from rhodecode.lib.utils import repo_name_slug
39 from rhodecode.lib.utils2 import safe_int, safe_unicode
39 from rhodecode.lib.utils2 import safe_int, safe_unicode
40 from rhodecode.model.forms import RepoForm
40 from rhodecode.model.forms import RepoForm
41 from rhodecode.model.permission import PermissionModel
41 from rhodecode.model.permission import PermissionModel
42 from rhodecode.model.repo import RepoModel
42 from rhodecode.model.repo import RepoModel
43 from rhodecode.model.scm import RepoList, RepoGroupList, ScmModel
43 from rhodecode.model.scm import RepoList, RepoGroupList, ScmModel
44 from rhodecode.model.settings import SettingsModel
44 from rhodecode.model.settings import SettingsModel
45 from rhodecode.model.db import (
45 from rhodecode.model.db import (
46 in_filter_generator, or_, func, Session, Repository, RepoGroup, User)
46 in_filter_generator, or_, func, Session, Repository, RepoGroup, User)
47
47
48 log = logging.getLogger(__name__)
48 log = logging.getLogger(__name__)
49
49
50
50
51 class AdminReposView(BaseAppView, DataGridAppView):
51 class AdminReposView(BaseAppView, DataGridAppView):
52
52
53 def load_default_context(self):
53 def load_default_context(self):
54 c = self._get_local_tmpl_context()
54 c = self._get_local_tmpl_context()
55
55
56 return c
56 return c
57
57
58 def _load_form_data(self, c):
58 def _load_form_data(self, c):
59 acl_groups = RepoGroupList(RepoGroup.query().all(),
59 acl_groups = RepoGroupList(RepoGroup.query().all(),
60 perm_set=['group.write', 'group.admin'])
60 perm_set=['group.write', 'group.admin'])
61 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
61 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
62 c.repo_groups_choices = map(lambda k: safe_unicode(k[0]), c.repo_groups)
62 c.repo_groups_choices = map(lambda k: safe_unicode(k[0]), c.repo_groups)
63 c.personal_repo_group = self._rhodecode_user.personal_repo_group
63 c.personal_repo_group = self._rhodecode_user.personal_repo_group
64
64
65 @LoginRequired()
65 @LoginRequired()
66 @NotAnonymous()
66 @NotAnonymous()
67 # perms check inside
67 # perms check inside
68 @view_config(
68 @view_config(
69 route_name='repos', request_method='GET',
69 route_name='repos', request_method='GET',
70 renderer='rhodecode:templates/admin/repos/repos.mako')
70 renderer='rhodecode:templates/admin/repos/repos.mako')
71 def repository_list(self):
71 def repository_list(self):
72 c = self.load_default_context()
72 c = self.load_default_context()
73 return self._get_template_context(c)
73 return self._get_template_context(c)
74
74
75 @LoginRequired()
75 @LoginRequired()
76 @NotAnonymous()
76 @NotAnonymous()
77 # perms check inside
77 # perms check inside
78 @view_config(
78 @view_config(
79 route_name='repos_data', request_method='GET',
79 route_name='repos_data', request_method='GET',
80 renderer='json_ext', xhr=True)
80 renderer='json_ext', xhr=True)
81 def repository_list_data(self):
81 def repository_list_data(self):
82 self.load_default_context()
82 self.load_default_context()
83 column_map = {
83 column_map = {
84 'name_raw': 'repo_name',
84 'name': 'repo_name',
85 'desc': 'description',
85 'desc': 'description',
86 'last_change_raw': 'updated_on',
86 'last_change': 'updated_on',
87 'owner': 'user_username',
87 'owner': 'user_username',
88 }
88 }
89 draw, start, limit = self._extract_chunk(self.request)
89 draw, start, limit = self._extract_chunk(self.request)
90 search_q, order_by, order_dir = self._extract_ordering(
90 search_q, order_by, order_dir = self._extract_ordering(
91 self.request, column_map=column_map)
91 self.request, column_map=column_map)
92
92
93 _perms = ['repository.admin']
93 _perms = ['repository.admin']
94 allowed_ids = [-1] + self._rhodecode_user.repo_acl_ids_from_stack(_perms)
94 allowed_ids = [-1] + self._rhodecode_user.repo_acl_ids_from_stack(_perms)
95
95
96 repos_data_total_count = Repository.query() \
96 repos_data_total_count = Repository.query() \
97 .filter(or_(
97 .filter(or_(
98 # generate multiple IN to fix limitation problems
98 # generate multiple IN to fix limitation problems
99 *in_filter_generator(Repository.repo_id, allowed_ids))
99 *in_filter_generator(Repository.repo_id, allowed_ids))
100 ) \
100 ) \
101 .count()
101 .count()
102
102
103 base_q = Session.query(
103 base_q = Session.query(
104 Repository.repo_id,
104 Repository.repo_id,
105 Repository.repo_name,
105 Repository.repo_name,
106 Repository.description,
106 Repository.description,
107 Repository.repo_type,
107 Repository.repo_type,
108 Repository.repo_state,
108 Repository.repo_state,
109 Repository.private,
109 Repository.private,
110 Repository.archived,
110 Repository.archived,
111 Repository.fork,
111 Repository.fork,
112 Repository.updated_on,
112 Repository.updated_on,
113 Repository._changeset_cache,
113 Repository._changeset_cache,
114 User,
114 User,
115 ) \
115 ) \
116 .filter(or_(
116 .filter(or_(
117 # generate multiple IN to fix limitation problems
117 # generate multiple IN to fix limitation problems
118 *in_filter_generator(Repository.repo_id, allowed_ids))
118 *in_filter_generator(Repository.repo_id, allowed_ids))
119 ) \
119 ) \
120 .join(User, User.user_id == Repository.user_id) \
120 .join(User, User.user_id == Repository.user_id) \
121 .group_by(Repository, User)
121 .group_by(Repository, User)
122
122
123 if search_q:
123 if search_q:
124 like_expression = u'%{}%'.format(safe_unicode(search_q))
124 like_expression = u'%{}%'.format(safe_unicode(search_q))
125 base_q = base_q.filter(or_(
125 base_q = base_q.filter(or_(
126 Repository.repo_name.ilike(like_expression),
126 Repository.repo_name.ilike(like_expression),
127 ))
127 ))
128
128
129 repos_data_total_filtered_count = base_q.count()
129 repos_data_total_filtered_count = base_q.count()
130
130
131 sort_defined = False
131 sort_defined = False
132 if order_by == 'repo_name':
132 if order_by == 'repo_name':
133 sort_col = func.lower(Repository.repo_name)
133 sort_col = func.lower(Repository.repo_name)
134 sort_defined = True
134 sort_defined = True
135 elif order_by == 'user_username':
135 elif order_by == 'user_username':
136 sort_col = User.username
136 sort_col = User.username
137 else:
137 else:
138 sort_col = getattr(Repository, order_by, None)
138 sort_col = getattr(Repository, order_by, None)
139
139
140 if sort_defined or sort_col:
140 if sort_defined or sort_col:
141 if order_dir == 'asc':
141 if order_dir == 'asc':
142 sort_col = sort_col.asc()
142 sort_col = sort_col.asc()
143 else:
143 else:
144 sort_col = sort_col.desc()
144 sort_col = sort_col.desc()
145
145
146 base_q = base_q.order_by(sort_col)
146 base_q = base_q.order_by(sort_col)
147 base_q = base_q.offset(start).limit(limit)
147 base_q = base_q.offset(start).limit(limit)
148
148
149 repos_list = base_q.all()
149 repos_list = base_q.all()
150
150
151 repos_data = RepoModel().get_repos_as_dict(
151 repos_data = RepoModel().get_repos_as_dict(
152 repo_list=repos_list, admin=True, super_user_actions=True)
152 repo_list=repos_list, admin=True, super_user_actions=True)
153
153
154 data = ({
154 data = ({
155 'draw': draw,
155 'draw': draw,
156 'data': repos_data,
156 'data': repos_data,
157 'recordsTotal': repos_data_total_count,
157 'recordsTotal': repos_data_total_count,
158 'recordsFiltered': repos_data_total_filtered_count,
158 'recordsFiltered': repos_data_total_filtered_count,
159 })
159 })
160 return data
160 return data
161
161
162 @LoginRequired()
162 @LoginRequired()
163 @NotAnonymous()
163 @NotAnonymous()
164 # perms check inside
164 # perms check inside
165 @view_config(
165 @view_config(
166 route_name='repo_new', request_method='GET',
166 route_name='repo_new', request_method='GET',
167 renderer='rhodecode:templates/admin/repos/repo_add.mako')
167 renderer='rhodecode:templates/admin/repos/repo_add.mako')
168 def repository_new(self):
168 def repository_new(self):
169 c = self.load_default_context()
169 c = self.load_default_context()
170
170
171 new_repo = self.request.GET.get('repo', '')
171 new_repo = self.request.GET.get('repo', '')
172 parent_group = safe_int(self.request.GET.get('parent_group'))
172 parent_group = safe_int(self.request.GET.get('parent_group'))
173 _gr = RepoGroup.get(parent_group)
173 _gr = RepoGroup.get(parent_group)
174
174
175 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
175 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
176 # you're not super admin nor have global create permissions,
176 # you're not super admin nor have global create permissions,
177 # but maybe you have at least write permission to a parent group ?
177 # but maybe you have at least write permission to a parent group ?
178
178
179 gr_name = _gr.group_name if _gr else None
179 gr_name = _gr.group_name if _gr else None
180 # create repositories with write permission on group is set to true
180 # create repositories with write permission on group is set to true
181 create_on_write = HasPermissionAny('hg.create.write_on_repogroup.true')()
181 create_on_write = HasPermissionAny('hg.create.write_on_repogroup.true')()
182 group_admin = HasRepoGroupPermissionAny('group.admin')(group_name=gr_name)
182 group_admin = HasRepoGroupPermissionAny('group.admin')(group_name=gr_name)
183 group_write = HasRepoGroupPermissionAny('group.write')(group_name=gr_name)
183 group_write = HasRepoGroupPermissionAny('group.write')(group_name=gr_name)
184 if not (group_admin or (group_write and create_on_write)):
184 if not (group_admin or (group_write and create_on_write)):
185 raise HTTPForbidden()
185 raise HTTPForbidden()
186
186
187 self._load_form_data(c)
187 self._load_form_data(c)
188 c.new_repo = repo_name_slug(new_repo)
188 c.new_repo = repo_name_slug(new_repo)
189
189
190 # apply the defaults from defaults page
190 # apply the defaults from defaults page
191 defaults = SettingsModel().get_default_repo_settings(strip_prefix=True)
191 defaults = SettingsModel().get_default_repo_settings(strip_prefix=True)
192 # set checkbox to autochecked
192 # set checkbox to autochecked
193 defaults['repo_copy_permissions'] = True
193 defaults['repo_copy_permissions'] = True
194
194
195 parent_group_choice = '-1'
195 parent_group_choice = '-1'
196 if not self._rhodecode_user.is_admin and self._rhodecode_user.personal_repo_group:
196 if not self._rhodecode_user.is_admin and self._rhodecode_user.personal_repo_group:
197 parent_group_choice = self._rhodecode_user.personal_repo_group
197 parent_group_choice = self._rhodecode_user.personal_repo_group
198
198
199 if parent_group and _gr:
199 if parent_group and _gr:
200 if parent_group in [x[0] for x in c.repo_groups]:
200 if parent_group in [x[0] for x in c.repo_groups]:
201 parent_group_choice = safe_unicode(parent_group)
201 parent_group_choice = safe_unicode(parent_group)
202
202
203 defaults.update({'repo_group': parent_group_choice})
203 defaults.update({'repo_group': parent_group_choice})
204
204
205 data = render('rhodecode:templates/admin/repos/repo_add.mako',
205 data = render('rhodecode:templates/admin/repos/repo_add.mako',
206 self._get_template_context(c), self.request)
206 self._get_template_context(c), self.request)
207 html = formencode.htmlfill.render(
207 html = formencode.htmlfill.render(
208 data,
208 data,
209 defaults=defaults,
209 defaults=defaults,
210 encoding="UTF-8",
210 encoding="UTF-8",
211 force_defaults=False
211 force_defaults=False
212 )
212 )
213 return Response(html)
213 return Response(html)
214
214
215 @LoginRequired()
215 @LoginRequired()
216 @NotAnonymous()
216 @NotAnonymous()
217 @CSRFRequired()
217 @CSRFRequired()
218 # perms check inside
218 # perms check inside
219 @view_config(
219 @view_config(
220 route_name='repo_create', request_method='POST',
220 route_name='repo_create', request_method='POST',
221 renderer='rhodecode:templates/admin/repos/repos.mako')
221 renderer='rhodecode:templates/admin/repos/repos.mako')
222 def repository_create(self):
222 def repository_create(self):
223 c = self.load_default_context()
223 c = self.load_default_context()
224
224
225 form_result = {}
225 form_result = {}
226 self._load_form_data(c)
226 self._load_form_data(c)
227
227
228 try:
228 try:
229 # CanWriteToGroup validators checks permissions of this POST
229 # CanWriteToGroup validators checks permissions of this POST
230 form = RepoForm(
230 form = RepoForm(
231 self.request.translate, repo_groups=c.repo_groups_choices)()
231 self.request.translate, repo_groups=c.repo_groups_choices)()
232 form_result = form.to_python(dict(self.request.POST))
232 form_result = form.to_python(dict(self.request.POST))
233 copy_permissions = form_result.get('repo_copy_permissions')
233 copy_permissions = form_result.get('repo_copy_permissions')
234 # create is done sometimes async on celery, db transaction
234 # create is done sometimes async on celery, db transaction
235 # management is handled there.
235 # management is handled there.
236 task = RepoModel().create(form_result, self._rhodecode_user.user_id)
236 task = RepoModel().create(form_result, self._rhodecode_user.user_id)
237 task_id = get_task_id(task)
237 task_id = get_task_id(task)
238 except formencode.Invalid as errors:
238 except formencode.Invalid as errors:
239 data = render('rhodecode:templates/admin/repos/repo_add.mako',
239 data = render('rhodecode:templates/admin/repos/repo_add.mako',
240 self._get_template_context(c), self.request)
240 self._get_template_context(c), self.request)
241 html = formencode.htmlfill.render(
241 html = formencode.htmlfill.render(
242 data,
242 data,
243 defaults=errors.value,
243 defaults=errors.value,
244 errors=errors.error_dict or {},
244 errors=errors.error_dict or {},
245 prefix_error=False,
245 prefix_error=False,
246 encoding="UTF-8",
246 encoding="UTF-8",
247 force_defaults=False
247 force_defaults=False
248 )
248 )
249 return Response(html)
249 return Response(html)
250
250
251 except Exception as e:
251 except Exception as e:
252 msg = self._log_creation_exception(e, form_result.get('repo_name'))
252 msg = self._log_creation_exception(e, form_result.get('repo_name'))
253 h.flash(msg, category='error')
253 h.flash(msg, category='error')
254 raise HTTPFound(h.route_path('home'))
254 raise HTTPFound(h.route_path('home'))
255
255
256 repo_name = form_result.get('repo_name_full')
256 repo_name = form_result.get('repo_name_full')
257
257
258 affected_user_ids = [self._rhodecode_user.user_id]
258 affected_user_ids = [self._rhodecode_user.user_id]
259 if copy_permissions:
259 if copy_permissions:
260 # permission flush is done in repo creating
260 # permission flush is done in repo creating
261 pass
261 pass
262 PermissionModel().trigger_permission_flush(affected_user_ids)
262 PermissionModel().trigger_permission_flush(affected_user_ids)
263
263
264 raise HTTPFound(
264 raise HTTPFound(
265 h.route_path('repo_creating', repo_name=repo_name,
265 h.route_path('repo_creating', repo_name=repo_name,
266 _query=dict(task_id=task_id)))
266 _query=dict(task_id=task_id)))
@@ -1,269 +1,268 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2019 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 import events
31 from rhodecode import events
32 from rhodecode.apps._base import BaseAppView, DataGridAppView
32 from rhodecode.apps._base import BaseAppView, DataGridAppView
33 from rhodecode.lib.auth import (
33 from rhodecode.lib.auth import (
34 LoginRequired, NotAnonymous, CSRFRequired, HasPermissionAnyDecorator)
34 LoginRequired, NotAnonymous, CSRFRequired, HasPermissionAnyDecorator)
35 from rhodecode.lib import helpers as h, audit_logger
35 from rhodecode.lib import helpers as h, audit_logger
36 from rhodecode.lib.utils2 import safe_unicode
36 from rhodecode.lib.utils2 import safe_unicode
37
37
38 from rhodecode.model.forms import UserGroupForm
38 from rhodecode.model.forms import UserGroupForm
39 from rhodecode.model.permission import PermissionModel
39 from rhodecode.model.permission import PermissionModel
40 from rhodecode.model.scm import UserGroupList
40 from rhodecode.model.scm import UserGroupList
41 from rhodecode.model.db import (
41 from rhodecode.model.db import (
42 or_, count, User, UserGroup, UserGroupMember, in_filter_generator)
42 or_, count, User, UserGroup, UserGroupMember, in_filter_generator)
43 from rhodecode.model.meta import Session
43 from rhodecode.model.meta import Session
44 from rhodecode.model.user_group import UserGroupModel
44 from rhodecode.model.user_group import UserGroupModel
45 from rhodecode.model.db import true
45 from rhodecode.model.db import true
46
46
47 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
48
48
49
49
50 class AdminUserGroupsView(BaseAppView, DataGridAppView):
50 class AdminUserGroupsView(BaseAppView, DataGridAppView):
51
51
52 def load_default_context(self):
52 def load_default_context(self):
53 c = self._get_local_tmpl_context()
53 c = self._get_local_tmpl_context()
54
54
55 PermissionModel().set_global_permission_choices(
55 PermissionModel().set_global_permission_choices(
56 c, gettext_translator=self.request.translate)
56 c, gettext_translator=self.request.translate)
57
57
58 return c
58 return c
59
59
60 # permission check in data loading of
60 # permission check in data loading of
61 # `user_groups_list_data` via UserGroupList
61 # `user_groups_list_data` via UserGroupList
62 @LoginRequired()
62 @LoginRequired()
63 @NotAnonymous()
63 @NotAnonymous()
64 @view_config(
64 @view_config(
65 route_name='user_groups', request_method='GET',
65 route_name='user_groups', request_method='GET',
66 renderer='rhodecode:templates/admin/user_groups/user_groups.mako')
66 renderer='rhodecode:templates/admin/user_groups/user_groups.mako')
67 def user_groups_list(self):
67 def user_groups_list(self):
68 c = self.load_default_context()
68 c = self.load_default_context()
69 return self._get_template_context(c)
69 return self._get_template_context(c)
70
70
71 # permission check inside
71 # permission check inside
72 @LoginRequired()
72 @LoginRequired()
73 @NotAnonymous()
73 @NotAnonymous()
74 @view_config(
74 @view_config(
75 route_name='user_groups_data', request_method='GET',
75 route_name='user_groups_data', request_method='GET',
76 renderer='json_ext', xhr=True)
76 renderer='json_ext', xhr=True)
77 def user_groups_list_data(self):
77 def user_groups_list_data(self):
78 self.load_default_context()
78 self.load_default_context()
79 column_map = {
79 column_map = {
80 'active': 'users_group_active',
80 'active': 'users_group_active',
81 'description': 'user_group_description',
81 'description': 'user_group_description',
82 'members': 'members_total',
82 'members': 'members_total',
83 'owner': 'user_username',
83 'owner': 'user_username',
84 'sync': 'group_data'
84 'sync': 'group_data'
85 }
85 }
86 draw, start, limit = self._extract_chunk(self.request)
86 draw, start, limit = self._extract_chunk(self.request)
87 search_q, order_by, order_dir = self._extract_ordering(
87 search_q, order_by, order_dir = self._extract_ordering(
88 self.request, column_map=column_map)
88 self.request, column_map=column_map)
89
89
90 _render = self.request.get_partial_renderer(
90 _render = self.request.get_partial_renderer(
91 'rhodecode:templates/data_table/_dt_elements.mako')
91 'rhodecode:templates/data_table/_dt_elements.mako')
92
92
93 def user_group_name(user_group_name):
93 def user_group_name(user_group_name):
94 return _render("user_group_name", user_group_name)
94 return _render("user_group_name", user_group_name)
95
95
96 def user_group_actions(user_group_id, user_group_name):
96 def user_group_actions(user_group_id, user_group_name):
97 return _render("user_group_actions", user_group_id, user_group_name)
97 return _render("user_group_actions", user_group_id, user_group_name)
98
98
99 def user_profile(username):
99 def user_profile(username):
100 return _render('user_profile', username)
100 return _render('user_profile', username)
101
101
102 _perms = ['usergroup.admin']
102 _perms = ['usergroup.admin']
103 allowed_ids = [-1] + self._rhodecode_user.user_group_acl_ids_from_stack(_perms)
103 allowed_ids = [-1] + self._rhodecode_user.user_group_acl_ids_from_stack(_perms)
104
104
105 user_groups_data_total_count = UserGroup.query()\
105 user_groups_data_total_count = UserGroup.query()\
106 .filter(or_(
106 .filter(or_(
107 # generate multiple IN to fix limitation problems
107 # generate multiple IN to fix limitation problems
108 *in_filter_generator(UserGroup.users_group_id, allowed_ids)
108 *in_filter_generator(UserGroup.users_group_id, allowed_ids)
109 ))\
109 ))\
110 .count()
110 .count()
111
111
112 user_groups_data_total_inactive_count = UserGroup.query()\
112 user_groups_data_total_inactive_count = UserGroup.query()\
113 .filter(or_(
113 .filter(or_(
114 # generate multiple IN to fix limitation problems
114 # generate multiple IN to fix limitation problems
115 *in_filter_generator(UserGroup.users_group_id, allowed_ids)
115 *in_filter_generator(UserGroup.users_group_id, allowed_ids)
116 ))\
116 ))\
117 .filter(UserGroup.users_group_active != true()).count()
117 .filter(UserGroup.users_group_active != true()).count()
118
118
119 member_count = count(UserGroupMember.user_id)
119 member_count = count(UserGroupMember.user_id)
120 base_q = Session.query(
120 base_q = Session.query(
121 UserGroup.users_group_name,
121 UserGroup.users_group_name,
122 UserGroup.user_group_description,
122 UserGroup.user_group_description,
123 UserGroup.users_group_active,
123 UserGroup.users_group_active,
124 UserGroup.users_group_id,
124 UserGroup.users_group_id,
125 UserGroup.group_data,
125 UserGroup.group_data,
126 User,
126 User,
127 member_count.label('member_count')
127 member_count.label('member_count')
128 ) \
128 ) \
129 .filter(or_(
129 .filter(or_(
130 # generate multiple IN to fix limitation problems
130 # generate multiple IN to fix limitation problems
131 *in_filter_generator(UserGroup.users_group_id, allowed_ids)
131 *in_filter_generator(UserGroup.users_group_id, allowed_ids)
132 )) \
132 )) \
133 .outerjoin(UserGroupMember, UserGroupMember.users_group_id == UserGroup.users_group_id) \
133 .outerjoin(UserGroupMember, UserGroupMember.users_group_id == UserGroup.users_group_id) \
134 .join(User, User.user_id == UserGroup.user_id) \
134 .join(User, User.user_id == UserGroup.user_id) \
135 .group_by(UserGroup, User)
135 .group_by(UserGroup, User)
136
136
137 base_q_inactive = base_q.filter(UserGroup.users_group_active != true())
137 base_q_inactive = base_q.filter(UserGroup.users_group_active != true())
138
138
139 if search_q:
139 if search_q:
140 like_expression = u'%{}%'.format(safe_unicode(search_q))
140 like_expression = u'%{}%'.format(safe_unicode(search_q))
141 base_q = base_q.filter(or_(
141 base_q = base_q.filter(or_(
142 UserGroup.users_group_name.ilike(like_expression),
142 UserGroup.users_group_name.ilike(like_expression),
143 ))
143 ))
144 base_q_inactive = base_q.filter(UserGroup.users_group_active != true())
144 base_q_inactive = base_q.filter(UserGroup.users_group_active != true())
145
145
146 user_groups_data_total_filtered_count = base_q.count()
146 user_groups_data_total_filtered_count = base_q.count()
147 user_groups_data_total_filtered_inactive_count = base_q_inactive.count()
147 user_groups_data_total_filtered_inactive_count = base_q_inactive.count()
148
148
149 sort_defined = False
149 sort_defined = False
150 if order_by == 'members_total':
150 if order_by == 'members_total':
151 sort_col = member_count
151 sort_col = member_count
152 sort_defined = True
152 sort_defined = True
153 elif order_by == 'user_username':
153 elif order_by == 'user_username':
154 sort_col = User.username
154 sort_col = User.username
155 else:
155 else:
156 sort_col = getattr(UserGroup, order_by, None)
156 sort_col = getattr(UserGroup, order_by, None)
157
157
158 if sort_defined or sort_col:
158 if sort_defined or sort_col:
159 if order_dir == 'asc':
159 if order_dir == 'asc':
160 sort_col = sort_col.asc()
160 sort_col = sort_col.asc()
161 else:
161 else:
162 sort_col = sort_col.desc()
162 sort_col = sort_col.desc()
163
163
164 base_q = base_q.order_by(sort_col)
164 base_q = base_q.order_by(sort_col)
165 base_q = base_q.offset(start).limit(limit)
165 base_q = base_q.offset(start).limit(limit)
166
166
167 # authenticated access to user groups
167 # authenticated access to user groups
168 auth_user_group_list = base_q.all()
168 auth_user_group_list = base_q.all()
169
169
170 user_groups_data = []
170 user_groups_data = []
171 for user_gr in auth_user_group_list:
171 for user_gr in auth_user_group_list:
172 row = {
172 row = {
173 "users_group_name": user_group_name(user_gr.users_group_name),
173 "users_group_name": user_group_name(user_gr.users_group_name),
174 "name_raw": h.escape(user_gr.users_group_name),
175 "description": h.escape(user_gr.user_group_description),
174 "description": h.escape(user_gr.user_group_description),
176 "members": user_gr.member_count,
175 "members": user_gr.member_count,
177 # NOTE(marcink): because of advanced query we
176 # NOTE(marcink): because of advanced query we
178 # need to load it like that
177 # need to load it like that
179 "sync": UserGroup._load_sync(
178 "sync": UserGroup._load_sync(
180 UserGroup._load_group_data(user_gr.group_data)),
179 UserGroup._load_group_data(user_gr.group_data)),
181 "active": h.bool2icon(user_gr.users_group_active),
180 "active": h.bool2icon(user_gr.users_group_active),
182 "owner": user_profile(user_gr.User.username),
181 "owner": user_profile(user_gr.User.username),
183 "action": user_group_actions(
182 "action": user_group_actions(
184 user_gr.users_group_id, user_gr.users_group_name)
183 user_gr.users_group_id, user_gr.users_group_name)
185 }
184 }
186 user_groups_data.append(row)
185 user_groups_data.append(row)
187
186
188 data = ({
187 data = ({
189 'draw': draw,
188 'draw': draw,
190 'data': user_groups_data,
189 'data': user_groups_data,
191 'recordsTotal': user_groups_data_total_count,
190 'recordsTotal': user_groups_data_total_count,
192 'recordsTotalInactive': user_groups_data_total_inactive_count,
191 'recordsTotalInactive': user_groups_data_total_inactive_count,
193 'recordsFiltered': user_groups_data_total_filtered_count,
192 'recordsFiltered': user_groups_data_total_filtered_count,
194 'recordsFilteredInactive': user_groups_data_total_filtered_inactive_count,
193 'recordsFilteredInactive': user_groups_data_total_filtered_inactive_count,
195 })
194 })
196
195
197 return data
196 return data
198
197
199 @LoginRequired()
198 @LoginRequired()
200 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
199 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
201 @view_config(
200 @view_config(
202 route_name='user_groups_new', request_method='GET',
201 route_name='user_groups_new', request_method='GET',
203 renderer='rhodecode:templates/admin/user_groups/user_group_add.mako')
202 renderer='rhodecode:templates/admin/user_groups/user_group_add.mako')
204 def user_groups_new(self):
203 def user_groups_new(self):
205 c = self.load_default_context()
204 c = self.load_default_context()
206 return self._get_template_context(c)
205 return self._get_template_context(c)
207
206
208 @LoginRequired()
207 @LoginRequired()
209 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
208 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
210 @CSRFRequired()
209 @CSRFRequired()
211 @view_config(
210 @view_config(
212 route_name='user_groups_create', request_method='POST',
211 route_name='user_groups_create', request_method='POST',
213 renderer='rhodecode:templates/admin/user_groups/user_group_add.mako')
212 renderer='rhodecode:templates/admin/user_groups/user_group_add.mako')
214 def user_groups_create(self):
213 def user_groups_create(self):
215 _ = self.request.translate
214 _ = self.request.translate
216 c = self.load_default_context()
215 c = self.load_default_context()
217 users_group_form = UserGroupForm(self.request.translate)()
216 users_group_form = UserGroupForm(self.request.translate)()
218
217
219 user_group_name = self.request.POST.get('users_group_name')
218 user_group_name = self.request.POST.get('users_group_name')
220 try:
219 try:
221 form_result = users_group_form.to_python(dict(self.request.POST))
220 form_result = users_group_form.to_python(dict(self.request.POST))
222 user_group = UserGroupModel().create(
221 user_group = UserGroupModel().create(
223 name=form_result['users_group_name'],
222 name=form_result['users_group_name'],
224 description=form_result['user_group_description'],
223 description=form_result['user_group_description'],
225 owner=self._rhodecode_user.user_id,
224 owner=self._rhodecode_user.user_id,
226 active=form_result['users_group_active'])
225 active=form_result['users_group_active'])
227 Session().flush()
226 Session().flush()
228 creation_data = user_group.get_api_data()
227 creation_data = user_group.get_api_data()
229 user_group_name = form_result['users_group_name']
228 user_group_name = form_result['users_group_name']
230
229
231 audit_logger.store_web(
230 audit_logger.store_web(
232 'user_group.create', action_data={'data': creation_data},
231 'user_group.create', action_data={'data': creation_data},
233 user=self._rhodecode_user)
232 user=self._rhodecode_user)
234
233
235 user_group_link = h.link_to(
234 user_group_link = h.link_to(
236 h.escape(user_group_name),
235 h.escape(user_group_name),
237 h.route_path(
236 h.route_path(
238 'edit_user_group', user_group_id=user_group.users_group_id))
237 'edit_user_group', user_group_id=user_group.users_group_id))
239 h.flash(h.literal(_('Created user group %(user_group_link)s')
238 h.flash(h.literal(_('Created user group %(user_group_link)s')
240 % {'user_group_link': user_group_link}),
239 % {'user_group_link': user_group_link}),
241 category='success')
240 category='success')
242 Session().commit()
241 Session().commit()
243 user_group_id = user_group.users_group_id
242 user_group_id = user_group.users_group_id
244 except formencode.Invalid as errors:
243 except formencode.Invalid as errors:
245
244
246 data = render(
245 data = render(
247 'rhodecode:templates/admin/user_groups/user_group_add.mako',
246 'rhodecode:templates/admin/user_groups/user_group_add.mako',
248 self._get_template_context(c), self.request)
247 self._get_template_context(c), self.request)
249 html = formencode.htmlfill.render(
248 html = formencode.htmlfill.render(
250 data,
249 data,
251 defaults=errors.value,
250 defaults=errors.value,
252 errors=errors.error_dict or {},
251 errors=errors.error_dict or {},
253 prefix_error=False,
252 prefix_error=False,
254 encoding="UTF-8",
253 encoding="UTF-8",
255 force_defaults=False
254 force_defaults=False
256 )
255 )
257 return Response(html)
256 return Response(html)
258
257
259 except Exception:
258 except Exception:
260 log.exception("Exception creating user group")
259 log.exception("Exception creating user group")
261 h.flash(_('Error occurred during creation of user group %s') \
260 h.flash(_('Error occurred during creation of user group %s') \
262 % user_group_name, category='error')
261 % user_group_name, category='error')
263 raise HTTPFound(h.route_path('user_groups_new'))
262 raise HTTPFound(h.route_path('user_groups_new'))
264
263
265 affected_user_ids = [self._rhodecode_user.user_id]
264 affected_user_ids = [self._rhodecode_user.user_id]
266 PermissionModel().trigger_permission_flush(affected_user_ids)
265 PermissionModel().trigger_permission_flush(affected_user_ids)
267
266
268 raise HTTPFound(
267 raise HTTPFound(
269 h.route_path('edit_user_group', user_group_id=user_group_id))
268 h.route_path('edit_user_group', user_group_id=user_group_id))
@@ -1,823 +1,823 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2019 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 import collections
23 import collections
24
24
25 from pyramid.httpexceptions import HTTPNotFound
25 from pyramid.httpexceptions import HTTPNotFound
26 from pyramid.view import view_config
26 from pyramid.view import view_config
27
27
28 from rhodecode.apps._base import BaseAppView, DataGridAppView
28 from rhodecode.apps._base import BaseAppView, DataGridAppView
29 from rhodecode.lib import helpers as h
29 from rhodecode.lib import helpers as h
30 from rhodecode.lib.auth import (
30 from rhodecode.lib.auth import (
31 LoginRequired, NotAnonymous, HasRepoGroupPermissionAnyDecorator, CSRFRequired,
31 LoginRequired, NotAnonymous, HasRepoGroupPermissionAnyDecorator, CSRFRequired,
32 HasRepoGroupPermissionAny)
32 HasRepoGroupPermissionAny)
33 from rhodecode.lib.codeblocks import filenode_as_lines_tokens
33 from rhodecode.lib.codeblocks import filenode_as_lines_tokens
34 from rhodecode.lib.index import searcher_from_config
34 from rhodecode.lib.index import searcher_from_config
35 from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int
35 from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int
36 from rhodecode.lib.vcs.nodes import FileNode
36 from rhodecode.lib.vcs.nodes import FileNode
37 from rhodecode.model.db import (
37 from rhodecode.model.db import (
38 func, true, or_, case, in_filter_generator, Session,
38 func, true, or_, case, in_filter_generator, Session,
39 Repository, RepoGroup, User, UserGroup)
39 Repository, RepoGroup, User, UserGroup)
40 from rhodecode.model.repo import RepoModel
40 from rhodecode.model.repo import RepoModel
41 from rhodecode.model.repo_group import RepoGroupModel
41 from rhodecode.model.repo_group import RepoGroupModel
42 from rhodecode.model.user import UserModel
42 from rhodecode.model.user import UserModel
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 HomeView(BaseAppView, DataGridAppView):
48 class HomeView(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 c.user = c.auth_user.get_instance()
52 c.user = c.auth_user.get_instance()
53
53
54 return c
54 return c
55
55
56 @LoginRequired()
56 @LoginRequired()
57 @view_config(
57 @view_config(
58 route_name='user_autocomplete_data', request_method='GET',
58 route_name='user_autocomplete_data', request_method='GET',
59 renderer='json_ext', xhr=True)
59 renderer='json_ext', xhr=True)
60 def user_autocomplete_data(self):
60 def user_autocomplete_data(self):
61 self.load_default_context()
61 self.load_default_context()
62 query = self.request.GET.get('query')
62 query = self.request.GET.get('query')
63 active = str2bool(self.request.GET.get('active') or True)
63 active = str2bool(self.request.GET.get('active') or True)
64 include_groups = str2bool(self.request.GET.get('user_groups'))
64 include_groups = str2bool(self.request.GET.get('user_groups'))
65 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
65 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
66 skip_default_user = str2bool(self.request.GET.get('skip_default_user'))
66 skip_default_user = str2bool(self.request.GET.get('skip_default_user'))
67
67
68 log.debug('generating user list, query:%s, active:%s, with_groups:%s',
68 log.debug('generating user list, query:%s, active:%s, with_groups:%s',
69 query, active, include_groups)
69 query, active, include_groups)
70
70
71 _users = UserModel().get_users(
71 _users = UserModel().get_users(
72 name_contains=query, only_active=active)
72 name_contains=query, only_active=active)
73
73
74 def maybe_skip_default_user(usr):
74 def maybe_skip_default_user(usr):
75 if skip_default_user and usr['username'] == UserModel.cls.DEFAULT_USER:
75 if skip_default_user and usr['username'] == UserModel.cls.DEFAULT_USER:
76 return False
76 return False
77 return True
77 return True
78 _users = filter(maybe_skip_default_user, _users)
78 _users = filter(maybe_skip_default_user, _users)
79
79
80 if include_groups:
80 if include_groups:
81 # extend with user groups
81 # extend with user groups
82 _user_groups = UserGroupModel().get_user_groups(
82 _user_groups = UserGroupModel().get_user_groups(
83 name_contains=query, only_active=active,
83 name_contains=query, only_active=active,
84 expand_groups=expand_groups)
84 expand_groups=expand_groups)
85 _users = _users + _user_groups
85 _users = _users + _user_groups
86
86
87 return {'suggestions': _users}
87 return {'suggestions': _users}
88
88
89 @LoginRequired()
89 @LoginRequired()
90 @NotAnonymous()
90 @NotAnonymous()
91 @view_config(
91 @view_config(
92 route_name='user_group_autocomplete_data', request_method='GET',
92 route_name='user_group_autocomplete_data', request_method='GET',
93 renderer='json_ext', xhr=True)
93 renderer='json_ext', xhr=True)
94 def user_group_autocomplete_data(self):
94 def user_group_autocomplete_data(self):
95 self.load_default_context()
95 self.load_default_context()
96 query = self.request.GET.get('query')
96 query = self.request.GET.get('query')
97 active = str2bool(self.request.GET.get('active') or True)
97 active = str2bool(self.request.GET.get('active') or True)
98 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
98 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
99
99
100 log.debug('generating user group list, query:%s, active:%s',
100 log.debug('generating user group list, query:%s, active:%s',
101 query, active)
101 query, active)
102
102
103 _user_groups = UserGroupModel().get_user_groups(
103 _user_groups = UserGroupModel().get_user_groups(
104 name_contains=query, only_active=active,
104 name_contains=query, only_active=active,
105 expand_groups=expand_groups)
105 expand_groups=expand_groups)
106 _user_groups = _user_groups
106 _user_groups = _user_groups
107
107
108 return {'suggestions': _user_groups}
108 return {'suggestions': _user_groups}
109
109
110 def _get_repo_list(self, name_contains=None, repo_type=None, repo_group_name='', limit=20):
110 def _get_repo_list(self, name_contains=None, repo_type=None, repo_group_name='', limit=20):
111 org_query = name_contains
111 org_query = name_contains
112 allowed_ids = self._rhodecode_user.repo_acl_ids(
112 allowed_ids = self._rhodecode_user.repo_acl_ids(
113 ['repository.read', 'repository.write', 'repository.admin'],
113 ['repository.read', 'repository.write', 'repository.admin'],
114 cache=False, name_filter=name_contains) or [-1]
114 cache=False, name_filter=name_contains) or [-1]
115
115
116 query = Repository.query()\
116 query = Repository.query()\
117 .filter(Repository.archived.isnot(true()))\
117 .filter(Repository.archived.isnot(true()))\
118 .filter(or_(
118 .filter(or_(
119 # generate multiple IN to fix limitation problems
119 # generate multiple IN to fix limitation problems
120 *in_filter_generator(Repository.repo_id, allowed_ids)
120 *in_filter_generator(Repository.repo_id, allowed_ids)
121 ))
121 ))
122
122
123 query = query.order_by(case(
123 query = query.order_by(case(
124 [
124 [
125 (Repository.repo_name.startswith(repo_group_name), repo_group_name+'/'),
125 (Repository.repo_name.startswith(repo_group_name), repo_group_name+'/'),
126 ],
126 ],
127 ))
127 ))
128 query = query.order_by(func.length(Repository.repo_name))
128 query = query.order_by(func.length(Repository.repo_name))
129 query = query.order_by(Repository.repo_name)
129 query = query.order_by(Repository.repo_name)
130
130
131 if repo_type:
131 if repo_type:
132 query = query.filter(Repository.repo_type == repo_type)
132 query = query.filter(Repository.repo_type == repo_type)
133
133
134 if name_contains:
134 if name_contains:
135 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
135 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
136 query = query.filter(
136 query = query.filter(
137 Repository.repo_name.ilike(ilike_expression))
137 Repository.repo_name.ilike(ilike_expression))
138 query = query.limit(limit)
138 query = query.limit(limit)
139
139
140 acl_iter = query
140 acl_iter = query
141
141
142 return [
142 return [
143 {
143 {
144 'id': obj.repo_name,
144 'id': obj.repo_name,
145 'value': org_query,
145 'value': org_query,
146 'value_display': obj.repo_name,
146 'value_display': obj.repo_name,
147 'text': obj.repo_name,
147 'text': obj.repo_name,
148 'type': 'repo',
148 'type': 'repo',
149 'repo_id': obj.repo_id,
149 'repo_id': obj.repo_id,
150 'repo_type': obj.repo_type,
150 'repo_type': obj.repo_type,
151 'private': obj.private,
151 'private': obj.private,
152 'url': h.route_path('repo_summary', repo_name=obj.repo_name)
152 'url': h.route_path('repo_summary', repo_name=obj.repo_name)
153 }
153 }
154 for obj in acl_iter]
154 for obj in acl_iter]
155
155
156 def _get_repo_group_list(self, name_contains=None, repo_group_name='', limit=20):
156 def _get_repo_group_list(self, name_contains=None, repo_group_name='', limit=20):
157 org_query = name_contains
157 org_query = name_contains
158 allowed_ids = self._rhodecode_user.repo_group_acl_ids(
158 allowed_ids = self._rhodecode_user.repo_group_acl_ids(
159 ['group.read', 'group.write', 'group.admin'],
159 ['group.read', 'group.write', 'group.admin'],
160 cache=False, name_filter=name_contains) or [-1]
160 cache=False, name_filter=name_contains) or [-1]
161
161
162 query = RepoGroup.query()\
162 query = RepoGroup.query()\
163 .filter(or_(
163 .filter(or_(
164 # generate multiple IN to fix limitation problems
164 # generate multiple IN to fix limitation problems
165 *in_filter_generator(RepoGroup.group_id, allowed_ids)
165 *in_filter_generator(RepoGroup.group_id, allowed_ids)
166 ))
166 ))
167
167
168 query = query.order_by(case(
168 query = query.order_by(case(
169 [
169 [
170 (RepoGroup.group_name.startswith(repo_group_name), repo_group_name+'/'),
170 (RepoGroup.group_name.startswith(repo_group_name), repo_group_name+'/'),
171 ],
171 ],
172 ))
172 ))
173 query = query.order_by(func.length(RepoGroup.group_name))
173 query = query.order_by(func.length(RepoGroup.group_name))
174 query = query.order_by(RepoGroup.group_name)
174 query = query.order_by(RepoGroup.group_name)
175
175
176 if name_contains:
176 if name_contains:
177 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
177 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
178 query = query.filter(
178 query = query.filter(
179 RepoGroup.group_name.ilike(ilike_expression))
179 RepoGroup.group_name.ilike(ilike_expression))
180 query = query.limit(limit)
180 query = query.limit(limit)
181
181
182 acl_iter = query
182 acl_iter = query
183
183
184 return [
184 return [
185 {
185 {
186 'id': obj.group_name,
186 'id': obj.group_name,
187 'value': org_query,
187 'value': org_query,
188 'value_display': obj.group_name,
188 'value_display': obj.group_name,
189 'text': obj.group_name,
189 'text': obj.group_name,
190 'type': 'repo_group',
190 'type': 'repo_group',
191 'repo_group_id': obj.group_id,
191 'repo_group_id': obj.group_id,
192 'url': h.route_path(
192 'url': h.route_path(
193 'repo_group_home', repo_group_name=obj.group_name)
193 'repo_group_home', repo_group_name=obj.group_name)
194 }
194 }
195 for obj in acl_iter]
195 for obj in acl_iter]
196
196
197 def _get_user_list(self, name_contains=None, limit=20):
197 def _get_user_list(self, name_contains=None, limit=20):
198 org_query = name_contains
198 org_query = name_contains
199 if not name_contains:
199 if not name_contains:
200 return [], False
200 return [], False
201
201
202 # TODO(marcink): should all logged in users be allowed to search others?
202 # TODO(marcink): should all logged in users be allowed to search others?
203 allowed_user_search = self._rhodecode_user.username != User.DEFAULT_USER
203 allowed_user_search = self._rhodecode_user.username != User.DEFAULT_USER
204 if not allowed_user_search:
204 if not allowed_user_search:
205 return [], False
205 return [], False
206
206
207 name_contains = re.compile('(?:user:[ ]?)(.+)').findall(name_contains)
207 name_contains = re.compile('(?:user:[ ]?)(.+)').findall(name_contains)
208 if len(name_contains) != 1:
208 if len(name_contains) != 1:
209 return [], False
209 return [], False
210
210
211 name_contains = name_contains[0]
211 name_contains = name_contains[0]
212
212
213 query = User.query()\
213 query = User.query()\
214 .order_by(func.length(User.username))\
214 .order_by(func.length(User.username))\
215 .order_by(User.username) \
215 .order_by(User.username) \
216 .filter(User.username != User.DEFAULT_USER)
216 .filter(User.username != User.DEFAULT_USER)
217
217
218 if name_contains:
218 if name_contains:
219 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
219 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
220 query = query.filter(
220 query = query.filter(
221 User.username.ilike(ilike_expression))
221 User.username.ilike(ilike_expression))
222 query = query.limit(limit)
222 query = query.limit(limit)
223
223
224 acl_iter = query
224 acl_iter = query
225
225
226 return [
226 return [
227 {
227 {
228 'id': obj.user_id,
228 'id': obj.user_id,
229 'value': org_query,
229 'value': org_query,
230 'value_display': 'user: `{}`'.format(obj.username),
230 'value_display': 'user: `{}`'.format(obj.username),
231 'type': 'user',
231 'type': 'user',
232 'icon_link': h.gravatar_url(obj.email, 30),
232 'icon_link': h.gravatar_url(obj.email, 30),
233 'url': h.route_path(
233 'url': h.route_path(
234 'user_profile', username=obj.username)
234 'user_profile', username=obj.username)
235 }
235 }
236 for obj in acl_iter], True
236 for obj in acl_iter], True
237
237
238 def _get_user_groups_list(self, name_contains=None, limit=20):
238 def _get_user_groups_list(self, name_contains=None, limit=20):
239 org_query = name_contains
239 org_query = name_contains
240 if not name_contains:
240 if not name_contains:
241 return [], False
241 return [], False
242
242
243 # TODO(marcink): should all logged in users be allowed to search others?
243 # TODO(marcink): should all logged in users be allowed to search others?
244 allowed_user_search = self._rhodecode_user.username != User.DEFAULT_USER
244 allowed_user_search = self._rhodecode_user.username != User.DEFAULT_USER
245 if not allowed_user_search:
245 if not allowed_user_search:
246 return [], False
246 return [], False
247
247
248 name_contains = re.compile('(?:user_group:[ ]?)(.+)').findall(name_contains)
248 name_contains = re.compile('(?:user_group:[ ]?)(.+)').findall(name_contains)
249 if len(name_contains) != 1:
249 if len(name_contains) != 1:
250 return [], False
250 return [], False
251
251
252 name_contains = name_contains[0]
252 name_contains = name_contains[0]
253
253
254 query = UserGroup.query()\
254 query = UserGroup.query()\
255 .order_by(func.length(UserGroup.users_group_name))\
255 .order_by(func.length(UserGroup.users_group_name))\
256 .order_by(UserGroup.users_group_name)
256 .order_by(UserGroup.users_group_name)
257
257
258 if name_contains:
258 if name_contains:
259 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
259 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
260 query = query.filter(
260 query = query.filter(
261 UserGroup.users_group_name.ilike(ilike_expression))
261 UserGroup.users_group_name.ilike(ilike_expression))
262 query = query.limit(limit)
262 query = query.limit(limit)
263
263
264 acl_iter = query
264 acl_iter = query
265
265
266 return [
266 return [
267 {
267 {
268 'id': obj.users_group_id,
268 'id': obj.users_group_id,
269 'value': org_query,
269 'value': org_query,
270 'value_display': 'user_group: `{}`'.format(obj.users_group_name),
270 'value_display': 'user_group: `{}`'.format(obj.users_group_name),
271 'type': 'user_group',
271 'type': 'user_group',
272 'url': h.route_path(
272 'url': h.route_path(
273 'user_group_profile', user_group_name=obj.users_group_name)
273 'user_group_profile', user_group_name=obj.users_group_name)
274 }
274 }
275 for obj in acl_iter], True
275 for obj in acl_iter], True
276
276
277 def _get_hash_commit_list(self, auth_user, searcher, query, repo=None, repo_group=None):
277 def _get_hash_commit_list(self, auth_user, searcher, query, repo=None, repo_group=None):
278 repo_name = repo_group_name = None
278 repo_name = repo_group_name = None
279 if repo:
279 if repo:
280 repo_name = repo.repo_name
280 repo_name = repo.repo_name
281 if repo_group:
281 if repo_group:
282 repo_group_name = repo_group.group_name
282 repo_group_name = repo_group.group_name
283
283
284 org_query = query
284 org_query = query
285 if not query or len(query) < 3 or not searcher:
285 if not query or len(query) < 3 or not searcher:
286 return [], False
286 return [], False
287
287
288 commit_hashes = re.compile('(?:commit:[ ]?)([0-9a-f]{2,40})').findall(query)
288 commit_hashes = re.compile('(?:commit:[ ]?)([0-9a-f]{2,40})').findall(query)
289
289
290 if len(commit_hashes) != 1:
290 if len(commit_hashes) != 1:
291 return [], False
291 return [], False
292
292
293 commit_hash = commit_hashes[0]
293 commit_hash = commit_hashes[0]
294
294
295 result = searcher.search(
295 result = searcher.search(
296 'commit_id:{}*'.format(commit_hash), 'commit', auth_user,
296 'commit_id:{}*'.format(commit_hash), 'commit', auth_user,
297 repo_name, repo_group_name, raise_on_exc=False)
297 repo_name, repo_group_name, raise_on_exc=False)
298
298
299 commits = []
299 commits = []
300 for entry in result['results']:
300 for entry in result['results']:
301 repo_data = {
301 repo_data = {
302 'repository_id': entry.get('repository_id'),
302 'repository_id': entry.get('repository_id'),
303 'repository_type': entry.get('repo_type'),
303 'repository_type': entry.get('repo_type'),
304 'repository_name': entry.get('repository'),
304 'repository_name': entry.get('repository'),
305 }
305 }
306
306
307 commit_entry = {
307 commit_entry = {
308 'id': entry['commit_id'],
308 'id': entry['commit_id'],
309 'value': org_query,
309 'value': org_query,
310 'value_display': '`{}` commit: {}'.format(
310 'value_display': '`{}` commit: {}'.format(
311 entry['repository'], entry['commit_id']),
311 entry['repository'], entry['commit_id']),
312 'type': 'commit',
312 'type': 'commit',
313 'repo': entry['repository'],
313 'repo': entry['repository'],
314 'repo_data': repo_data,
314 'repo_data': repo_data,
315
315
316 'url': h.route_path(
316 'url': h.route_path(
317 'repo_commit',
317 'repo_commit',
318 repo_name=entry['repository'], commit_id=entry['commit_id'])
318 repo_name=entry['repository'], commit_id=entry['commit_id'])
319 }
319 }
320
320
321 commits.append(commit_entry)
321 commits.append(commit_entry)
322 return commits, True
322 return commits, True
323
323
324 def _get_path_list(self, auth_user, searcher, query, repo=None, repo_group=None):
324 def _get_path_list(self, auth_user, searcher, query, repo=None, repo_group=None):
325 repo_name = repo_group_name = None
325 repo_name = repo_group_name = None
326 if repo:
326 if repo:
327 repo_name = repo.repo_name
327 repo_name = repo.repo_name
328 if repo_group:
328 if repo_group:
329 repo_group_name = repo_group.group_name
329 repo_group_name = repo_group.group_name
330
330
331 org_query = query
331 org_query = query
332 if not query or len(query) < 3 or not searcher:
332 if not query or len(query) < 3 or not searcher:
333 return [], False
333 return [], False
334
334
335 paths_re = re.compile('(?:file:[ ]?)(.+)').findall(query)
335 paths_re = re.compile('(?:file:[ ]?)(.+)').findall(query)
336 if len(paths_re) != 1:
336 if len(paths_re) != 1:
337 return [], False
337 return [], False
338
338
339 file_path = paths_re[0]
339 file_path = paths_re[0]
340
340
341 search_path = searcher.escape_specials(file_path)
341 search_path = searcher.escape_specials(file_path)
342 result = searcher.search(
342 result = searcher.search(
343 'file.raw:*{}*'.format(search_path), 'path', auth_user,
343 'file.raw:*{}*'.format(search_path), 'path', auth_user,
344 repo_name, repo_group_name, raise_on_exc=False)
344 repo_name, repo_group_name, raise_on_exc=False)
345
345
346 files = []
346 files = []
347 for entry in result['results']:
347 for entry in result['results']:
348 repo_data = {
348 repo_data = {
349 'repository_id': entry.get('repository_id'),
349 'repository_id': entry.get('repository_id'),
350 'repository_type': entry.get('repo_type'),
350 'repository_type': entry.get('repo_type'),
351 'repository_name': entry.get('repository'),
351 'repository_name': entry.get('repository'),
352 }
352 }
353
353
354 file_entry = {
354 file_entry = {
355 'id': entry['commit_id'],
355 'id': entry['commit_id'],
356 'value': org_query,
356 'value': org_query,
357 'value_display': '`{}` file: {}'.format(
357 'value_display': '`{}` file: {}'.format(
358 entry['repository'], entry['file']),
358 entry['repository'], entry['file']),
359 'type': 'file',
359 'type': 'file',
360 'repo': entry['repository'],
360 'repo': entry['repository'],
361 'repo_data': repo_data,
361 'repo_data': repo_data,
362
362
363 'url': h.route_path(
363 'url': h.route_path(
364 'repo_files',
364 'repo_files',
365 repo_name=entry['repository'], commit_id=entry['commit_id'],
365 repo_name=entry['repository'], commit_id=entry['commit_id'],
366 f_path=entry['file'])
366 f_path=entry['file'])
367 }
367 }
368
368
369 files.append(file_entry)
369 files.append(file_entry)
370 return files, True
370 return files, True
371
371
372 @LoginRequired()
372 @LoginRequired()
373 @view_config(
373 @view_config(
374 route_name='repo_list_data', request_method='GET',
374 route_name='repo_list_data', request_method='GET',
375 renderer='json_ext', xhr=True)
375 renderer='json_ext', xhr=True)
376 def repo_list_data(self):
376 def repo_list_data(self):
377 _ = self.request.translate
377 _ = self.request.translate
378 self.load_default_context()
378 self.load_default_context()
379
379
380 query = self.request.GET.get('query')
380 query = self.request.GET.get('query')
381 repo_type = self.request.GET.get('repo_type')
381 repo_type = self.request.GET.get('repo_type')
382 log.debug('generating repo list, query:%s, repo_type:%s',
382 log.debug('generating repo list, query:%s, repo_type:%s',
383 query, repo_type)
383 query, repo_type)
384
384
385 res = []
385 res = []
386 repos = self._get_repo_list(query, repo_type=repo_type)
386 repos = self._get_repo_list(query, repo_type=repo_type)
387 if repos:
387 if repos:
388 res.append({
388 res.append({
389 'text': _('Repositories'),
389 'text': _('Repositories'),
390 'children': repos
390 'children': repos
391 })
391 })
392
392
393 data = {
393 data = {
394 'more': False,
394 'more': False,
395 'results': res
395 'results': res
396 }
396 }
397 return data
397 return data
398
398
399 @LoginRequired()
399 @LoginRequired()
400 @view_config(
400 @view_config(
401 route_name='repo_group_list_data', request_method='GET',
401 route_name='repo_group_list_data', request_method='GET',
402 renderer='json_ext', xhr=True)
402 renderer='json_ext', xhr=True)
403 def repo_group_list_data(self):
403 def repo_group_list_data(self):
404 _ = self.request.translate
404 _ = self.request.translate
405 self.load_default_context()
405 self.load_default_context()
406
406
407 query = self.request.GET.get('query')
407 query = self.request.GET.get('query')
408
408
409 log.debug('generating repo group list, query:%s',
409 log.debug('generating repo group list, query:%s',
410 query)
410 query)
411
411
412 res = []
412 res = []
413 repo_groups = self._get_repo_group_list(query)
413 repo_groups = self._get_repo_group_list(query)
414 if repo_groups:
414 if repo_groups:
415 res.append({
415 res.append({
416 'text': _('Repository Groups'),
416 'text': _('Repository Groups'),
417 'children': repo_groups
417 'children': repo_groups
418 })
418 })
419
419
420 data = {
420 data = {
421 'more': False,
421 'more': False,
422 'results': res
422 'results': res
423 }
423 }
424 return data
424 return data
425
425
426 def _get_default_search_queries(self, search_context, searcher, query):
426 def _get_default_search_queries(self, search_context, searcher, query):
427 if not searcher:
427 if not searcher:
428 return []
428 return []
429
429
430 is_es_6 = searcher.is_es_6
430 is_es_6 = searcher.is_es_6
431
431
432 queries = []
432 queries = []
433 repo_group_name, repo_name, repo_context = None, None, None
433 repo_group_name, repo_name, repo_context = None, None, None
434
434
435 # repo group context
435 # repo group context
436 if search_context.get('search_context[repo_group_name]'):
436 if search_context.get('search_context[repo_group_name]'):
437 repo_group_name = search_context.get('search_context[repo_group_name]')
437 repo_group_name = search_context.get('search_context[repo_group_name]')
438 if search_context.get('search_context[repo_name]'):
438 if search_context.get('search_context[repo_name]'):
439 repo_name = search_context.get('search_context[repo_name]')
439 repo_name = search_context.get('search_context[repo_name]')
440 repo_context = search_context.get('search_context[repo_view_type]')
440 repo_context = search_context.get('search_context[repo_view_type]')
441
441
442 if is_es_6 and repo_name:
442 if is_es_6 and repo_name:
443 # files
443 # files
444 def query_modifier():
444 def query_modifier():
445 qry = query
445 qry = query
446 return {'q': qry, 'type': 'content'}
446 return {'q': qry, 'type': 'content'}
447
447
448 label = u'File search for `{}`'.format(h.escape(query))
448 label = u'File search for `{}`'.format(h.escape(query))
449 file_qry = {
449 file_qry = {
450 'id': -10,
450 'id': -10,
451 'value': query,
451 'value': query,
452 'value_display': label,
452 'value_display': label,
453 'value_icon': '<i class="icon-code"></i>',
453 'value_icon': '<i class="icon-code"></i>',
454 'type': 'search',
454 'type': 'search',
455 'subtype': 'repo',
455 'subtype': 'repo',
456 'url': h.route_path('search_repo',
456 'url': h.route_path('search_repo',
457 repo_name=repo_name,
457 repo_name=repo_name,
458 _query=query_modifier())
458 _query=query_modifier())
459 }
459 }
460
460
461 # commits
461 # commits
462 def query_modifier():
462 def query_modifier():
463 qry = query
463 qry = query
464 return {'q': qry, 'type': 'commit'}
464 return {'q': qry, 'type': 'commit'}
465
465
466 label = u'Commit search for `{}`'.format(h.escape(query))
466 label = u'Commit search for `{}`'.format(h.escape(query))
467 commit_qry = {
467 commit_qry = {
468 'id': -20,
468 'id': -20,
469 'value': query,
469 'value': query,
470 'value_display': label,
470 'value_display': label,
471 'value_icon': '<i class="icon-history"></i>',
471 'value_icon': '<i class="icon-history"></i>',
472 'type': 'search',
472 'type': 'search',
473 'subtype': 'repo',
473 'subtype': 'repo',
474 'url': h.route_path('search_repo',
474 'url': h.route_path('search_repo',
475 repo_name=repo_name,
475 repo_name=repo_name,
476 _query=query_modifier())
476 _query=query_modifier())
477 }
477 }
478
478
479 if repo_context in ['commit', 'commits']:
479 if repo_context in ['commit', 'commits']:
480 queries.extend([commit_qry, file_qry])
480 queries.extend([commit_qry, file_qry])
481 elif repo_context in ['files', 'summary']:
481 elif repo_context in ['files', 'summary']:
482 queries.extend([file_qry, commit_qry])
482 queries.extend([file_qry, commit_qry])
483 else:
483 else:
484 queries.extend([commit_qry, file_qry])
484 queries.extend([commit_qry, file_qry])
485
485
486 elif is_es_6 and repo_group_name:
486 elif is_es_6 and repo_group_name:
487 # files
487 # files
488 def query_modifier():
488 def query_modifier():
489 qry = query
489 qry = query
490 return {'q': qry, 'type': 'content'}
490 return {'q': qry, 'type': 'content'}
491
491
492 label = u'File search for `{}`'.format(query)
492 label = u'File search for `{}`'.format(query)
493 file_qry = {
493 file_qry = {
494 'id': -30,
494 'id': -30,
495 'value': query,
495 'value': query,
496 'value_display': label,
496 'value_display': label,
497 'value_icon': '<i class="icon-code"></i>',
497 'value_icon': '<i class="icon-code"></i>',
498 'type': 'search',
498 'type': 'search',
499 'subtype': 'repo_group',
499 'subtype': 'repo_group',
500 'url': h.route_path('search_repo_group',
500 'url': h.route_path('search_repo_group',
501 repo_group_name=repo_group_name,
501 repo_group_name=repo_group_name,
502 _query=query_modifier())
502 _query=query_modifier())
503 }
503 }
504
504
505 # commits
505 # commits
506 def query_modifier():
506 def query_modifier():
507 qry = query
507 qry = query
508 return {'q': qry, 'type': 'commit'}
508 return {'q': qry, 'type': 'commit'}
509
509
510 label = u'Commit search for `{}`'.format(query)
510 label = u'Commit search for `{}`'.format(query)
511 commit_qry = {
511 commit_qry = {
512 'id': -40,
512 'id': -40,
513 'value': query,
513 'value': query,
514 'value_display': label,
514 'value_display': label,
515 'value_icon': '<i class="icon-history"></i>',
515 'value_icon': '<i class="icon-history"></i>',
516 'type': 'search',
516 'type': 'search',
517 'subtype': 'repo_group',
517 'subtype': 'repo_group',
518 'url': h.route_path('search_repo_group',
518 'url': h.route_path('search_repo_group',
519 repo_group_name=repo_group_name,
519 repo_group_name=repo_group_name,
520 _query=query_modifier())
520 _query=query_modifier())
521 }
521 }
522
522
523 if repo_context in ['commit', 'commits']:
523 if repo_context in ['commit', 'commits']:
524 queries.extend([commit_qry, file_qry])
524 queries.extend([commit_qry, file_qry])
525 elif repo_context in ['files', 'summary']:
525 elif repo_context in ['files', 'summary']:
526 queries.extend([file_qry, commit_qry])
526 queries.extend([file_qry, commit_qry])
527 else:
527 else:
528 queries.extend([commit_qry, file_qry])
528 queries.extend([commit_qry, file_qry])
529
529
530 # Global, not scoped
530 # Global, not scoped
531 if not queries:
531 if not queries:
532 queries.append(
532 queries.append(
533 {
533 {
534 'id': -1,
534 'id': -1,
535 'value': query,
535 'value': query,
536 'value_display': u'File search for: `{}`'.format(query),
536 'value_display': u'File search for: `{}`'.format(query),
537 'value_icon': '<i class="icon-code"></i>',
537 'value_icon': '<i class="icon-code"></i>',
538 'type': 'search',
538 'type': 'search',
539 'subtype': 'global',
539 'subtype': 'global',
540 'url': h.route_path('search',
540 'url': h.route_path('search',
541 _query={'q': query, 'type': 'content'})
541 _query={'q': query, 'type': 'content'})
542 })
542 })
543 queries.append(
543 queries.append(
544 {
544 {
545 'id': -2,
545 'id': -2,
546 'value': query,
546 'value': query,
547 'value_display': u'Commit search for: `{}`'.format(query),
547 'value_display': u'Commit search for: `{}`'.format(query),
548 'value_icon': '<i class="icon-history"></i>',
548 'value_icon': '<i class="icon-history"></i>',
549 'type': 'search',
549 'type': 'search',
550 'subtype': 'global',
550 'subtype': 'global',
551 'url': h.route_path('search',
551 'url': h.route_path('search',
552 _query={'q': query, 'type': 'commit'})
552 _query={'q': query, 'type': 'commit'})
553 })
553 })
554
554
555 return queries
555 return queries
556
556
557 @LoginRequired()
557 @LoginRequired()
558 @view_config(
558 @view_config(
559 route_name='goto_switcher_data', request_method='GET',
559 route_name='goto_switcher_data', request_method='GET',
560 renderer='json_ext', xhr=True)
560 renderer='json_ext', xhr=True)
561 def goto_switcher_data(self):
561 def goto_switcher_data(self):
562 c = self.load_default_context()
562 c = self.load_default_context()
563
563
564 _ = self.request.translate
564 _ = self.request.translate
565
565
566 query = self.request.GET.get('query')
566 query = self.request.GET.get('query')
567 log.debug('generating main filter data, query %s', query)
567 log.debug('generating main filter data, query %s', query)
568
568
569 res = []
569 res = []
570 if not query:
570 if not query:
571 return {'suggestions': res}
571 return {'suggestions': res}
572
572
573 def no_match(name):
573 def no_match(name):
574 return {
574 return {
575 'id': -1,
575 'id': -1,
576 'value': "",
576 'value': "",
577 'value_display': name,
577 'value_display': name,
578 'type': 'text',
578 'type': 'text',
579 'url': ""
579 'url': ""
580 }
580 }
581 searcher = searcher_from_config(self.request.registry.settings)
581 searcher = searcher_from_config(self.request.registry.settings)
582 has_specialized_search = False
582 has_specialized_search = False
583
583
584 # set repo context
584 # set repo context
585 repo = None
585 repo = None
586 repo_id = safe_int(self.request.GET.get('search_context[repo_id]'))
586 repo_id = safe_int(self.request.GET.get('search_context[repo_id]'))
587 if repo_id:
587 if repo_id:
588 repo = Repository.get(repo_id)
588 repo = Repository.get(repo_id)
589
589
590 # set group context
590 # set group context
591 repo_group = None
591 repo_group = None
592 repo_group_id = safe_int(self.request.GET.get('search_context[repo_group_id]'))
592 repo_group_id = safe_int(self.request.GET.get('search_context[repo_group_id]'))
593 if repo_group_id:
593 if repo_group_id:
594 repo_group = RepoGroup.get(repo_group_id)
594 repo_group = RepoGroup.get(repo_group_id)
595 prefix_match = False
595 prefix_match = False
596
596
597 # user: type search
597 # user: type search
598 if not prefix_match:
598 if not prefix_match:
599 users, prefix_match = self._get_user_list(query)
599 users, prefix_match = self._get_user_list(query)
600 if users:
600 if users:
601 has_specialized_search = True
601 has_specialized_search = True
602 for serialized_user in users:
602 for serialized_user in users:
603 res.append(serialized_user)
603 res.append(serialized_user)
604 elif prefix_match:
604 elif prefix_match:
605 has_specialized_search = True
605 has_specialized_search = True
606 res.append(no_match('No matching users found'))
606 res.append(no_match('No matching users found'))
607
607
608 # user_group: type search
608 # user_group: type search
609 if not prefix_match:
609 if not prefix_match:
610 user_groups, prefix_match = self._get_user_groups_list(query)
610 user_groups, prefix_match = self._get_user_groups_list(query)
611 if user_groups:
611 if user_groups:
612 has_specialized_search = True
612 has_specialized_search = True
613 for serialized_user_group in user_groups:
613 for serialized_user_group in user_groups:
614 res.append(serialized_user_group)
614 res.append(serialized_user_group)
615 elif prefix_match:
615 elif prefix_match:
616 has_specialized_search = True
616 has_specialized_search = True
617 res.append(no_match('No matching user groups found'))
617 res.append(no_match('No matching user groups found'))
618
618
619 # FTS commit: type search
619 # FTS commit: type search
620 if not prefix_match:
620 if not prefix_match:
621 commits, prefix_match = self._get_hash_commit_list(
621 commits, prefix_match = self._get_hash_commit_list(
622 c.auth_user, searcher, query, repo, repo_group)
622 c.auth_user, searcher, query, repo, repo_group)
623 if commits:
623 if commits:
624 has_specialized_search = True
624 has_specialized_search = True
625 unique_repos = collections.OrderedDict()
625 unique_repos = collections.OrderedDict()
626 for commit in commits:
626 for commit in commits:
627 repo_name = commit['repo']
627 repo_name = commit['repo']
628 unique_repos.setdefault(repo_name, []).append(commit)
628 unique_repos.setdefault(repo_name, []).append(commit)
629
629
630 for _repo, commits in unique_repos.items():
630 for _repo, commits in unique_repos.items():
631 for commit in commits:
631 for commit in commits:
632 res.append(commit)
632 res.append(commit)
633 elif prefix_match:
633 elif prefix_match:
634 has_specialized_search = True
634 has_specialized_search = True
635 res.append(no_match('No matching commits found'))
635 res.append(no_match('No matching commits found'))
636
636
637 # FTS file: type search
637 # FTS file: type search
638 if not prefix_match:
638 if not prefix_match:
639 paths, prefix_match = self._get_path_list(
639 paths, prefix_match = self._get_path_list(
640 c.auth_user, searcher, query, repo, repo_group)
640 c.auth_user, searcher, query, repo, repo_group)
641 if paths:
641 if paths:
642 has_specialized_search = True
642 has_specialized_search = True
643 unique_repos = collections.OrderedDict()
643 unique_repos = collections.OrderedDict()
644 for path in paths:
644 for path in paths:
645 repo_name = path['repo']
645 repo_name = path['repo']
646 unique_repos.setdefault(repo_name, []).append(path)
646 unique_repos.setdefault(repo_name, []).append(path)
647
647
648 for repo, paths in unique_repos.items():
648 for repo, paths in unique_repos.items():
649 for path in paths:
649 for path in paths:
650 res.append(path)
650 res.append(path)
651 elif prefix_match:
651 elif prefix_match:
652 has_specialized_search = True
652 has_specialized_search = True
653 res.append(no_match('No matching files found'))
653 res.append(no_match('No matching files found'))
654
654
655 # main suggestions
655 # main suggestions
656 if not has_specialized_search:
656 if not has_specialized_search:
657 repo_group_name = ''
657 repo_group_name = ''
658 if repo_group:
658 if repo_group:
659 repo_group_name = repo_group.group_name
659 repo_group_name = repo_group.group_name
660
660
661 for _q in self._get_default_search_queries(self.request.GET, searcher, query):
661 for _q in self._get_default_search_queries(self.request.GET, searcher, query):
662 res.append(_q)
662 res.append(_q)
663
663
664 repo_groups = self._get_repo_group_list(query, repo_group_name=repo_group_name)
664 repo_groups = self._get_repo_group_list(query, repo_group_name=repo_group_name)
665 for serialized_repo_group in repo_groups:
665 for serialized_repo_group in repo_groups:
666 res.append(serialized_repo_group)
666 res.append(serialized_repo_group)
667
667
668 repos = self._get_repo_list(query, repo_group_name=repo_group_name)
668 repos = self._get_repo_list(query, repo_group_name=repo_group_name)
669 for serialized_repo in repos:
669 for serialized_repo in repos:
670 res.append(serialized_repo)
670 res.append(serialized_repo)
671
671
672 if not repos and not repo_groups:
672 if not repos and not repo_groups:
673 res.append(no_match('No matches found'))
673 res.append(no_match('No matches found'))
674
674
675 return {'suggestions': res}
675 return {'suggestions': res}
676
676
677 @LoginRequired()
677 @LoginRequired()
678 @view_config(
678 @view_config(
679 route_name='home', request_method='GET',
679 route_name='home', request_method='GET',
680 renderer='rhodecode:templates/index.mako')
680 renderer='rhodecode:templates/index.mako')
681 def main_page(self):
681 def main_page(self):
682 c = self.load_default_context()
682 c = self.load_default_context()
683 c.repo_group = None
683 c.repo_group = None
684 return self._get_template_context(c)
684 return self._get_template_context(c)
685
685
686 def _main_page_repo_groups_data(self, repo_group_id):
686 def _main_page_repo_groups_data(self, repo_group_id):
687 column_map = {
687 column_map = {
688 'name_raw': 'group_name_hash',
688 'name': 'group_name_hash',
689 'desc': 'group_description',
689 'desc': 'group_description',
690 'last_change_raw': 'updated_on',
690 'last_change': 'updated_on',
691 'owner': 'user_username',
691 'owner': 'user_username',
692 }
692 }
693 draw, start, limit = self._extract_chunk(self.request)
693 draw, start, limit = self._extract_chunk(self.request)
694 search_q, order_by, order_dir = self._extract_ordering(
694 search_q, order_by, order_dir = self._extract_ordering(
695 self.request, column_map=column_map)
695 self.request, column_map=column_map)
696 return RepoGroupModel().get_repo_groups_data_table(
696 return RepoGroupModel().get_repo_groups_data_table(
697 draw, start, limit,
697 draw, start, limit,
698 search_q, order_by, order_dir,
698 search_q, order_by, order_dir,
699 self._rhodecode_user, repo_group_id)
699 self._rhodecode_user, repo_group_id)
700
700
701 def _main_page_repos_data(self, repo_group_id):
701 def _main_page_repos_data(self, repo_group_id):
702 column_map = {
702 column_map = {
703 'name_raw': 'repo_name',
703 'name': 'repo_name',
704 'desc': 'description',
704 'desc': 'description',
705 'last_change_raw': 'updated_on',
705 'last_change': 'updated_on',
706 'owner': 'user_username',
706 'owner': 'user_username',
707 }
707 }
708 draw, start, limit = self._extract_chunk(self.request)
708 draw, start, limit = self._extract_chunk(self.request)
709 search_q, order_by, order_dir = self._extract_ordering(
709 search_q, order_by, order_dir = self._extract_ordering(
710 self.request, column_map=column_map)
710 self.request, column_map=column_map)
711 return RepoModel().get_repos_data_table(
711 return RepoModel().get_repos_data_table(
712 draw, start, limit,
712 draw, start, limit,
713 search_q, order_by, order_dir,
713 search_q, order_by, order_dir,
714 self._rhodecode_user, repo_group_id)
714 self._rhodecode_user, repo_group_id)
715
715
716 @LoginRequired()
716 @LoginRequired()
717 @view_config(
717 @view_config(
718 route_name='main_page_repo_groups_data',
718 route_name='main_page_repo_groups_data',
719 request_method='GET', renderer='json_ext', xhr=True)
719 request_method='GET', renderer='json_ext', xhr=True)
720 def main_page_repo_groups_data(self):
720 def main_page_repo_groups_data(self):
721 self.load_default_context()
721 self.load_default_context()
722 repo_group_id = safe_int(self.request.GET.get('repo_group_id'))
722 repo_group_id = safe_int(self.request.GET.get('repo_group_id'))
723
723
724 if repo_group_id:
724 if repo_group_id:
725 group = RepoGroup.get_or_404(repo_group_id)
725 group = RepoGroup.get_or_404(repo_group_id)
726 _perms = ['group.read', 'group.write', 'group.admin']
726 _perms = ['group.read', 'group.write', 'group.admin']
727 if not HasRepoGroupPermissionAny(*_perms)(
727 if not HasRepoGroupPermissionAny(*_perms)(
728 group.group_name, 'user is allowed to list repo group children'):
728 group.group_name, 'user is allowed to list repo group children'):
729 raise HTTPNotFound()
729 raise HTTPNotFound()
730
730
731 return self._main_page_repo_groups_data(repo_group_id)
731 return self._main_page_repo_groups_data(repo_group_id)
732
732
733 @LoginRequired()
733 @LoginRequired()
734 @view_config(
734 @view_config(
735 route_name='main_page_repos_data',
735 route_name='main_page_repos_data',
736 request_method='GET', renderer='json_ext', xhr=True)
736 request_method='GET', renderer='json_ext', xhr=True)
737 def main_page_repos_data(self):
737 def main_page_repos_data(self):
738 self.load_default_context()
738 self.load_default_context()
739 repo_group_id = safe_int(self.request.GET.get('repo_group_id'))
739 repo_group_id = safe_int(self.request.GET.get('repo_group_id'))
740
740
741 if repo_group_id:
741 if repo_group_id:
742 group = RepoGroup.get_or_404(repo_group_id)
742 group = RepoGroup.get_or_404(repo_group_id)
743 _perms = ['group.read', 'group.write', 'group.admin']
743 _perms = ['group.read', 'group.write', 'group.admin']
744 if not HasRepoGroupPermissionAny(*_perms)(
744 if not HasRepoGroupPermissionAny(*_perms)(
745 group.group_name, 'user is allowed to list repo group children'):
745 group.group_name, 'user is allowed to list repo group children'):
746 raise HTTPNotFound()
746 raise HTTPNotFound()
747
747
748 return self._main_page_repos_data(repo_group_id)
748 return self._main_page_repos_data(repo_group_id)
749
749
750 @LoginRequired()
750 @LoginRequired()
751 @HasRepoGroupPermissionAnyDecorator(
751 @HasRepoGroupPermissionAnyDecorator(
752 'group.read', 'group.write', 'group.admin')
752 'group.read', 'group.write', 'group.admin')
753 @view_config(
753 @view_config(
754 route_name='repo_group_home', request_method='GET',
754 route_name='repo_group_home', request_method='GET',
755 renderer='rhodecode:templates/index_repo_group.mako')
755 renderer='rhodecode:templates/index_repo_group.mako')
756 @view_config(
756 @view_config(
757 route_name='repo_group_home_slash', request_method='GET',
757 route_name='repo_group_home_slash', request_method='GET',
758 renderer='rhodecode:templates/index_repo_group.mako')
758 renderer='rhodecode:templates/index_repo_group.mako')
759 def repo_group_main_page(self):
759 def repo_group_main_page(self):
760 c = self.load_default_context()
760 c = self.load_default_context()
761 c.repo_group = self.request.db_repo_group
761 c.repo_group = self.request.db_repo_group
762 return self._get_template_context(c)
762 return self._get_template_context(c)
763
763
764 @LoginRequired()
764 @LoginRequired()
765 @CSRFRequired()
765 @CSRFRequired()
766 @view_config(
766 @view_config(
767 route_name='markup_preview', request_method='POST',
767 route_name='markup_preview', request_method='POST',
768 renderer='string', xhr=True)
768 renderer='string', xhr=True)
769 def markup_preview(self):
769 def markup_preview(self):
770 # Technically a CSRF token is not needed as no state changes with this
770 # Technically a CSRF token is not needed as no state changes with this
771 # call. However, as this is a POST is better to have it, so automated
771 # call. However, as this is a POST is better to have it, so automated
772 # tools don't flag it as potential CSRF.
772 # tools don't flag it as potential CSRF.
773 # Post is required because the payload could be bigger than the maximum
773 # Post is required because the payload could be bigger than the maximum
774 # allowed by GET.
774 # allowed by GET.
775
775
776 text = self.request.POST.get('text')
776 text = self.request.POST.get('text')
777 renderer = self.request.POST.get('renderer') or 'rst'
777 renderer = self.request.POST.get('renderer') or 'rst'
778 if text:
778 if text:
779 return h.render(text, renderer=renderer, mentions=True)
779 return h.render(text, renderer=renderer, mentions=True)
780 return ''
780 return ''
781
781
782 @LoginRequired()
782 @LoginRequired()
783 @CSRFRequired()
783 @CSRFRequired()
784 @view_config(
784 @view_config(
785 route_name='file_preview', request_method='POST',
785 route_name='file_preview', request_method='POST',
786 renderer='string', xhr=True)
786 renderer='string', xhr=True)
787 def file_preview(self):
787 def file_preview(self):
788 # Technically a CSRF token is not needed as no state changes with this
788 # Technically a CSRF token is not needed as no state changes with this
789 # call. However, as this is a POST is better to have it, so automated
789 # call. However, as this is a POST is better to have it, so automated
790 # tools don't flag it as potential CSRF.
790 # tools don't flag it as potential CSRF.
791 # Post is required because the payload could be bigger than the maximum
791 # Post is required because the payload could be bigger than the maximum
792 # allowed by GET.
792 # allowed by GET.
793
793
794 text = self.request.POST.get('text')
794 text = self.request.POST.get('text')
795 file_path = self.request.POST.get('file_path')
795 file_path = self.request.POST.get('file_path')
796
796
797 renderer = h.renderer_from_filename(file_path)
797 renderer = h.renderer_from_filename(file_path)
798
798
799 if renderer:
799 if renderer:
800 return h.render(text, renderer=renderer, mentions=True)
800 return h.render(text, renderer=renderer, mentions=True)
801 else:
801 else:
802 self.load_default_context()
802 self.load_default_context()
803 _render = self.request.get_partial_renderer(
803 _render = self.request.get_partial_renderer(
804 'rhodecode:templates/files/file_content.mako')
804 'rhodecode:templates/files/file_content.mako')
805
805
806 lines = filenode_as_lines_tokens(FileNode(file_path, text))
806 lines = filenode_as_lines_tokens(FileNode(file_path, text))
807
807
808 return _render('render_lines', lines)
808 return _render('render_lines', lines)
809
809
810 @LoginRequired()
810 @LoginRequired()
811 @CSRFRequired()
811 @CSRFRequired()
812 @view_config(
812 @view_config(
813 route_name='store_user_session_value', request_method='POST',
813 route_name='store_user_session_value', request_method='POST',
814 renderer='string', xhr=True)
814 renderer='string', xhr=True)
815 def store_user_session_attr(self):
815 def store_user_session_attr(self):
816 key = self.request.POST.get('key')
816 key = self.request.POST.get('key')
817 val = self.request.POST.get('val')
817 val = self.request.POST.get('val')
818
818
819 existing_value = self.request.session.get(key)
819 existing_value = self.request.session.get(key)
820 if existing_value != val:
820 if existing_value != val:
821 self.request.session[key] = val
821 self.request.session[key] = val
822
822
823 return 'stored:{}:{}'.format(key, val)
823 return 'stored:{}:{}'.format(key, val)
@@ -1,1160 +1,1158 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2019 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 os
21 import os
22 import re
22 import re
23 import shutil
23 import shutil
24 import time
24 import time
25 import logging
25 import logging
26 import traceback
26 import traceback
27 import datetime
27 import datetime
28
28
29 from pyramid.threadlocal import get_current_request
29 from pyramid.threadlocal import get_current_request
30 from zope.cachedescriptors.property import Lazy as LazyProperty
30 from zope.cachedescriptors.property import Lazy as LazyProperty
31
31
32 from rhodecode import events
32 from rhodecode import events
33 from rhodecode.lib.auth import HasUserGroupPermissionAny
33 from rhodecode.lib.auth import HasUserGroupPermissionAny
34 from rhodecode.lib.caching_query import FromCache
34 from rhodecode.lib.caching_query import FromCache
35 from rhodecode.lib.exceptions import AttachedForksError, AttachedPullRequestsError
35 from rhodecode.lib.exceptions import AttachedForksError, AttachedPullRequestsError
36 from rhodecode.lib.hooks_base import log_delete_repository
36 from rhodecode.lib.hooks_base import log_delete_repository
37 from rhodecode.lib.user_log_filter import user_log_filter
37 from rhodecode.lib.user_log_filter import user_log_filter
38 from rhodecode.lib.utils import make_db_config
38 from rhodecode.lib.utils import make_db_config
39 from rhodecode.lib.utils2 import (
39 from rhodecode.lib.utils2 import (
40 safe_str, safe_unicode, remove_prefix, obfuscate_url_pw,
40 safe_str, safe_unicode, remove_prefix, obfuscate_url_pw,
41 get_current_rhodecode_user, safe_int, datetime_to_time,
41 get_current_rhodecode_user, safe_int, datetime_to_time,
42 action_logger_generic)
42 action_logger_generic)
43 from rhodecode.lib.vcs.backends import get_backend
43 from rhodecode.lib.vcs.backends import get_backend
44 from rhodecode.model import BaseModel
44 from rhodecode.model import BaseModel
45 from rhodecode.model.db import (
45 from rhodecode.model.db import (
46 _hash_key, func, case, joinedload, or_, in_filter_generator,
46 _hash_key, func, case, joinedload, or_, in_filter_generator,
47 Session, Repository, UserRepoToPerm, UserGroupRepoToPerm,
47 Session, Repository, UserRepoToPerm, UserGroupRepoToPerm,
48 UserRepoGroupToPerm, UserGroupRepoGroupToPerm, User, Permission,
48 UserRepoGroupToPerm, UserGroupRepoGroupToPerm, User, Permission,
49 Statistics, UserGroup, RepoGroup, RepositoryField, UserLog)
49 Statistics, UserGroup, RepoGroup, RepositoryField, UserLog)
50 from rhodecode.model.settings import VcsSettingsModel
50 from rhodecode.model.settings import VcsSettingsModel
51
51
52 log = logging.getLogger(__name__)
52 log = logging.getLogger(__name__)
53
53
54
54
55 class RepoModel(BaseModel):
55 class RepoModel(BaseModel):
56
56
57 cls = Repository
57 cls = Repository
58
58
59 def _get_user_group(self, users_group):
59 def _get_user_group(self, users_group):
60 return self._get_instance(UserGroup, users_group,
60 return self._get_instance(UserGroup, users_group,
61 callback=UserGroup.get_by_group_name)
61 callback=UserGroup.get_by_group_name)
62
62
63 def _get_repo_group(self, repo_group):
63 def _get_repo_group(self, repo_group):
64 return self._get_instance(RepoGroup, repo_group,
64 return self._get_instance(RepoGroup, repo_group,
65 callback=RepoGroup.get_by_group_name)
65 callback=RepoGroup.get_by_group_name)
66
66
67 def _create_default_perms(self, repository, private):
67 def _create_default_perms(self, repository, private):
68 # create default permission
68 # create default permission
69 default = 'repository.read'
69 default = 'repository.read'
70 def_user = User.get_default_user()
70 def_user = User.get_default_user()
71 for p in def_user.user_perms:
71 for p in def_user.user_perms:
72 if p.permission.permission_name.startswith('repository.'):
72 if p.permission.permission_name.startswith('repository.'):
73 default = p.permission.permission_name
73 default = p.permission.permission_name
74 break
74 break
75
75
76 default_perm = 'repository.none' if private else default
76 default_perm = 'repository.none' if private else default
77
77
78 repo_to_perm = UserRepoToPerm()
78 repo_to_perm = UserRepoToPerm()
79 repo_to_perm.permission = Permission.get_by_key(default_perm)
79 repo_to_perm.permission = Permission.get_by_key(default_perm)
80
80
81 repo_to_perm.repository = repository
81 repo_to_perm.repository = repository
82 repo_to_perm.user_id = def_user.user_id
82 repo_to_perm.user_id = def_user.user_id
83
83
84 return repo_to_perm
84 return repo_to_perm
85
85
86 @LazyProperty
86 @LazyProperty
87 def repos_path(self):
87 def repos_path(self):
88 """
88 """
89 Gets the repositories root path from database
89 Gets the repositories root path from database
90 """
90 """
91 settings_model = VcsSettingsModel(sa=self.sa)
91 settings_model = VcsSettingsModel(sa=self.sa)
92 return settings_model.get_repos_location()
92 return settings_model.get_repos_location()
93
93
94 def get(self, repo_id):
94 def get(self, repo_id):
95 repo = self.sa.query(Repository) \
95 repo = self.sa.query(Repository) \
96 .filter(Repository.repo_id == repo_id)
96 .filter(Repository.repo_id == repo_id)
97
97
98 return repo.scalar()
98 return repo.scalar()
99
99
100 def get_repo(self, repository):
100 def get_repo(self, repository):
101 return self._get_repo(repository)
101 return self._get_repo(repository)
102
102
103 def get_by_repo_name(self, repo_name, cache=False):
103 def get_by_repo_name(self, repo_name, cache=False):
104 repo = self.sa.query(Repository) \
104 repo = self.sa.query(Repository) \
105 .filter(Repository.repo_name == repo_name)
105 .filter(Repository.repo_name == repo_name)
106
106
107 if cache:
107 if cache:
108 name_key = _hash_key(repo_name)
108 name_key = _hash_key(repo_name)
109 repo = repo.options(
109 repo = repo.options(
110 FromCache("sql_cache_short", "get_repo_%s" % name_key))
110 FromCache("sql_cache_short", "get_repo_%s" % name_key))
111 return repo.scalar()
111 return repo.scalar()
112
112
113 def _extract_id_from_repo_name(self, repo_name):
113 def _extract_id_from_repo_name(self, repo_name):
114 if repo_name.startswith('/'):
114 if repo_name.startswith('/'):
115 repo_name = repo_name.lstrip('/')
115 repo_name = repo_name.lstrip('/')
116 by_id_match = re.match(r'^_(\d{1,})', repo_name)
116 by_id_match = re.match(r'^_(\d{1,})', repo_name)
117 if by_id_match:
117 if by_id_match:
118 return by_id_match.groups()[0]
118 return by_id_match.groups()[0]
119
119
120 def get_repo_by_id(self, repo_name):
120 def get_repo_by_id(self, repo_name):
121 """
121 """
122 Extracts repo_name by id from special urls.
122 Extracts repo_name by id from special urls.
123 Example url is _11/repo_name
123 Example url is _11/repo_name
124
124
125 :param repo_name:
125 :param repo_name:
126 :return: repo object if matched else None
126 :return: repo object if matched else None
127 """
127 """
128
128
129 try:
129 try:
130 _repo_id = self._extract_id_from_repo_name(repo_name)
130 _repo_id = self._extract_id_from_repo_name(repo_name)
131 if _repo_id:
131 if _repo_id:
132 return self.get(_repo_id)
132 return self.get(_repo_id)
133 except Exception:
133 except Exception:
134 log.exception('Failed to extract repo_name from URL')
134 log.exception('Failed to extract repo_name from URL')
135
135
136 return None
136 return None
137
137
138 def get_repos_for_root(self, root, traverse=False):
138 def get_repos_for_root(self, root, traverse=False):
139 if traverse:
139 if traverse:
140 like_expression = u'{}%'.format(safe_unicode(root))
140 like_expression = u'{}%'.format(safe_unicode(root))
141 repos = Repository.query().filter(
141 repos = Repository.query().filter(
142 Repository.repo_name.like(like_expression)).all()
142 Repository.repo_name.like(like_expression)).all()
143 else:
143 else:
144 if root and not isinstance(root, RepoGroup):
144 if root and not isinstance(root, RepoGroup):
145 raise ValueError(
145 raise ValueError(
146 'Root must be an instance '
146 'Root must be an instance '
147 'of RepoGroup, got:{} instead'.format(type(root)))
147 'of RepoGroup, got:{} instead'.format(type(root)))
148 repos = Repository.query().filter(Repository.group == root).all()
148 repos = Repository.query().filter(Repository.group == root).all()
149 return repos
149 return repos
150
150
151 def get_url(self, repo, request=None, permalink=False):
151 def get_url(self, repo, request=None, permalink=False):
152 if not request:
152 if not request:
153 request = get_current_request()
153 request = get_current_request()
154
154
155 if not request:
155 if not request:
156 return
156 return
157
157
158 if permalink:
158 if permalink:
159 return request.route_url(
159 return request.route_url(
160 'repo_summary', repo_name='_{}'.format(safe_str(repo.repo_id)))
160 'repo_summary', repo_name='_{}'.format(safe_str(repo.repo_id)))
161 else:
161 else:
162 return request.route_url(
162 return request.route_url(
163 'repo_summary', repo_name=safe_str(repo.repo_name))
163 'repo_summary', repo_name=safe_str(repo.repo_name))
164
164
165 def get_commit_url(self, repo, commit_id, request=None, permalink=False):
165 def get_commit_url(self, repo, commit_id, request=None, permalink=False):
166 if not request:
166 if not request:
167 request = get_current_request()
167 request = get_current_request()
168
168
169 if not request:
169 if not request:
170 return
170 return
171
171
172 if permalink:
172 if permalink:
173 return request.route_url(
173 return request.route_url(
174 'repo_commit', repo_name=safe_str(repo.repo_id),
174 'repo_commit', repo_name=safe_str(repo.repo_id),
175 commit_id=commit_id)
175 commit_id=commit_id)
176
176
177 else:
177 else:
178 return request.route_url(
178 return request.route_url(
179 'repo_commit', repo_name=safe_str(repo.repo_name),
179 'repo_commit', repo_name=safe_str(repo.repo_name),
180 commit_id=commit_id)
180 commit_id=commit_id)
181
181
182 def get_repo_log(self, repo, filter_term):
182 def get_repo_log(self, repo, filter_term):
183 repo_log = UserLog.query()\
183 repo_log = UserLog.query()\
184 .filter(or_(UserLog.repository_id == repo.repo_id,
184 .filter(or_(UserLog.repository_id == repo.repo_id,
185 UserLog.repository_name == repo.repo_name))\
185 UserLog.repository_name == repo.repo_name))\
186 .options(joinedload(UserLog.user))\
186 .options(joinedload(UserLog.user))\
187 .options(joinedload(UserLog.repository))\
187 .options(joinedload(UserLog.repository))\
188 .order_by(UserLog.action_date.desc())
188 .order_by(UserLog.action_date.desc())
189
189
190 repo_log = user_log_filter(repo_log, filter_term)
190 repo_log = user_log_filter(repo_log, filter_term)
191 return repo_log
191 return repo_log
192
192
193 @classmethod
193 @classmethod
194 def update_commit_cache(cls, repositories=None):
194 def update_commit_cache(cls, repositories=None):
195 if not repositories:
195 if not repositories:
196 repositories = Repository.getAll()
196 repositories = Repository.getAll()
197 for repo in repositories:
197 for repo in repositories:
198 repo.update_commit_cache()
198 repo.update_commit_cache()
199
199
200 def get_repos_as_dict(self, repo_list=None, admin=False,
200 def get_repos_as_dict(self, repo_list=None, admin=False,
201 super_user_actions=False, short_name=None):
201 super_user_actions=False, short_name=None):
202 _render = get_current_request().get_partial_renderer(
202 _render = get_current_request().get_partial_renderer(
203 'rhodecode:templates/data_table/_dt_elements.mako')
203 'rhodecode:templates/data_table/_dt_elements.mako')
204 c = _render.get_call_context()
204 c = _render.get_call_context()
205
205
206 def quick_menu(repo_name):
206 def quick_menu(repo_name):
207 return _render('quick_menu', repo_name)
207 return _render('quick_menu', repo_name)
208
208
209 def repo_lnk(name, rtype, rstate, private, archived, fork_of):
209 def repo_lnk(name, rtype, rstate, private, archived, fork_of):
210 if short_name is not None:
210 if short_name is not None:
211 short_name_var = short_name
211 short_name_var = short_name
212 else:
212 else:
213 short_name_var = not admin
213 short_name_var = not admin
214 return _render('repo_name', name, rtype, rstate, private, archived, fork_of,
214 return _render('repo_name', name, rtype, rstate, private, archived, fork_of,
215 short_name=short_name_var, admin=False)
215 short_name=short_name_var, admin=False)
216
216
217 def last_change(last_change):
217 def last_change(last_change):
218 if admin and isinstance(last_change, datetime.datetime) and not last_change.tzinfo:
218 if admin and isinstance(last_change, datetime.datetime) and not last_change.tzinfo:
219 ts = time.time()
219 ts = time.time()
220 utc_offset = (datetime.datetime.fromtimestamp(ts)
220 utc_offset = (datetime.datetime.fromtimestamp(ts)
221 - datetime.datetime.utcfromtimestamp(ts)).total_seconds()
221 - datetime.datetime.utcfromtimestamp(ts)).total_seconds()
222 last_change = last_change + datetime.timedelta(seconds=utc_offset)
222 last_change = last_change + datetime.timedelta(seconds=utc_offset)
223
223
224 return _render("last_change", last_change)
224 return _render("last_change", last_change)
225
225
226 def rss_lnk(repo_name):
226 def rss_lnk(repo_name):
227 return _render("rss", repo_name)
227 return _render("rss", repo_name)
228
228
229 def atom_lnk(repo_name):
229 def atom_lnk(repo_name):
230 return _render("atom", repo_name)
230 return _render("atom", repo_name)
231
231
232 def last_rev(repo_name, cs_cache):
232 def last_rev(repo_name, cs_cache):
233 return _render('revision', repo_name, cs_cache.get('revision'),
233 return _render('revision', repo_name, cs_cache.get('revision'),
234 cs_cache.get('raw_id'), cs_cache.get('author'),
234 cs_cache.get('raw_id'), cs_cache.get('author'),
235 cs_cache.get('message'), cs_cache.get('date'))
235 cs_cache.get('message'), cs_cache.get('date'))
236
236
237 def desc(desc):
237 def desc(desc):
238 return _render('repo_desc', desc, c.visual.stylify_metatags)
238 return _render('repo_desc', desc, c.visual.stylify_metatags)
239
239
240 def state(repo_state):
240 def state(repo_state):
241 return _render("repo_state", repo_state)
241 return _render("repo_state", repo_state)
242
242
243 def repo_actions(repo_name):
243 def repo_actions(repo_name):
244 return _render('repo_actions', repo_name, super_user_actions)
244 return _render('repo_actions', repo_name, super_user_actions)
245
245
246 def user_profile(username):
246 def user_profile(username):
247 return _render('user_profile', username)
247 return _render('user_profile', username)
248
248
249 repos_data = []
249 repos_data = []
250 for repo in repo_list:
250 for repo in repo_list:
251 # NOTE(marcink): because we use only raw column we need to load it like that
251 # NOTE(marcink): because we use only raw column we need to load it like that
252 changeset_cache = Repository._load_changeset_cache(
252 changeset_cache = Repository._load_changeset_cache(
253 repo.repo_id, repo._changeset_cache)
253 repo.repo_id, repo._changeset_cache)
254 last_commit_change = Repository._load_commit_change(changeset_cache)
255
254
256 row = {
255 row = {
257 "menu": quick_menu(repo.repo_name),
256 "menu": quick_menu(repo.repo_name),
258
257
259 "name": repo_lnk(repo.repo_name, repo.repo_type, repo.repo_state,
258 "name": repo_lnk(repo.repo_name, repo.repo_type, repo.repo_state,
260 repo.private, repo.archived, repo.fork),
259 repo.private, repo.archived, repo.fork),
261 "name_raw": repo.repo_name.lower(),
260
262 "desc": desc(repo.description),
261 "desc": desc(repo.description),
263
262
264 "last_change": last_change(last_commit_change),
263 "last_change": last_change(repo.updated_on),
265 "last_change_raw": datetime_to_time(last_commit_change),
266
264
267 "last_changeset": last_rev(repo.repo_name, changeset_cache),
265 "last_changeset": last_rev(repo.repo_name, changeset_cache),
268 "last_changeset_raw": changeset_cache.get('revision'),
266 "last_changeset_raw": changeset_cache.get('revision'),
269
267
270 "owner": user_profile(repo.User.username),
268 "owner": user_profile(repo.User.username),
271
269
272 "state": state(repo.repo_state),
270 "state": state(repo.repo_state),
273 "rss": rss_lnk(repo.repo_name),
271 "rss": rss_lnk(repo.repo_name),
274 "atom": atom_lnk(repo.repo_name),
272 "atom": atom_lnk(repo.repo_name),
275 }
273 }
276 if admin:
274 if admin:
277 row.update({
275 row.update({
278 "action": repo_actions(repo.repo_name),
276 "action": repo_actions(repo.repo_name),
279 })
277 })
280 repos_data.append(row)
278 repos_data.append(row)
281
279
282 return repos_data
280 return repos_data
283
281
284 def get_repos_data_table(
282 def get_repos_data_table(
285 self, draw, start, limit,
283 self, draw, start, limit,
286 search_q, order_by, order_dir,
284 search_q, order_by, order_dir,
287 auth_user, repo_group_id):
285 auth_user, repo_group_id):
288 from rhodecode.model.scm import RepoList
286 from rhodecode.model.scm import RepoList
289
287
290 _perms = ['repository.read', 'repository.write', 'repository.admin']
288 _perms = ['repository.read', 'repository.write', 'repository.admin']
291
289
292 repos = Repository.query() \
290 repos = Repository.query() \
293 .filter(Repository.group_id == repo_group_id) \
291 .filter(Repository.group_id == repo_group_id) \
294 .all()
292 .all()
295 auth_repo_list = RepoList(
293 auth_repo_list = RepoList(
296 repos, perm_set=_perms,
294 repos, perm_set=_perms,
297 extra_kwargs=dict(user=auth_user))
295 extra_kwargs=dict(user=auth_user))
298
296
299 allowed_ids = [-1]
297 allowed_ids = [-1]
300 for repo in auth_repo_list:
298 for repo in auth_repo_list:
301 allowed_ids.append(repo.repo_id)
299 allowed_ids.append(repo.repo_id)
302
300
303 repos_data_total_count = Repository.query() \
301 repos_data_total_count = Repository.query() \
304 .filter(Repository.group_id == repo_group_id) \
302 .filter(Repository.group_id == repo_group_id) \
305 .filter(or_(
303 .filter(or_(
306 # generate multiple IN to fix limitation problems
304 # generate multiple IN to fix limitation problems
307 *in_filter_generator(Repository.repo_id, allowed_ids))
305 *in_filter_generator(Repository.repo_id, allowed_ids))
308 ) \
306 ) \
309 .count()
307 .count()
310
308
311 base_q = Session.query(
309 base_q = Session.query(
312 Repository.repo_id,
310 Repository.repo_id,
313 Repository.repo_name,
311 Repository.repo_name,
314 Repository.description,
312 Repository.description,
315 Repository.repo_type,
313 Repository.repo_type,
316 Repository.repo_state,
314 Repository.repo_state,
317 Repository.private,
315 Repository.private,
318 Repository.archived,
316 Repository.archived,
319 Repository.fork,
317 Repository.fork,
320 Repository.updated_on,
318 Repository.updated_on,
321 Repository._changeset_cache,
319 Repository._changeset_cache,
322 User,
320 User,
323 ) \
321 ) \
324 .filter(Repository.group_id == repo_group_id) \
322 .filter(Repository.group_id == repo_group_id) \
325 .filter(or_(
323 .filter(or_(
326 # generate multiple IN to fix limitation problems
324 # generate multiple IN to fix limitation problems
327 *in_filter_generator(Repository.repo_id, allowed_ids))
325 *in_filter_generator(Repository.repo_id, allowed_ids))
328 ) \
326 ) \
329 .join(User, User.user_id == Repository.user_id) \
327 .join(User, User.user_id == Repository.user_id) \
330 .group_by(Repository, User)
328 .group_by(Repository, User)
331
329
332 repos_data_total_filtered_count = base_q.count()
330 repos_data_total_filtered_count = base_q.count()
333
331
334 sort_defined = False
332 sort_defined = False
335 if order_by == 'repo_name':
333 if order_by == 'repo_name':
336 sort_col = func.lower(Repository.repo_name)
334 sort_col = func.lower(Repository.repo_name)
337 sort_defined = True
335 sort_defined = True
338 elif order_by == 'user_username':
336 elif order_by == 'user_username':
339 sort_col = User.username
337 sort_col = User.username
340 else:
338 else:
341 sort_col = getattr(Repository, order_by, None)
339 sort_col = getattr(Repository, order_by, None)
342
340
343 if sort_defined or sort_col:
341 if sort_defined or sort_col:
344 if order_dir == 'asc':
342 if order_dir == 'asc':
345 sort_col = sort_col.asc()
343 sort_col = sort_col.asc()
346 else:
344 else:
347 sort_col = sort_col.desc()
345 sort_col = sort_col.desc()
348
346
349 base_q = base_q.order_by(sort_col)
347 base_q = base_q.order_by(sort_col)
350 base_q = base_q.offset(start).limit(limit)
348 base_q = base_q.offset(start).limit(limit)
351
349
352 repos_list = base_q.all()
350 repos_list = base_q.all()
353
351
354 repos_data = RepoModel().get_repos_as_dict(
352 repos_data = RepoModel().get_repos_as_dict(
355 repo_list=repos_list, admin=False)
353 repo_list=repos_list, admin=False)
356
354
357 data = ({
355 data = ({
358 'draw': draw,
356 'draw': draw,
359 'data': repos_data,
357 'data': repos_data,
360 'recordsTotal': repos_data_total_count,
358 'recordsTotal': repos_data_total_count,
361 'recordsFiltered': repos_data_total_filtered_count,
359 'recordsFiltered': repos_data_total_filtered_count,
362 })
360 })
363 return data
361 return data
364
362
365 def _get_defaults(self, repo_name):
363 def _get_defaults(self, repo_name):
366 """
364 """
367 Gets information about repository, and returns a dict for
365 Gets information about repository, and returns a dict for
368 usage in forms
366 usage in forms
369
367
370 :param repo_name:
368 :param repo_name:
371 """
369 """
372
370
373 repo_info = Repository.get_by_repo_name(repo_name)
371 repo_info = Repository.get_by_repo_name(repo_name)
374
372
375 if repo_info is None:
373 if repo_info is None:
376 return None
374 return None
377
375
378 defaults = repo_info.get_dict()
376 defaults = repo_info.get_dict()
379 defaults['repo_name'] = repo_info.just_name
377 defaults['repo_name'] = repo_info.just_name
380
378
381 groups = repo_info.groups_with_parents
379 groups = repo_info.groups_with_parents
382 parent_group = groups[-1] if groups else None
380 parent_group = groups[-1] if groups else None
383
381
384 # we use -1 as this is how in HTML, we mark an empty group
382 # we use -1 as this is how in HTML, we mark an empty group
385 defaults['repo_group'] = getattr(parent_group, 'group_id', -1)
383 defaults['repo_group'] = getattr(parent_group, 'group_id', -1)
386
384
387 keys_to_process = (
385 keys_to_process = (
388 {'k': 'repo_type', 'strip': False},
386 {'k': 'repo_type', 'strip': False},
389 {'k': 'repo_enable_downloads', 'strip': True},
387 {'k': 'repo_enable_downloads', 'strip': True},
390 {'k': 'repo_description', 'strip': True},
388 {'k': 'repo_description', 'strip': True},
391 {'k': 'repo_enable_locking', 'strip': True},
389 {'k': 'repo_enable_locking', 'strip': True},
392 {'k': 'repo_landing_rev', 'strip': True},
390 {'k': 'repo_landing_rev', 'strip': True},
393 {'k': 'clone_uri', 'strip': False},
391 {'k': 'clone_uri', 'strip': False},
394 {'k': 'push_uri', 'strip': False},
392 {'k': 'push_uri', 'strip': False},
395 {'k': 'repo_private', 'strip': True},
393 {'k': 'repo_private', 'strip': True},
396 {'k': 'repo_enable_statistics', 'strip': True}
394 {'k': 'repo_enable_statistics', 'strip': True}
397 )
395 )
398
396
399 for item in keys_to_process:
397 for item in keys_to_process:
400 attr = item['k']
398 attr = item['k']
401 if item['strip']:
399 if item['strip']:
402 attr = remove_prefix(item['k'], 'repo_')
400 attr = remove_prefix(item['k'], 'repo_')
403
401
404 val = defaults[attr]
402 val = defaults[attr]
405 if item['k'] == 'repo_landing_rev':
403 if item['k'] == 'repo_landing_rev':
406 val = ':'.join(defaults[attr])
404 val = ':'.join(defaults[attr])
407 defaults[item['k']] = val
405 defaults[item['k']] = val
408 if item['k'] == 'clone_uri':
406 if item['k'] == 'clone_uri':
409 defaults['clone_uri_hidden'] = repo_info.clone_uri_hidden
407 defaults['clone_uri_hidden'] = repo_info.clone_uri_hidden
410 if item['k'] == 'push_uri':
408 if item['k'] == 'push_uri':
411 defaults['push_uri_hidden'] = repo_info.push_uri_hidden
409 defaults['push_uri_hidden'] = repo_info.push_uri_hidden
412
410
413 # fill owner
411 # fill owner
414 if repo_info.user:
412 if repo_info.user:
415 defaults.update({'user': repo_info.user.username})
413 defaults.update({'user': repo_info.user.username})
416 else:
414 else:
417 replacement_user = User.get_first_super_admin().username
415 replacement_user = User.get_first_super_admin().username
418 defaults.update({'user': replacement_user})
416 defaults.update({'user': replacement_user})
419
417
420 return defaults
418 return defaults
421
419
422 def update(self, repo, **kwargs):
420 def update(self, repo, **kwargs):
423 try:
421 try:
424 cur_repo = self._get_repo(repo)
422 cur_repo = self._get_repo(repo)
425 source_repo_name = cur_repo.repo_name
423 source_repo_name = cur_repo.repo_name
426 if 'user' in kwargs:
424 if 'user' in kwargs:
427 cur_repo.user = User.get_by_username(kwargs['user'])
425 cur_repo.user = User.get_by_username(kwargs['user'])
428
426
429 if 'repo_group' in kwargs:
427 if 'repo_group' in kwargs:
430 cur_repo.group = RepoGroup.get(kwargs['repo_group'])
428 cur_repo.group = RepoGroup.get(kwargs['repo_group'])
431 log.debug('Updating repo %s with params:%s', cur_repo, kwargs)
429 log.debug('Updating repo %s with params:%s', cur_repo, kwargs)
432
430
433 update_keys = [
431 update_keys = [
434 (1, 'repo_description'),
432 (1, 'repo_description'),
435 (1, 'repo_landing_rev'),
433 (1, 'repo_landing_rev'),
436 (1, 'repo_private'),
434 (1, 'repo_private'),
437 (1, 'repo_enable_downloads'),
435 (1, 'repo_enable_downloads'),
438 (1, 'repo_enable_locking'),
436 (1, 'repo_enable_locking'),
439 (1, 'repo_enable_statistics'),
437 (1, 'repo_enable_statistics'),
440 (0, 'clone_uri'),
438 (0, 'clone_uri'),
441 (0, 'push_uri'),
439 (0, 'push_uri'),
442 (0, 'fork_id')
440 (0, 'fork_id')
443 ]
441 ]
444 for strip, k in update_keys:
442 for strip, k in update_keys:
445 if k in kwargs:
443 if k in kwargs:
446 val = kwargs[k]
444 val = kwargs[k]
447 if strip:
445 if strip:
448 k = remove_prefix(k, 'repo_')
446 k = remove_prefix(k, 'repo_')
449
447
450 setattr(cur_repo, k, val)
448 setattr(cur_repo, k, val)
451
449
452 new_name = cur_repo.get_new_name(kwargs['repo_name'])
450 new_name = cur_repo.get_new_name(kwargs['repo_name'])
453 cur_repo.repo_name = new_name
451 cur_repo.repo_name = new_name
454
452
455 # if private flag is set, reset default permission to NONE
453 # if private flag is set, reset default permission to NONE
456 if kwargs.get('repo_private'):
454 if kwargs.get('repo_private'):
457 EMPTY_PERM = 'repository.none'
455 EMPTY_PERM = 'repository.none'
458 RepoModel().grant_user_permission(
456 RepoModel().grant_user_permission(
459 repo=cur_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM
457 repo=cur_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM
460 )
458 )
461
459
462 # handle extra fields
460 # handle extra fields
463 for field in filter(lambda k: k.startswith(RepositoryField.PREFIX), kwargs):
461 for field in filter(lambda k: k.startswith(RepositoryField.PREFIX), kwargs):
464 k = RepositoryField.un_prefix_key(field)
462 k = RepositoryField.un_prefix_key(field)
465 ex_field = RepositoryField.get_by_key_name(
463 ex_field = RepositoryField.get_by_key_name(
466 key=k, repo=cur_repo)
464 key=k, repo=cur_repo)
467 if ex_field:
465 if ex_field:
468 ex_field.field_value = kwargs[field]
466 ex_field.field_value = kwargs[field]
469 self.sa.add(ex_field)
467 self.sa.add(ex_field)
470
468
471 self.sa.add(cur_repo)
469 self.sa.add(cur_repo)
472
470
473 if source_repo_name != new_name:
471 if source_repo_name != new_name:
474 # rename repository
472 # rename repository
475 self._rename_filesystem_repo(
473 self._rename_filesystem_repo(
476 old=source_repo_name, new=new_name)
474 old=source_repo_name, new=new_name)
477
475
478 return cur_repo
476 return cur_repo
479 except Exception:
477 except Exception:
480 log.error(traceback.format_exc())
478 log.error(traceback.format_exc())
481 raise
479 raise
482
480
483 def _create_repo(self, repo_name, repo_type, description, owner,
481 def _create_repo(self, repo_name, repo_type, description, owner,
484 private=False, clone_uri=None, repo_group=None,
482 private=False, clone_uri=None, repo_group=None,
485 landing_rev='rev:tip', fork_of=None,
483 landing_rev='rev:tip', fork_of=None,
486 copy_fork_permissions=False, enable_statistics=False,
484 copy_fork_permissions=False, enable_statistics=False,
487 enable_locking=False, enable_downloads=False,
485 enable_locking=False, enable_downloads=False,
488 copy_group_permissions=False,
486 copy_group_permissions=False,
489 state=Repository.STATE_PENDING):
487 state=Repository.STATE_PENDING):
490 """
488 """
491 Create repository inside database with PENDING state, this should be
489 Create repository inside database with PENDING state, this should be
492 only executed by create() repo. With exception of importing existing
490 only executed by create() repo. With exception of importing existing
493 repos
491 repos
494 """
492 """
495 from rhodecode.model.scm import ScmModel
493 from rhodecode.model.scm import ScmModel
496
494
497 owner = self._get_user(owner)
495 owner = self._get_user(owner)
498 fork_of = self._get_repo(fork_of)
496 fork_of = self._get_repo(fork_of)
499 repo_group = self._get_repo_group(safe_int(repo_group))
497 repo_group = self._get_repo_group(safe_int(repo_group))
500
498
501 try:
499 try:
502 repo_name = safe_unicode(repo_name)
500 repo_name = safe_unicode(repo_name)
503 description = safe_unicode(description)
501 description = safe_unicode(description)
504 # repo name is just a name of repository
502 # repo name is just a name of repository
505 # while repo_name_full is a full qualified name that is combined
503 # while repo_name_full is a full qualified name that is combined
506 # with name and path of group
504 # with name and path of group
507 repo_name_full = repo_name
505 repo_name_full = repo_name
508 repo_name = repo_name.split(Repository.NAME_SEP)[-1]
506 repo_name = repo_name.split(Repository.NAME_SEP)[-1]
509
507
510 new_repo = Repository()
508 new_repo = Repository()
511 new_repo.repo_state = state
509 new_repo.repo_state = state
512 new_repo.enable_statistics = False
510 new_repo.enable_statistics = False
513 new_repo.repo_name = repo_name_full
511 new_repo.repo_name = repo_name_full
514 new_repo.repo_type = repo_type
512 new_repo.repo_type = repo_type
515 new_repo.user = owner
513 new_repo.user = owner
516 new_repo.group = repo_group
514 new_repo.group = repo_group
517 new_repo.description = description or repo_name
515 new_repo.description = description or repo_name
518 new_repo.private = private
516 new_repo.private = private
519 new_repo.archived = False
517 new_repo.archived = False
520 new_repo.clone_uri = clone_uri
518 new_repo.clone_uri = clone_uri
521 new_repo.landing_rev = landing_rev
519 new_repo.landing_rev = landing_rev
522
520
523 new_repo.enable_statistics = enable_statistics
521 new_repo.enable_statistics = enable_statistics
524 new_repo.enable_locking = enable_locking
522 new_repo.enable_locking = enable_locking
525 new_repo.enable_downloads = enable_downloads
523 new_repo.enable_downloads = enable_downloads
526
524
527 if repo_group:
525 if repo_group:
528 new_repo.enable_locking = repo_group.enable_locking
526 new_repo.enable_locking = repo_group.enable_locking
529
527
530 if fork_of:
528 if fork_of:
531 parent_repo = fork_of
529 parent_repo = fork_of
532 new_repo.fork = parent_repo
530 new_repo.fork = parent_repo
533
531
534 events.trigger(events.RepoPreCreateEvent(new_repo))
532 events.trigger(events.RepoPreCreateEvent(new_repo))
535
533
536 self.sa.add(new_repo)
534 self.sa.add(new_repo)
537
535
538 EMPTY_PERM = 'repository.none'
536 EMPTY_PERM = 'repository.none'
539 if fork_of and copy_fork_permissions:
537 if fork_of and copy_fork_permissions:
540 repo = fork_of
538 repo = fork_of
541 user_perms = UserRepoToPerm.query() \
539 user_perms = UserRepoToPerm.query() \
542 .filter(UserRepoToPerm.repository == repo).all()
540 .filter(UserRepoToPerm.repository == repo).all()
543 group_perms = UserGroupRepoToPerm.query() \
541 group_perms = UserGroupRepoToPerm.query() \
544 .filter(UserGroupRepoToPerm.repository == repo).all()
542 .filter(UserGroupRepoToPerm.repository == repo).all()
545
543
546 for perm in user_perms:
544 for perm in user_perms:
547 UserRepoToPerm.create(
545 UserRepoToPerm.create(
548 perm.user, new_repo, perm.permission)
546 perm.user, new_repo, perm.permission)
549
547
550 for perm in group_perms:
548 for perm in group_perms:
551 UserGroupRepoToPerm.create(
549 UserGroupRepoToPerm.create(
552 perm.users_group, new_repo, perm.permission)
550 perm.users_group, new_repo, perm.permission)
553 # in case we copy permissions and also set this repo to private
551 # in case we copy permissions and also set this repo to private
554 # override the default user permission to make it a private repo
552 # override the default user permission to make it a private repo
555 if private:
553 if private:
556 RepoModel(self.sa).grant_user_permission(
554 RepoModel(self.sa).grant_user_permission(
557 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
555 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
558
556
559 elif repo_group and copy_group_permissions:
557 elif repo_group and copy_group_permissions:
560 user_perms = UserRepoGroupToPerm.query() \
558 user_perms = UserRepoGroupToPerm.query() \
561 .filter(UserRepoGroupToPerm.group == repo_group).all()
559 .filter(UserRepoGroupToPerm.group == repo_group).all()
562
560
563 group_perms = UserGroupRepoGroupToPerm.query() \
561 group_perms = UserGroupRepoGroupToPerm.query() \
564 .filter(UserGroupRepoGroupToPerm.group == repo_group).all()
562 .filter(UserGroupRepoGroupToPerm.group == repo_group).all()
565
563
566 for perm in user_perms:
564 for perm in user_perms:
567 perm_name = perm.permission.permission_name.replace(
565 perm_name = perm.permission.permission_name.replace(
568 'group.', 'repository.')
566 'group.', 'repository.')
569 perm_obj = Permission.get_by_key(perm_name)
567 perm_obj = Permission.get_by_key(perm_name)
570 UserRepoToPerm.create(perm.user, new_repo, perm_obj)
568 UserRepoToPerm.create(perm.user, new_repo, perm_obj)
571
569
572 for perm in group_perms:
570 for perm in group_perms:
573 perm_name = perm.permission.permission_name.replace(
571 perm_name = perm.permission.permission_name.replace(
574 'group.', 'repository.')
572 'group.', 'repository.')
575 perm_obj = Permission.get_by_key(perm_name)
573 perm_obj = Permission.get_by_key(perm_name)
576 UserGroupRepoToPerm.create(perm.users_group, new_repo, perm_obj)
574 UserGroupRepoToPerm.create(perm.users_group, new_repo, perm_obj)
577
575
578 if private:
576 if private:
579 RepoModel(self.sa).grant_user_permission(
577 RepoModel(self.sa).grant_user_permission(
580 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
578 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
581
579
582 else:
580 else:
583 perm_obj = self._create_default_perms(new_repo, private)
581 perm_obj = self._create_default_perms(new_repo, private)
584 self.sa.add(perm_obj)
582 self.sa.add(perm_obj)
585
583
586 # now automatically start following this repository as owner
584 # now automatically start following this repository as owner
587 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id, owner.user_id)
585 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id, owner.user_id)
588
586
589 # we need to flush here, in order to check if database won't
587 # we need to flush here, in order to check if database won't
590 # throw any exceptions, create filesystem dirs at the very end
588 # throw any exceptions, create filesystem dirs at the very end
591 self.sa.flush()
589 self.sa.flush()
592 events.trigger(events.RepoCreateEvent(new_repo))
590 events.trigger(events.RepoCreateEvent(new_repo))
593 return new_repo
591 return new_repo
594
592
595 except Exception:
593 except Exception:
596 log.error(traceback.format_exc())
594 log.error(traceback.format_exc())
597 raise
595 raise
598
596
599 def create(self, form_data, cur_user):
597 def create(self, form_data, cur_user):
600 """
598 """
601 Create repository using celery tasks
599 Create repository using celery tasks
602
600
603 :param form_data:
601 :param form_data:
604 :param cur_user:
602 :param cur_user:
605 """
603 """
606 from rhodecode.lib.celerylib import tasks, run_task
604 from rhodecode.lib.celerylib import tasks, run_task
607 return run_task(tasks.create_repo, form_data, cur_user)
605 return run_task(tasks.create_repo, form_data, cur_user)
608
606
609 def update_permissions(self, repo, perm_additions=None, perm_updates=None,
607 def update_permissions(self, repo, perm_additions=None, perm_updates=None,
610 perm_deletions=None, check_perms=True,
608 perm_deletions=None, check_perms=True,
611 cur_user=None):
609 cur_user=None):
612 if not perm_additions:
610 if not perm_additions:
613 perm_additions = []
611 perm_additions = []
614 if not perm_updates:
612 if not perm_updates:
615 perm_updates = []
613 perm_updates = []
616 if not perm_deletions:
614 if not perm_deletions:
617 perm_deletions = []
615 perm_deletions = []
618
616
619 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
617 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
620
618
621 changes = {
619 changes = {
622 'added': [],
620 'added': [],
623 'updated': [],
621 'updated': [],
624 'deleted': []
622 'deleted': []
625 }
623 }
626 # update permissions
624 # update permissions
627 for member_id, perm, member_type in perm_updates:
625 for member_id, perm, member_type in perm_updates:
628 member_id = int(member_id)
626 member_id = int(member_id)
629 if member_type == 'user':
627 if member_type == 'user':
630 member_name = User.get(member_id).username
628 member_name = User.get(member_id).username
631 # this updates also current one if found
629 # this updates also current one if found
632 self.grant_user_permission(
630 self.grant_user_permission(
633 repo=repo, user=member_id, perm=perm)
631 repo=repo, user=member_id, perm=perm)
634 elif member_type == 'user_group':
632 elif member_type == 'user_group':
635 # check if we have permissions to alter this usergroup
633 # check if we have permissions to alter this usergroup
636 member_name = UserGroup.get(member_id).users_group_name
634 member_name = UserGroup.get(member_id).users_group_name
637 if not check_perms or HasUserGroupPermissionAny(
635 if not check_perms or HasUserGroupPermissionAny(
638 *req_perms)(member_name, user=cur_user):
636 *req_perms)(member_name, user=cur_user):
639 self.grant_user_group_permission(
637 self.grant_user_group_permission(
640 repo=repo, group_name=member_id, perm=perm)
638 repo=repo, group_name=member_id, perm=perm)
641 else:
639 else:
642 raise ValueError("member_type must be 'user' or 'user_group' "
640 raise ValueError("member_type must be 'user' or 'user_group' "
643 "got {} instead".format(member_type))
641 "got {} instead".format(member_type))
644 changes['updated'].append({'type': member_type, 'id': member_id,
642 changes['updated'].append({'type': member_type, 'id': member_id,
645 'name': member_name, 'new_perm': perm})
643 'name': member_name, 'new_perm': perm})
646
644
647 # set new permissions
645 # set new permissions
648 for member_id, perm, member_type in perm_additions:
646 for member_id, perm, member_type in perm_additions:
649 member_id = int(member_id)
647 member_id = int(member_id)
650 if member_type == 'user':
648 if member_type == 'user':
651 member_name = User.get(member_id).username
649 member_name = User.get(member_id).username
652 self.grant_user_permission(
650 self.grant_user_permission(
653 repo=repo, user=member_id, perm=perm)
651 repo=repo, user=member_id, perm=perm)
654 elif member_type == 'user_group':
652 elif member_type == 'user_group':
655 # check if we have permissions to alter this usergroup
653 # check if we have permissions to alter this usergroup
656 member_name = UserGroup.get(member_id).users_group_name
654 member_name = UserGroup.get(member_id).users_group_name
657 if not check_perms or HasUserGroupPermissionAny(
655 if not check_perms or HasUserGroupPermissionAny(
658 *req_perms)(member_name, user=cur_user):
656 *req_perms)(member_name, user=cur_user):
659 self.grant_user_group_permission(
657 self.grant_user_group_permission(
660 repo=repo, group_name=member_id, perm=perm)
658 repo=repo, group_name=member_id, perm=perm)
661 else:
659 else:
662 raise ValueError("member_type must be 'user' or 'user_group' "
660 raise ValueError("member_type must be 'user' or 'user_group' "
663 "got {} instead".format(member_type))
661 "got {} instead".format(member_type))
664
662
665 changes['added'].append({'type': member_type, 'id': member_id,
663 changes['added'].append({'type': member_type, 'id': member_id,
666 'name': member_name, 'new_perm': perm})
664 'name': member_name, 'new_perm': perm})
667 # delete permissions
665 # delete permissions
668 for member_id, perm, member_type in perm_deletions:
666 for member_id, perm, member_type in perm_deletions:
669 member_id = int(member_id)
667 member_id = int(member_id)
670 if member_type == 'user':
668 if member_type == 'user':
671 member_name = User.get(member_id).username
669 member_name = User.get(member_id).username
672 self.revoke_user_permission(repo=repo, user=member_id)
670 self.revoke_user_permission(repo=repo, user=member_id)
673 elif member_type == 'user_group':
671 elif member_type == 'user_group':
674 # check if we have permissions to alter this usergroup
672 # check if we have permissions to alter this usergroup
675 member_name = UserGroup.get(member_id).users_group_name
673 member_name = UserGroup.get(member_id).users_group_name
676 if not check_perms or HasUserGroupPermissionAny(
674 if not check_perms or HasUserGroupPermissionAny(
677 *req_perms)(member_name, user=cur_user):
675 *req_perms)(member_name, user=cur_user):
678 self.revoke_user_group_permission(
676 self.revoke_user_group_permission(
679 repo=repo, group_name=member_id)
677 repo=repo, group_name=member_id)
680 else:
678 else:
681 raise ValueError("member_type must be 'user' or 'user_group' "
679 raise ValueError("member_type must be 'user' or 'user_group' "
682 "got {} instead".format(member_type))
680 "got {} instead".format(member_type))
683
681
684 changes['deleted'].append({'type': member_type, 'id': member_id,
682 changes['deleted'].append({'type': member_type, 'id': member_id,
685 'name': member_name, 'new_perm': perm})
683 'name': member_name, 'new_perm': perm})
686 return changes
684 return changes
687
685
688 def create_fork(self, form_data, cur_user):
686 def create_fork(self, form_data, cur_user):
689 """
687 """
690 Simple wrapper into executing celery task for fork creation
688 Simple wrapper into executing celery task for fork creation
691
689
692 :param form_data:
690 :param form_data:
693 :param cur_user:
691 :param cur_user:
694 """
692 """
695 from rhodecode.lib.celerylib import tasks, run_task
693 from rhodecode.lib.celerylib import tasks, run_task
696 return run_task(tasks.create_repo_fork, form_data, cur_user)
694 return run_task(tasks.create_repo_fork, form_data, cur_user)
697
695
698 def archive(self, repo):
696 def archive(self, repo):
699 """
697 """
700 Archive given repository. Set archive flag.
698 Archive given repository. Set archive flag.
701
699
702 :param repo:
700 :param repo:
703 """
701 """
704 repo = self._get_repo(repo)
702 repo = self._get_repo(repo)
705 if repo:
703 if repo:
706
704
707 try:
705 try:
708 repo.archived = True
706 repo.archived = True
709 self.sa.add(repo)
707 self.sa.add(repo)
710 self.sa.commit()
708 self.sa.commit()
711 except Exception:
709 except Exception:
712 log.error(traceback.format_exc())
710 log.error(traceback.format_exc())
713 raise
711 raise
714
712
715 def delete(self, repo, forks=None, pull_requests=None, fs_remove=True, cur_user=None):
713 def delete(self, repo, forks=None, pull_requests=None, fs_remove=True, cur_user=None):
716 """
714 """
717 Delete given repository, forks parameter defines what do do with
715 Delete given repository, forks parameter defines what do do with
718 attached forks. Throws AttachedForksError if deleted repo has attached
716 attached forks. Throws AttachedForksError if deleted repo has attached
719 forks
717 forks
720
718
721 :param repo:
719 :param repo:
722 :param forks: str 'delete' or 'detach'
720 :param forks: str 'delete' or 'detach'
723 :param pull_requests: str 'delete' or None
721 :param pull_requests: str 'delete' or None
724 :param fs_remove: remove(archive) repo from filesystem
722 :param fs_remove: remove(archive) repo from filesystem
725 """
723 """
726 if not cur_user:
724 if not cur_user:
727 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
725 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
728 repo = self._get_repo(repo)
726 repo = self._get_repo(repo)
729 if repo:
727 if repo:
730 if forks == 'detach':
728 if forks == 'detach':
731 for r in repo.forks:
729 for r in repo.forks:
732 r.fork = None
730 r.fork = None
733 self.sa.add(r)
731 self.sa.add(r)
734 elif forks == 'delete':
732 elif forks == 'delete':
735 for r in repo.forks:
733 for r in repo.forks:
736 self.delete(r, forks='delete')
734 self.delete(r, forks='delete')
737 elif [f for f in repo.forks]:
735 elif [f for f in repo.forks]:
738 raise AttachedForksError()
736 raise AttachedForksError()
739
737
740 # check for pull requests
738 # check for pull requests
741 pr_sources = repo.pull_requests_source
739 pr_sources = repo.pull_requests_source
742 pr_targets = repo.pull_requests_target
740 pr_targets = repo.pull_requests_target
743 if pull_requests != 'delete' and (pr_sources or pr_targets):
741 if pull_requests != 'delete' and (pr_sources or pr_targets):
744 raise AttachedPullRequestsError()
742 raise AttachedPullRequestsError()
745
743
746 old_repo_dict = repo.get_dict()
744 old_repo_dict = repo.get_dict()
747 events.trigger(events.RepoPreDeleteEvent(repo))
745 events.trigger(events.RepoPreDeleteEvent(repo))
748 try:
746 try:
749 self.sa.delete(repo)
747 self.sa.delete(repo)
750 if fs_remove:
748 if fs_remove:
751 self._delete_filesystem_repo(repo)
749 self._delete_filesystem_repo(repo)
752 else:
750 else:
753 log.debug('skipping removal from filesystem')
751 log.debug('skipping removal from filesystem')
754 old_repo_dict.update({
752 old_repo_dict.update({
755 'deleted_by': cur_user,
753 'deleted_by': cur_user,
756 'deleted_on': time.time(),
754 'deleted_on': time.time(),
757 })
755 })
758 log_delete_repository(**old_repo_dict)
756 log_delete_repository(**old_repo_dict)
759 events.trigger(events.RepoDeleteEvent(repo))
757 events.trigger(events.RepoDeleteEvent(repo))
760 except Exception:
758 except Exception:
761 log.error(traceback.format_exc())
759 log.error(traceback.format_exc())
762 raise
760 raise
763
761
764 def grant_user_permission(self, repo, user, perm):
762 def grant_user_permission(self, repo, user, perm):
765 """
763 """
766 Grant permission for user on given repository, or update existing one
764 Grant permission for user on given repository, or update existing one
767 if found
765 if found
768
766
769 :param repo: Instance of Repository, repository_id, or repository name
767 :param repo: Instance of Repository, repository_id, or repository name
770 :param user: Instance of User, user_id or username
768 :param user: Instance of User, user_id or username
771 :param perm: Instance of Permission, or permission_name
769 :param perm: Instance of Permission, or permission_name
772 """
770 """
773 user = self._get_user(user)
771 user = self._get_user(user)
774 repo = self._get_repo(repo)
772 repo = self._get_repo(repo)
775 permission = self._get_perm(perm)
773 permission = self._get_perm(perm)
776
774
777 # check if we have that permission already
775 # check if we have that permission already
778 obj = self.sa.query(UserRepoToPerm) \
776 obj = self.sa.query(UserRepoToPerm) \
779 .filter(UserRepoToPerm.user == user) \
777 .filter(UserRepoToPerm.user == user) \
780 .filter(UserRepoToPerm.repository == repo) \
778 .filter(UserRepoToPerm.repository == repo) \
781 .scalar()
779 .scalar()
782 if obj is None:
780 if obj is None:
783 # create new !
781 # create new !
784 obj = UserRepoToPerm()
782 obj = UserRepoToPerm()
785 obj.repository = repo
783 obj.repository = repo
786 obj.user = user
784 obj.user = user
787 obj.permission = permission
785 obj.permission = permission
788 self.sa.add(obj)
786 self.sa.add(obj)
789 log.debug('Granted perm %s to %s on %s', perm, user, repo)
787 log.debug('Granted perm %s to %s on %s', perm, user, repo)
790 action_logger_generic(
788 action_logger_generic(
791 'granted permission: {} to user: {} on repo: {}'.format(
789 'granted permission: {} to user: {} on repo: {}'.format(
792 perm, user, repo), namespace='security.repo')
790 perm, user, repo), namespace='security.repo')
793 return obj
791 return obj
794
792
795 def revoke_user_permission(self, repo, user):
793 def revoke_user_permission(self, repo, user):
796 """
794 """
797 Revoke permission for user on given repository
795 Revoke permission for user on given repository
798
796
799 :param repo: Instance of Repository, repository_id, or repository name
797 :param repo: Instance of Repository, repository_id, or repository name
800 :param user: Instance of User, user_id or username
798 :param user: Instance of User, user_id or username
801 """
799 """
802
800
803 user = self._get_user(user)
801 user = self._get_user(user)
804 repo = self._get_repo(repo)
802 repo = self._get_repo(repo)
805
803
806 obj = self.sa.query(UserRepoToPerm) \
804 obj = self.sa.query(UserRepoToPerm) \
807 .filter(UserRepoToPerm.repository == repo) \
805 .filter(UserRepoToPerm.repository == repo) \
808 .filter(UserRepoToPerm.user == user) \
806 .filter(UserRepoToPerm.user == user) \
809 .scalar()
807 .scalar()
810 if obj:
808 if obj:
811 self.sa.delete(obj)
809 self.sa.delete(obj)
812 log.debug('Revoked perm on %s on %s', repo, user)
810 log.debug('Revoked perm on %s on %s', repo, user)
813 action_logger_generic(
811 action_logger_generic(
814 'revoked permission from user: {} on repo: {}'.format(
812 'revoked permission from user: {} on repo: {}'.format(
815 user, repo), namespace='security.repo')
813 user, repo), namespace='security.repo')
816
814
817 def grant_user_group_permission(self, repo, group_name, perm):
815 def grant_user_group_permission(self, repo, group_name, perm):
818 """
816 """
819 Grant permission for user group on given repository, or update
817 Grant permission for user group on given repository, or update
820 existing one if found
818 existing one if found
821
819
822 :param repo: Instance of Repository, repository_id, or repository name
820 :param repo: Instance of Repository, repository_id, or repository name
823 :param group_name: Instance of UserGroup, users_group_id,
821 :param group_name: Instance of UserGroup, users_group_id,
824 or user group name
822 or user group name
825 :param perm: Instance of Permission, or permission_name
823 :param perm: Instance of Permission, or permission_name
826 """
824 """
827 repo = self._get_repo(repo)
825 repo = self._get_repo(repo)
828 group_name = self._get_user_group(group_name)
826 group_name = self._get_user_group(group_name)
829 permission = self._get_perm(perm)
827 permission = self._get_perm(perm)
830
828
831 # check if we have that permission already
829 # check if we have that permission already
832 obj = self.sa.query(UserGroupRepoToPerm) \
830 obj = self.sa.query(UserGroupRepoToPerm) \
833 .filter(UserGroupRepoToPerm.users_group == group_name) \
831 .filter(UserGroupRepoToPerm.users_group == group_name) \
834 .filter(UserGroupRepoToPerm.repository == repo) \
832 .filter(UserGroupRepoToPerm.repository == repo) \
835 .scalar()
833 .scalar()
836
834
837 if obj is None:
835 if obj is None:
838 # create new
836 # create new
839 obj = UserGroupRepoToPerm()
837 obj = UserGroupRepoToPerm()
840
838
841 obj.repository = repo
839 obj.repository = repo
842 obj.users_group = group_name
840 obj.users_group = group_name
843 obj.permission = permission
841 obj.permission = permission
844 self.sa.add(obj)
842 self.sa.add(obj)
845 log.debug('Granted perm %s to %s on %s', perm, group_name, repo)
843 log.debug('Granted perm %s to %s on %s', perm, group_name, repo)
846 action_logger_generic(
844 action_logger_generic(
847 'granted permission: {} to usergroup: {} on repo: {}'.format(
845 'granted permission: {} to usergroup: {} on repo: {}'.format(
848 perm, group_name, repo), namespace='security.repo')
846 perm, group_name, repo), namespace='security.repo')
849
847
850 return obj
848 return obj
851
849
852 def revoke_user_group_permission(self, repo, group_name):
850 def revoke_user_group_permission(self, repo, group_name):
853 """
851 """
854 Revoke permission for user group on given repository
852 Revoke permission for user group on given repository
855
853
856 :param repo: Instance of Repository, repository_id, or repository name
854 :param repo: Instance of Repository, repository_id, or repository name
857 :param group_name: Instance of UserGroup, users_group_id,
855 :param group_name: Instance of UserGroup, users_group_id,
858 or user group name
856 or user group name
859 """
857 """
860 repo = self._get_repo(repo)
858 repo = self._get_repo(repo)
861 group_name = self._get_user_group(group_name)
859 group_name = self._get_user_group(group_name)
862
860
863 obj = self.sa.query(UserGroupRepoToPerm) \
861 obj = self.sa.query(UserGroupRepoToPerm) \
864 .filter(UserGroupRepoToPerm.repository == repo) \
862 .filter(UserGroupRepoToPerm.repository == repo) \
865 .filter(UserGroupRepoToPerm.users_group == group_name) \
863 .filter(UserGroupRepoToPerm.users_group == group_name) \
866 .scalar()
864 .scalar()
867 if obj:
865 if obj:
868 self.sa.delete(obj)
866 self.sa.delete(obj)
869 log.debug('Revoked perm to %s on %s', repo, group_name)
867 log.debug('Revoked perm to %s on %s', repo, group_name)
870 action_logger_generic(
868 action_logger_generic(
871 'revoked permission from usergroup: {} on repo: {}'.format(
869 'revoked permission from usergroup: {} on repo: {}'.format(
872 group_name, repo), namespace='security.repo')
870 group_name, repo), namespace='security.repo')
873
871
874 def delete_stats(self, repo_name):
872 def delete_stats(self, repo_name):
875 """
873 """
876 removes stats for given repo
874 removes stats for given repo
877
875
878 :param repo_name:
876 :param repo_name:
879 """
877 """
880 repo = self._get_repo(repo_name)
878 repo = self._get_repo(repo_name)
881 try:
879 try:
882 obj = self.sa.query(Statistics) \
880 obj = self.sa.query(Statistics) \
883 .filter(Statistics.repository == repo).scalar()
881 .filter(Statistics.repository == repo).scalar()
884 if obj:
882 if obj:
885 self.sa.delete(obj)
883 self.sa.delete(obj)
886 except Exception:
884 except Exception:
887 log.error(traceback.format_exc())
885 log.error(traceback.format_exc())
888 raise
886 raise
889
887
890 def add_repo_field(self, repo_name, field_key, field_label, field_value='',
888 def add_repo_field(self, repo_name, field_key, field_label, field_value='',
891 field_type='str', field_desc=''):
889 field_type='str', field_desc=''):
892
890
893 repo = self._get_repo(repo_name)
891 repo = self._get_repo(repo_name)
894
892
895 new_field = RepositoryField()
893 new_field = RepositoryField()
896 new_field.repository = repo
894 new_field.repository = repo
897 new_field.field_key = field_key
895 new_field.field_key = field_key
898 new_field.field_type = field_type # python type
896 new_field.field_type = field_type # python type
899 new_field.field_value = field_value
897 new_field.field_value = field_value
900 new_field.field_desc = field_desc
898 new_field.field_desc = field_desc
901 new_field.field_label = field_label
899 new_field.field_label = field_label
902 self.sa.add(new_field)
900 self.sa.add(new_field)
903 return new_field
901 return new_field
904
902
905 def delete_repo_field(self, repo_name, field_key):
903 def delete_repo_field(self, repo_name, field_key):
906 repo = self._get_repo(repo_name)
904 repo = self._get_repo(repo_name)
907 field = RepositoryField.get_by_key_name(field_key, repo)
905 field = RepositoryField.get_by_key_name(field_key, repo)
908 if field:
906 if field:
909 self.sa.delete(field)
907 self.sa.delete(field)
910
908
911 def _create_filesystem_repo(self, repo_name, repo_type, repo_group,
909 def _create_filesystem_repo(self, repo_name, repo_type, repo_group,
912 clone_uri=None, repo_store_location=None,
910 clone_uri=None, repo_store_location=None,
913 use_global_config=False, install_hooks=True):
911 use_global_config=False, install_hooks=True):
914 """
912 """
915 makes repository on filesystem. It's group aware means it'll create
913 makes repository on filesystem. It's group aware means it'll create
916 a repository within a group, and alter the paths accordingly of
914 a repository within a group, and alter the paths accordingly of
917 group location
915 group location
918
916
919 :param repo_name:
917 :param repo_name:
920 :param alias:
918 :param alias:
921 :param parent:
919 :param parent:
922 :param clone_uri:
920 :param clone_uri:
923 :param repo_store_location:
921 :param repo_store_location:
924 """
922 """
925 from rhodecode.lib.utils import is_valid_repo, is_valid_repo_group
923 from rhodecode.lib.utils import is_valid_repo, is_valid_repo_group
926 from rhodecode.model.scm import ScmModel
924 from rhodecode.model.scm import ScmModel
927
925
928 if Repository.NAME_SEP in repo_name:
926 if Repository.NAME_SEP in repo_name:
929 raise ValueError(
927 raise ValueError(
930 'repo_name must not contain groups got `%s`' % repo_name)
928 'repo_name must not contain groups got `%s`' % repo_name)
931
929
932 if isinstance(repo_group, RepoGroup):
930 if isinstance(repo_group, RepoGroup):
933 new_parent_path = os.sep.join(repo_group.full_path_splitted)
931 new_parent_path = os.sep.join(repo_group.full_path_splitted)
934 else:
932 else:
935 new_parent_path = repo_group or ''
933 new_parent_path = repo_group or ''
936
934
937 if repo_store_location:
935 if repo_store_location:
938 _paths = [repo_store_location]
936 _paths = [repo_store_location]
939 else:
937 else:
940 _paths = [self.repos_path, new_parent_path, repo_name]
938 _paths = [self.repos_path, new_parent_path, repo_name]
941 # we need to make it str for mercurial
939 # we need to make it str for mercurial
942 repo_path = os.path.join(*map(lambda x: safe_str(x), _paths))
940 repo_path = os.path.join(*map(lambda x: safe_str(x), _paths))
943
941
944 # check if this path is not a repository
942 # check if this path is not a repository
945 if is_valid_repo(repo_path, self.repos_path):
943 if is_valid_repo(repo_path, self.repos_path):
946 raise Exception('This path %s is a valid repository' % repo_path)
944 raise Exception('This path %s is a valid repository' % repo_path)
947
945
948 # check if this path is a group
946 # check if this path is a group
949 if is_valid_repo_group(repo_path, self.repos_path):
947 if is_valid_repo_group(repo_path, self.repos_path):
950 raise Exception('This path %s is a valid group' % repo_path)
948 raise Exception('This path %s is a valid group' % repo_path)
951
949
952 log.info('creating repo %s in %s from url: `%s`',
950 log.info('creating repo %s in %s from url: `%s`',
953 repo_name, safe_unicode(repo_path),
951 repo_name, safe_unicode(repo_path),
954 obfuscate_url_pw(clone_uri))
952 obfuscate_url_pw(clone_uri))
955
953
956 backend = get_backend(repo_type)
954 backend = get_backend(repo_type)
957
955
958 config_repo = None if use_global_config else repo_name
956 config_repo = None if use_global_config else repo_name
959 if config_repo and new_parent_path:
957 if config_repo and new_parent_path:
960 config_repo = Repository.NAME_SEP.join(
958 config_repo = Repository.NAME_SEP.join(
961 (new_parent_path, config_repo))
959 (new_parent_path, config_repo))
962 config = make_db_config(clear_session=False, repo=config_repo)
960 config = make_db_config(clear_session=False, repo=config_repo)
963 config.set('extensions', 'largefiles', '')
961 config.set('extensions', 'largefiles', '')
964
962
965 # patch and reset hooks section of UI config to not run any
963 # patch and reset hooks section of UI config to not run any
966 # hooks on creating remote repo
964 # hooks on creating remote repo
967 config.clear_section('hooks')
965 config.clear_section('hooks')
968
966
969 # TODO: johbo: Unify this, hardcoded "bare=True" does not look nice
967 # TODO: johbo: Unify this, hardcoded "bare=True" does not look nice
970 if repo_type == 'git':
968 if repo_type == 'git':
971 repo = backend(
969 repo = backend(
972 repo_path, config=config, create=True, src_url=clone_uri, bare=True,
970 repo_path, config=config, create=True, src_url=clone_uri, bare=True,
973 with_wire={"cache": False})
971 with_wire={"cache": False})
974 else:
972 else:
975 repo = backend(
973 repo = backend(
976 repo_path, config=config, create=True, src_url=clone_uri,
974 repo_path, config=config, create=True, src_url=clone_uri,
977 with_wire={"cache": False})
975 with_wire={"cache": False})
978
976
979 if install_hooks:
977 if install_hooks:
980 repo.install_hooks()
978 repo.install_hooks()
981
979
982 log.debug('Created repo %s with %s backend',
980 log.debug('Created repo %s with %s backend',
983 safe_unicode(repo_name), safe_unicode(repo_type))
981 safe_unicode(repo_name), safe_unicode(repo_type))
984 return repo
982 return repo
985
983
986 def _rename_filesystem_repo(self, old, new):
984 def _rename_filesystem_repo(self, old, new):
987 """
985 """
988 renames repository on filesystem
986 renames repository on filesystem
989
987
990 :param old: old name
988 :param old: old name
991 :param new: new name
989 :param new: new name
992 """
990 """
993 log.info('renaming repo from %s to %s', old, new)
991 log.info('renaming repo from %s to %s', old, new)
994
992
995 old_path = os.path.join(self.repos_path, old)
993 old_path = os.path.join(self.repos_path, old)
996 new_path = os.path.join(self.repos_path, new)
994 new_path = os.path.join(self.repos_path, new)
997 if os.path.isdir(new_path):
995 if os.path.isdir(new_path):
998 raise Exception(
996 raise Exception(
999 'Was trying to rename to already existing dir %s' % new_path
997 'Was trying to rename to already existing dir %s' % new_path
1000 )
998 )
1001 shutil.move(old_path, new_path)
999 shutil.move(old_path, new_path)
1002
1000
1003 def _delete_filesystem_repo(self, repo):
1001 def _delete_filesystem_repo(self, repo):
1004 """
1002 """
1005 removes repo from filesystem, the removal is acctually made by
1003 removes repo from filesystem, the removal is acctually made by
1006 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
1004 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
1007 repository is no longer valid for rhodecode, can be undeleted later on
1005 repository is no longer valid for rhodecode, can be undeleted later on
1008 by reverting the renames on this repository
1006 by reverting the renames on this repository
1009
1007
1010 :param repo: repo object
1008 :param repo: repo object
1011 """
1009 """
1012 rm_path = os.path.join(self.repos_path, repo.repo_name)
1010 rm_path = os.path.join(self.repos_path, repo.repo_name)
1013 repo_group = repo.group
1011 repo_group = repo.group
1014 log.info("Removing repository %s", rm_path)
1012 log.info("Removing repository %s", rm_path)
1015 # disable hg/git internal that it doesn't get detected as repo
1013 # disable hg/git internal that it doesn't get detected as repo
1016 alias = repo.repo_type
1014 alias = repo.repo_type
1017
1015
1018 config = make_db_config(clear_session=False)
1016 config = make_db_config(clear_session=False)
1019 config.set('extensions', 'largefiles', '')
1017 config.set('extensions', 'largefiles', '')
1020 bare = getattr(repo.scm_instance(config=config), 'bare', False)
1018 bare = getattr(repo.scm_instance(config=config), 'bare', False)
1021
1019
1022 # skip this for bare git repos
1020 # skip this for bare git repos
1023 if not bare:
1021 if not bare:
1024 # disable VCS repo
1022 # disable VCS repo
1025 vcs_path = os.path.join(rm_path, '.%s' % alias)
1023 vcs_path = os.path.join(rm_path, '.%s' % alias)
1026 if os.path.exists(vcs_path):
1024 if os.path.exists(vcs_path):
1027 shutil.move(vcs_path, os.path.join(rm_path, 'rm__.%s' % alias))
1025 shutil.move(vcs_path, os.path.join(rm_path, 'rm__.%s' % alias))
1028
1026
1029 _now = datetime.datetime.now()
1027 _now = datetime.datetime.now()
1030 _ms = str(_now.microsecond).rjust(6, '0')
1028 _ms = str(_now.microsecond).rjust(6, '0')
1031 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
1029 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
1032 repo.just_name)
1030 repo.just_name)
1033 if repo_group:
1031 if repo_group:
1034 # if repository is in group, prefix the removal path with the group
1032 # if repository is in group, prefix the removal path with the group
1035 args = repo_group.full_path_splitted + [_d]
1033 args = repo_group.full_path_splitted + [_d]
1036 _d = os.path.join(*args)
1034 _d = os.path.join(*args)
1037
1035
1038 if os.path.isdir(rm_path):
1036 if os.path.isdir(rm_path):
1039 shutil.move(rm_path, os.path.join(self.repos_path, _d))
1037 shutil.move(rm_path, os.path.join(self.repos_path, _d))
1040
1038
1041 # finally cleanup diff-cache if it exists
1039 # finally cleanup diff-cache if it exists
1042 cached_diffs_dir = repo.cached_diffs_dir
1040 cached_diffs_dir = repo.cached_diffs_dir
1043 if os.path.isdir(cached_diffs_dir):
1041 if os.path.isdir(cached_diffs_dir):
1044 shutil.rmtree(cached_diffs_dir)
1042 shutil.rmtree(cached_diffs_dir)
1045
1043
1046
1044
1047 class ReadmeFinder:
1045 class ReadmeFinder:
1048 """
1046 """
1049 Utility which knows how to find a readme for a specific commit.
1047 Utility which knows how to find a readme for a specific commit.
1050
1048
1051 The main idea is that this is a configurable algorithm. When creating an
1049 The main idea is that this is a configurable algorithm. When creating an
1052 instance you can define parameters, currently only the `default_renderer`.
1050 instance you can define parameters, currently only the `default_renderer`.
1053 Based on this configuration the method :meth:`search` behaves slightly
1051 Based on this configuration the method :meth:`search` behaves slightly
1054 different.
1052 different.
1055 """
1053 """
1056
1054
1057 readme_re = re.compile(r'^readme(\.[^\.]+)?$', re.IGNORECASE)
1055 readme_re = re.compile(r'^readme(\.[^\.]+)?$', re.IGNORECASE)
1058 path_re = re.compile(r'^docs?', re.IGNORECASE)
1056 path_re = re.compile(r'^docs?', re.IGNORECASE)
1059
1057
1060 default_priorities = {
1058 default_priorities = {
1061 None: 0,
1059 None: 0,
1062 '.text': 2,
1060 '.text': 2,
1063 '.txt': 3,
1061 '.txt': 3,
1064 '.rst': 1,
1062 '.rst': 1,
1065 '.rest': 2,
1063 '.rest': 2,
1066 '.md': 1,
1064 '.md': 1,
1067 '.mkdn': 2,
1065 '.mkdn': 2,
1068 '.mdown': 3,
1066 '.mdown': 3,
1069 '.markdown': 4,
1067 '.markdown': 4,
1070 }
1068 }
1071
1069
1072 path_priority = {
1070 path_priority = {
1073 'doc': 0,
1071 'doc': 0,
1074 'docs': 1,
1072 'docs': 1,
1075 }
1073 }
1076
1074
1077 FALLBACK_PRIORITY = 99
1075 FALLBACK_PRIORITY = 99
1078
1076
1079 RENDERER_TO_EXTENSION = {
1077 RENDERER_TO_EXTENSION = {
1080 'rst': ['.rst', '.rest'],
1078 'rst': ['.rst', '.rest'],
1081 'markdown': ['.md', 'mkdn', '.mdown', '.markdown'],
1079 'markdown': ['.md', 'mkdn', '.mdown', '.markdown'],
1082 }
1080 }
1083
1081
1084 def __init__(self, default_renderer=None):
1082 def __init__(self, default_renderer=None):
1085 self._default_renderer = default_renderer
1083 self._default_renderer = default_renderer
1086 self._renderer_extensions = self.RENDERER_TO_EXTENSION.get(
1084 self._renderer_extensions = self.RENDERER_TO_EXTENSION.get(
1087 default_renderer, [])
1085 default_renderer, [])
1088
1086
1089 def search(self, commit, path='/'):
1087 def search(self, commit, path='/'):
1090 """
1088 """
1091 Find a readme in the given `commit`.
1089 Find a readme in the given `commit`.
1092 """
1090 """
1093 nodes = commit.get_nodes(path)
1091 nodes = commit.get_nodes(path)
1094 matches = self._match_readmes(nodes)
1092 matches = self._match_readmes(nodes)
1095 matches = self._sort_according_to_priority(matches)
1093 matches = self._sort_according_to_priority(matches)
1096 if matches:
1094 if matches:
1097 return matches[0].node
1095 return matches[0].node
1098
1096
1099 paths = self._match_paths(nodes)
1097 paths = self._match_paths(nodes)
1100 paths = self._sort_paths_according_to_priority(paths)
1098 paths = self._sort_paths_according_to_priority(paths)
1101 for path in paths:
1099 for path in paths:
1102 match = self.search(commit, path=path)
1100 match = self.search(commit, path=path)
1103 if match:
1101 if match:
1104 return match
1102 return match
1105
1103
1106 return None
1104 return None
1107
1105
1108 def _match_readmes(self, nodes):
1106 def _match_readmes(self, nodes):
1109 for node in nodes:
1107 for node in nodes:
1110 if not node.is_file():
1108 if not node.is_file():
1111 continue
1109 continue
1112 path = node.path.rsplit('/', 1)[-1]
1110 path = node.path.rsplit('/', 1)[-1]
1113 match = self.readme_re.match(path)
1111 match = self.readme_re.match(path)
1114 if match:
1112 if match:
1115 extension = match.group(1)
1113 extension = match.group(1)
1116 yield ReadmeMatch(node, match, self._priority(extension))
1114 yield ReadmeMatch(node, match, self._priority(extension))
1117
1115
1118 def _match_paths(self, nodes):
1116 def _match_paths(self, nodes):
1119 for node in nodes:
1117 for node in nodes:
1120 if not node.is_dir():
1118 if not node.is_dir():
1121 continue
1119 continue
1122 match = self.path_re.match(node.path)
1120 match = self.path_re.match(node.path)
1123 if match:
1121 if match:
1124 yield node.path
1122 yield node.path
1125
1123
1126 def _priority(self, extension):
1124 def _priority(self, extension):
1127 renderer_priority = (
1125 renderer_priority = (
1128 0 if extension in self._renderer_extensions else 1)
1126 0 if extension in self._renderer_extensions else 1)
1129 extension_priority = self.default_priorities.get(
1127 extension_priority = self.default_priorities.get(
1130 extension, self.FALLBACK_PRIORITY)
1128 extension, self.FALLBACK_PRIORITY)
1131 return (renderer_priority, extension_priority)
1129 return (renderer_priority, extension_priority)
1132
1130
1133 def _sort_according_to_priority(self, matches):
1131 def _sort_according_to_priority(self, matches):
1134
1132
1135 def priority_and_path(match):
1133 def priority_and_path(match):
1136 return (match.priority, match.path)
1134 return (match.priority, match.path)
1137
1135
1138 return sorted(matches, key=priority_and_path)
1136 return sorted(matches, key=priority_and_path)
1139
1137
1140 def _sort_paths_according_to_priority(self, paths):
1138 def _sort_paths_according_to_priority(self, paths):
1141
1139
1142 def priority_and_path(path):
1140 def priority_and_path(path):
1143 return (self.path_priority.get(path, self.FALLBACK_PRIORITY), path)
1141 return (self.path_priority.get(path, self.FALLBACK_PRIORITY), path)
1144
1142
1145 return sorted(paths, key=priority_and_path)
1143 return sorted(paths, key=priority_and_path)
1146
1144
1147
1145
1148 class ReadmeMatch:
1146 class ReadmeMatch:
1149
1147
1150 def __init__(self, node, match, priority):
1148 def __init__(self, node, match, priority):
1151 self.node = node
1149 self.node = node
1152 self._match = match
1150 self._match = match
1153 self.priority = priority
1151 self.priority = priority
1154
1152
1155 @property
1153 @property
1156 def path(self):
1154 def path(self):
1157 return self.node.path
1155 return self.node.path
1158
1156
1159 def __repr__(self):
1157 def __repr__(self):
1160 return '<ReadmeMatch {} priority={}'.format(self.path, self.priority)
1158 return '<ReadmeMatch {} priority={}'.format(self.path, self.priority)
@@ -1,877 +1,876 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-2019 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
21
22 """
22 """
23 repo group model for RhodeCode
23 repo group model for RhodeCode
24 """
24 """
25
25
26 import os
26 import os
27 import datetime
27 import datetime
28 import itertools
28 import itertools
29 import logging
29 import logging
30 import shutil
30 import shutil
31 import time
31 import time
32 import traceback
32 import traceback
33 import string
33 import string
34
34
35 from zope.cachedescriptors.property import Lazy as LazyProperty
35 from zope.cachedescriptors.property import Lazy as LazyProperty
36
36
37 from rhodecode import events
37 from rhodecode import events
38 from rhodecode.model import BaseModel
38 from rhodecode.model import BaseModel
39 from rhodecode.model.db import (_hash_key, func, or_, in_filter_generator,
39 from rhodecode.model.db import (_hash_key, func, or_, in_filter_generator,
40 Session, RepoGroup, UserRepoGroupToPerm, User, Permission, UserGroupRepoGroupToPerm,
40 Session, RepoGroup, UserRepoGroupToPerm, User, Permission, UserGroupRepoGroupToPerm,
41 UserGroup, Repository)
41 UserGroup, Repository)
42 from rhodecode.model.settings import VcsSettingsModel, SettingsModel
42 from rhodecode.model.settings import VcsSettingsModel, SettingsModel
43 from rhodecode.lib.caching_query import FromCache
43 from rhodecode.lib.caching_query import FromCache
44 from rhodecode.lib.utils2 import action_logger_generic, datetime_to_time
44 from rhodecode.lib.utils2 import action_logger_generic, datetime_to_time
45
45
46 log = logging.getLogger(__name__)
46 log = logging.getLogger(__name__)
47
47
48
48
49 class RepoGroupModel(BaseModel):
49 class RepoGroupModel(BaseModel):
50
50
51 cls = RepoGroup
51 cls = RepoGroup
52 PERSONAL_GROUP_DESC = 'personal repo group of user `%(username)s`'
52 PERSONAL_GROUP_DESC = 'personal repo group of user `%(username)s`'
53 PERSONAL_GROUP_PATTERN = '${username}' # default
53 PERSONAL_GROUP_PATTERN = '${username}' # default
54
54
55 def _get_user_group(self, users_group):
55 def _get_user_group(self, users_group):
56 return self._get_instance(UserGroup, users_group,
56 return self._get_instance(UserGroup, users_group,
57 callback=UserGroup.get_by_group_name)
57 callback=UserGroup.get_by_group_name)
58
58
59 def _get_repo_group(self, repo_group):
59 def _get_repo_group(self, repo_group):
60 return self._get_instance(RepoGroup, repo_group,
60 return self._get_instance(RepoGroup, repo_group,
61 callback=RepoGroup.get_by_group_name)
61 callback=RepoGroup.get_by_group_name)
62
62
63 @LazyProperty
63 @LazyProperty
64 def repos_path(self):
64 def repos_path(self):
65 """
65 """
66 Gets the repositories root path from database
66 Gets the repositories root path from database
67 """
67 """
68
68
69 settings_model = VcsSettingsModel(sa=self.sa)
69 settings_model = VcsSettingsModel(sa=self.sa)
70 return settings_model.get_repos_location()
70 return settings_model.get_repos_location()
71
71
72 def get_by_group_name(self, repo_group_name, cache=None):
72 def get_by_group_name(self, repo_group_name, cache=None):
73 repo = self.sa.query(RepoGroup) \
73 repo = self.sa.query(RepoGroup) \
74 .filter(RepoGroup.group_name == repo_group_name)
74 .filter(RepoGroup.group_name == repo_group_name)
75
75
76 if cache:
76 if cache:
77 name_key = _hash_key(repo_group_name)
77 name_key = _hash_key(repo_group_name)
78 repo = repo.options(
78 repo = repo.options(
79 FromCache("sql_cache_short", "get_repo_group_%s" % name_key))
79 FromCache("sql_cache_short", "get_repo_group_%s" % name_key))
80 return repo.scalar()
80 return repo.scalar()
81
81
82 def get_default_create_personal_repo_group(self):
82 def get_default_create_personal_repo_group(self):
83 value = SettingsModel().get_setting_by_name(
83 value = SettingsModel().get_setting_by_name(
84 'create_personal_repo_group')
84 'create_personal_repo_group')
85 return value.app_settings_value if value else None or False
85 return value.app_settings_value if value else None or False
86
86
87 def get_personal_group_name_pattern(self):
87 def get_personal_group_name_pattern(self):
88 value = SettingsModel().get_setting_by_name(
88 value = SettingsModel().get_setting_by_name(
89 'personal_repo_group_pattern')
89 'personal_repo_group_pattern')
90 val = value.app_settings_value if value else None
90 val = value.app_settings_value if value else None
91 group_template = val or self.PERSONAL_GROUP_PATTERN
91 group_template = val or self.PERSONAL_GROUP_PATTERN
92
92
93 group_template = group_template.lstrip('/')
93 group_template = group_template.lstrip('/')
94 return group_template
94 return group_template
95
95
96 def get_personal_group_name(self, user):
96 def get_personal_group_name(self, user):
97 template = self.get_personal_group_name_pattern()
97 template = self.get_personal_group_name_pattern()
98 return string.Template(template).safe_substitute(
98 return string.Template(template).safe_substitute(
99 username=user.username,
99 username=user.username,
100 user_id=user.user_id,
100 user_id=user.user_id,
101 first_name=user.first_name,
101 first_name=user.first_name,
102 last_name=user.last_name,
102 last_name=user.last_name,
103 )
103 )
104
104
105 def create_personal_repo_group(self, user, commit_early=True):
105 def create_personal_repo_group(self, user, commit_early=True):
106 desc = self.PERSONAL_GROUP_DESC % {'username': user.username}
106 desc = self.PERSONAL_GROUP_DESC % {'username': user.username}
107 personal_repo_group_name = self.get_personal_group_name(user)
107 personal_repo_group_name = self.get_personal_group_name(user)
108
108
109 # create a new one
109 # create a new one
110 RepoGroupModel().create(
110 RepoGroupModel().create(
111 group_name=personal_repo_group_name,
111 group_name=personal_repo_group_name,
112 group_description=desc,
112 group_description=desc,
113 owner=user.username,
113 owner=user.username,
114 personal=True,
114 personal=True,
115 commit_early=commit_early)
115 commit_early=commit_early)
116
116
117 def _create_default_perms(self, new_group):
117 def _create_default_perms(self, new_group):
118 # create default permission
118 # create default permission
119 default_perm = 'group.read'
119 default_perm = 'group.read'
120 def_user = User.get_default_user()
120 def_user = User.get_default_user()
121 for p in def_user.user_perms:
121 for p in def_user.user_perms:
122 if p.permission.permission_name.startswith('group.'):
122 if p.permission.permission_name.startswith('group.'):
123 default_perm = p.permission.permission_name
123 default_perm = p.permission.permission_name
124 break
124 break
125
125
126 repo_group_to_perm = UserRepoGroupToPerm()
126 repo_group_to_perm = UserRepoGroupToPerm()
127 repo_group_to_perm.permission = Permission.get_by_key(default_perm)
127 repo_group_to_perm.permission = Permission.get_by_key(default_perm)
128
128
129 repo_group_to_perm.group = new_group
129 repo_group_to_perm.group = new_group
130 repo_group_to_perm.user_id = def_user.user_id
130 repo_group_to_perm.user_id = def_user.user_id
131 return repo_group_to_perm
131 return repo_group_to_perm
132
132
133 def _get_group_name_and_parent(self, group_name_full, repo_in_path=False,
133 def _get_group_name_and_parent(self, group_name_full, repo_in_path=False,
134 get_object=False):
134 get_object=False):
135 """
135 """
136 Get's the group name and a parent group name from given group name.
136 Get's the group name and a parent group name from given group name.
137 If repo_in_path is set to truth, we asume the full path also includes
137 If repo_in_path is set to truth, we asume the full path also includes
138 repo name, in such case we clean the last element.
138 repo name, in such case we clean the last element.
139
139
140 :param group_name_full:
140 :param group_name_full:
141 """
141 """
142 split_paths = 1
142 split_paths = 1
143 if repo_in_path:
143 if repo_in_path:
144 split_paths = 2
144 split_paths = 2
145 _parts = group_name_full.rsplit(RepoGroup.url_sep(), split_paths)
145 _parts = group_name_full.rsplit(RepoGroup.url_sep(), split_paths)
146
146
147 if repo_in_path and len(_parts) > 1:
147 if repo_in_path and len(_parts) > 1:
148 # such case last element is the repo_name
148 # such case last element is the repo_name
149 _parts.pop(-1)
149 _parts.pop(-1)
150 group_name_cleaned = _parts[-1] # just the group name
150 group_name_cleaned = _parts[-1] # just the group name
151 parent_repo_group_name = None
151 parent_repo_group_name = None
152
152
153 if len(_parts) > 1:
153 if len(_parts) > 1:
154 parent_repo_group_name = _parts[0]
154 parent_repo_group_name = _parts[0]
155
155
156 parent_group = None
156 parent_group = None
157 if parent_repo_group_name:
157 if parent_repo_group_name:
158 parent_group = RepoGroup.get_by_group_name(parent_repo_group_name)
158 parent_group = RepoGroup.get_by_group_name(parent_repo_group_name)
159
159
160 if get_object:
160 if get_object:
161 return group_name_cleaned, parent_repo_group_name, parent_group
161 return group_name_cleaned, parent_repo_group_name, parent_group
162
162
163 return group_name_cleaned, parent_repo_group_name
163 return group_name_cleaned, parent_repo_group_name
164
164
165 def check_exist_filesystem(self, group_name, exc_on_failure=True):
165 def check_exist_filesystem(self, group_name, exc_on_failure=True):
166 create_path = os.path.join(self.repos_path, group_name)
166 create_path = os.path.join(self.repos_path, group_name)
167 log.debug('creating new group in %s', create_path)
167 log.debug('creating new group in %s', create_path)
168
168
169 if os.path.isdir(create_path):
169 if os.path.isdir(create_path):
170 if exc_on_failure:
170 if exc_on_failure:
171 abs_create_path = os.path.abspath(create_path)
171 abs_create_path = os.path.abspath(create_path)
172 raise Exception('Directory `{}` already exists !'.format(abs_create_path))
172 raise Exception('Directory `{}` already exists !'.format(abs_create_path))
173 return False
173 return False
174 return True
174 return True
175
175
176 def _create_group(self, group_name):
176 def _create_group(self, group_name):
177 """
177 """
178 makes repository group on filesystem
178 makes repository group on filesystem
179
179
180 :param repo_name:
180 :param repo_name:
181 :param parent_id:
181 :param parent_id:
182 """
182 """
183
183
184 self.check_exist_filesystem(group_name)
184 self.check_exist_filesystem(group_name)
185 create_path = os.path.join(self.repos_path, group_name)
185 create_path = os.path.join(self.repos_path, group_name)
186 log.debug('creating new group in %s', create_path)
186 log.debug('creating new group in %s', create_path)
187 os.makedirs(create_path, mode=0o755)
187 os.makedirs(create_path, mode=0o755)
188 log.debug('created group in %s', create_path)
188 log.debug('created group in %s', create_path)
189
189
190 def _rename_group(self, old, new):
190 def _rename_group(self, old, new):
191 """
191 """
192 Renames a group on filesystem
192 Renames a group on filesystem
193
193
194 :param group_name:
194 :param group_name:
195 """
195 """
196
196
197 if old == new:
197 if old == new:
198 log.debug('skipping group rename')
198 log.debug('skipping group rename')
199 return
199 return
200
200
201 log.debug('renaming repository group from %s to %s', old, new)
201 log.debug('renaming repository group from %s to %s', old, new)
202
202
203 old_path = os.path.join(self.repos_path, old)
203 old_path = os.path.join(self.repos_path, old)
204 new_path = os.path.join(self.repos_path, new)
204 new_path = os.path.join(self.repos_path, new)
205
205
206 log.debug('renaming repos paths from %s to %s', old_path, new_path)
206 log.debug('renaming repos paths from %s to %s', old_path, new_path)
207
207
208 if os.path.isdir(new_path):
208 if os.path.isdir(new_path):
209 raise Exception('Was trying to rename to already '
209 raise Exception('Was trying to rename to already '
210 'existing dir %s' % new_path)
210 'existing dir %s' % new_path)
211 shutil.move(old_path, new_path)
211 shutil.move(old_path, new_path)
212
212
213 def _delete_filesystem_group(self, group, force_delete=False):
213 def _delete_filesystem_group(self, group, force_delete=False):
214 """
214 """
215 Deletes a group from a filesystem
215 Deletes a group from a filesystem
216
216
217 :param group: instance of group from database
217 :param group: instance of group from database
218 :param force_delete: use shutil rmtree to remove all objects
218 :param force_delete: use shutil rmtree to remove all objects
219 """
219 """
220 paths = group.full_path.split(RepoGroup.url_sep())
220 paths = group.full_path.split(RepoGroup.url_sep())
221 paths = os.sep.join(paths)
221 paths = os.sep.join(paths)
222
222
223 rm_path = os.path.join(self.repos_path, paths)
223 rm_path = os.path.join(self.repos_path, paths)
224 log.info("Removing group %s", rm_path)
224 log.info("Removing group %s", rm_path)
225 # delete only if that path really exists
225 # delete only if that path really exists
226 if os.path.isdir(rm_path):
226 if os.path.isdir(rm_path):
227 if force_delete:
227 if force_delete:
228 shutil.rmtree(rm_path)
228 shutil.rmtree(rm_path)
229 else:
229 else:
230 # archive that group`
230 # archive that group`
231 _now = datetime.datetime.now()
231 _now = datetime.datetime.now()
232 _ms = str(_now.microsecond).rjust(6, '0')
232 _ms = str(_now.microsecond).rjust(6, '0')
233 _d = 'rm__%s_GROUP_%s' % (
233 _d = 'rm__%s_GROUP_%s' % (
234 _now.strftime('%Y%m%d_%H%M%S_' + _ms), group.name)
234 _now.strftime('%Y%m%d_%H%M%S_' + _ms), group.name)
235 shutil.move(rm_path, os.path.join(self.repos_path, _d))
235 shutil.move(rm_path, os.path.join(self.repos_path, _d))
236
236
237 def create(self, group_name, group_description, owner, just_db=False,
237 def create(self, group_name, group_description, owner, just_db=False,
238 copy_permissions=False, personal=None, commit_early=True):
238 copy_permissions=False, personal=None, commit_early=True):
239
239
240 (group_name_cleaned,
240 (group_name_cleaned,
241 parent_group_name) = RepoGroupModel()._get_group_name_and_parent(group_name)
241 parent_group_name) = RepoGroupModel()._get_group_name_and_parent(group_name)
242
242
243 parent_group = None
243 parent_group = None
244 if parent_group_name:
244 if parent_group_name:
245 parent_group = self._get_repo_group(parent_group_name)
245 parent_group = self._get_repo_group(parent_group_name)
246 if not parent_group:
246 if not parent_group:
247 # we tried to create a nested group, but the parent is not
247 # we tried to create a nested group, but the parent is not
248 # existing
248 # existing
249 raise ValueError(
249 raise ValueError(
250 'Parent group `%s` given in `%s` group name '
250 'Parent group `%s` given in `%s` group name '
251 'is not yet existing.' % (parent_group_name, group_name))
251 'is not yet existing.' % (parent_group_name, group_name))
252
252
253 # because we are doing a cleanup, we need to check if such directory
253 # because we are doing a cleanup, we need to check if such directory
254 # already exists. If we don't do that we can accidentally delete
254 # already exists. If we don't do that we can accidentally delete
255 # existing directory via cleanup that can cause data issues, since
255 # existing directory via cleanup that can cause data issues, since
256 # delete does a folder rename to special syntax later cleanup
256 # delete does a folder rename to special syntax later cleanup
257 # functions can delete this
257 # functions can delete this
258 cleanup_group = self.check_exist_filesystem(group_name,
258 cleanup_group = self.check_exist_filesystem(group_name,
259 exc_on_failure=False)
259 exc_on_failure=False)
260 user = self._get_user(owner)
260 user = self._get_user(owner)
261 if not user:
261 if not user:
262 raise ValueError('Owner %s not found as rhodecode user', owner)
262 raise ValueError('Owner %s not found as rhodecode user', owner)
263
263
264 try:
264 try:
265 new_repo_group = RepoGroup()
265 new_repo_group = RepoGroup()
266 new_repo_group.user = user
266 new_repo_group.user = user
267 new_repo_group.group_description = group_description or group_name
267 new_repo_group.group_description = group_description or group_name
268 new_repo_group.parent_group = parent_group
268 new_repo_group.parent_group = parent_group
269 new_repo_group.group_name = group_name
269 new_repo_group.group_name = group_name
270 new_repo_group.personal = personal
270 new_repo_group.personal = personal
271
271
272 self.sa.add(new_repo_group)
272 self.sa.add(new_repo_group)
273
273
274 # create an ADMIN permission for owner except if we're super admin,
274 # create an ADMIN permission for owner except if we're super admin,
275 # later owner should go into the owner field of groups
275 # later owner should go into the owner field of groups
276 if not user.is_admin:
276 if not user.is_admin:
277 self.grant_user_permission(repo_group=new_repo_group,
277 self.grant_user_permission(repo_group=new_repo_group,
278 user=owner, perm='group.admin')
278 user=owner, perm='group.admin')
279
279
280 if parent_group and copy_permissions:
280 if parent_group and copy_permissions:
281 # copy permissions from parent
281 # copy permissions from parent
282 user_perms = UserRepoGroupToPerm.query() \
282 user_perms = UserRepoGroupToPerm.query() \
283 .filter(UserRepoGroupToPerm.group == parent_group).all()
283 .filter(UserRepoGroupToPerm.group == parent_group).all()
284
284
285 group_perms = UserGroupRepoGroupToPerm.query() \
285 group_perms = UserGroupRepoGroupToPerm.query() \
286 .filter(UserGroupRepoGroupToPerm.group == parent_group).all()
286 .filter(UserGroupRepoGroupToPerm.group == parent_group).all()
287
287
288 for perm in user_perms:
288 for perm in user_perms:
289 # don't copy over the permission for user who is creating
289 # don't copy over the permission for user who is creating
290 # this group, if he is not super admin he get's admin
290 # this group, if he is not super admin he get's admin
291 # permission set above
291 # permission set above
292 if perm.user != user or user.is_admin:
292 if perm.user != user or user.is_admin:
293 UserRepoGroupToPerm.create(
293 UserRepoGroupToPerm.create(
294 perm.user, new_repo_group, perm.permission)
294 perm.user, new_repo_group, perm.permission)
295
295
296 for perm in group_perms:
296 for perm in group_perms:
297 UserGroupRepoGroupToPerm.create(
297 UserGroupRepoGroupToPerm.create(
298 perm.users_group, new_repo_group, perm.permission)
298 perm.users_group, new_repo_group, perm.permission)
299 else:
299 else:
300 perm_obj = self._create_default_perms(new_repo_group)
300 perm_obj = self._create_default_perms(new_repo_group)
301 self.sa.add(perm_obj)
301 self.sa.add(perm_obj)
302
302
303 # now commit the changes, earlier so we are sure everything is in
303 # now commit the changes, earlier so we are sure everything is in
304 # the database.
304 # the database.
305 if commit_early:
305 if commit_early:
306 self.sa.commit()
306 self.sa.commit()
307 if not just_db:
307 if not just_db:
308 self._create_group(new_repo_group.group_name)
308 self._create_group(new_repo_group.group_name)
309
309
310 # trigger the post hook
310 # trigger the post hook
311 from rhodecode.lib.hooks_base import log_create_repository_group
311 from rhodecode.lib.hooks_base import log_create_repository_group
312 repo_group = RepoGroup.get_by_group_name(group_name)
312 repo_group = RepoGroup.get_by_group_name(group_name)
313
313
314 # update repo group commit caches initially
314 # update repo group commit caches initially
315 repo_group.update_commit_cache()
315 repo_group.update_commit_cache()
316
316
317 log_create_repository_group(
317 log_create_repository_group(
318 created_by=user.username, **repo_group.get_dict())
318 created_by=user.username, **repo_group.get_dict())
319
319
320 # Trigger create event.
320 # Trigger create event.
321 events.trigger(events.RepoGroupCreateEvent(repo_group))
321 events.trigger(events.RepoGroupCreateEvent(repo_group))
322
322
323 return new_repo_group
323 return new_repo_group
324 except Exception:
324 except Exception:
325 self.sa.rollback()
325 self.sa.rollback()
326 log.exception('Exception occurred when creating repository group, '
326 log.exception('Exception occurred when creating repository group, '
327 'doing cleanup...')
327 'doing cleanup...')
328 # rollback things manually !
328 # rollback things manually !
329 repo_group = RepoGroup.get_by_group_name(group_name)
329 repo_group = RepoGroup.get_by_group_name(group_name)
330 if repo_group:
330 if repo_group:
331 RepoGroup.delete(repo_group.group_id)
331 RepoGroup.delete(repo_group.group_id)
332 self.sa.commit()
332 self.sa.commit()
333 if cleanup_group:
333 if cleanup_group:
334 RepoGroupModel()._delete_filesystem_group(repo_group)
334 RepoGroupModel()._delete_filesystem_group(repo_group)
335 raise
335 raise
336
336
337 def update_permissions(
337 def update_permissions(
338 self, repo_group, perm_additions=None, perm_updates=None,
338 self, repo_group, perm_additions=None, perm_updates=None,
339 perm_deletions=None, recursive=None, check_perms=True,
339 perm_deletions=None, recursive=None, check_perms=True,
340 cur_user=None):
340 cur_user=None):
341 from rhodecode.model.repo import RepoModel
341 from rhodecode.model.repo import RepoModel
342 from rhodecode.lib.auth import HasUserGroupPermissionAny
342 from rhodecode.lib.auth import HasUserGroupPermissionAny
343
343
344 if not perm_additions:
344 if not perm_additions:
345 perm_additions = []
345 perm_additions = []
346 if not perm_updates:
346 if not perm_updates:
347 perm_updates = []
347 perm_updates = []
348 if not perm_deletions:
348 if not perm_deletions:
349 perm_deletions = []
349 perm_deletions = []
350
350
351 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
351 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
352
352
353 changes = {
353 changes = {
354 'added': [],
354 'added': [],
355 'updated': [],
355 'updated': [],
356 'deleted': []
356 'deleted': []
357 }
357 }
358
358
359 def _set_perm_user(obj, user, perm):
359 def _set_perm_user(obj, user, perm):
360 if isinstance(obj, RepoGroup):
360 if isinstance(obj, RepoGroup):
361 self.grant_user_permission(
361 self.grant_user_permission(
362 repo_group=obj, user=user, perm=perm)
362 repo_group=obj, user=user, perm=perm)
363 elif isinstance(obj, Repository):
363 elif isinstance(obj, Repository):
364 # private repos will not allow to change the default
364 # private repos will not allow to change the default
365 # permissions using recursive mode
365 # permissions using recursive mode
366 if obj.private and user == User.DEFAULT_USER:
366 if obj.private and user == User.DEFAULT_USER:
367 return
367 return
368
368
369 # we set group permission but we have to switch to repo
369 # we set group permission but we have to switch to repo
370 # permission
370 # permission
371 perm = perm.replace('group.', 'repository.')
371 perm = perm.replace('group.', 'repository.')
372 RepoModel().grant_user_permission(
372 RepoModel().grant_user_permission(
373 repo=obj, user=user, perm=perm)
373 repo=obj, user=user, perm=perm)
374
374
375 def _set_perm_group(obj, users_group, perm):
375 def _set_perm_group(obj, users_group, perm):
376 if isinstance(obj, RepoGroup):
376 if isinstance(obj, RepoGroup):
377 self.grant_user_group_permission(
377 self.grant_user_group_permission(
378 repo_group=obj, group_name=users_group, perm=perm)
378 repo_group=obj, group_name=users_group, perm=perm)
379 elif isinstance(obj, Repository):
379 elif isinstance(obj, Repository):
380 # we set group permission but we have to switch to repo
380 # we set group permission but we have to switch to repo
381 # permission
381 # permission
382 perm = perm.replace('group.', 'repository.')
382 perm = perm.replace('group.', 'repository.')
383 RepoModel().grant_user_group_permission(
383 RepoModel().grant_user_group_permission(
384 repo=obj, group_name=users_group, perm=perm)
384 repo=obj, group_name=users_group, perm=perm)
385
385
386 def _revoke_perm_user(obj, user):
386 def _revoke_perm_user(obj, user):
387 if isinstance(obj, RepoGroup):
387 if isinstance(obj, RepoGroup):
388 self.revoke_user_permission(repo_group=obj, user=user)
388 self.revoke_user_permission(repo_group=obj, user=user)
389 elif isinstance(obj, Repository):
389 elif isinstance(obj, Repository):
390 RepoModel().revoke_user_permission(repo=obj, user=user)
390 RepoModel().revoke_user_permission(repo=obj, user=user)
391
391
392 def _revoke_perm_group(obj, user_group):
392 def _revoke_perm_group(obj, user_group):
393 if isinstance(obj, RepoGroup):
393 if isinstance(obj, RepoGroup):
394 self.revoke_user_group_permission(
394 self.revoke_user_group_permission(
395 repo_group=obj, group_name=user_group)
395 repo_group=obj, group_name=user_group)
396 elif isinstance(obj, Repository):
396 elif isinstance(obj, Repository):
397 RepoModel().revoke_user_group_permission(
397 RepoModel().revoke_user_group_permission(
398 repo=obj, group_name=user_group)
398 repo=obj, group_name=user_group)
399
399
400 # start updates
400 # start updates
401 log.debug('Now updating permissions for %s in recursive mode:%s',
401 log.debug('Now updating permissions for %s in recursive mode:%s',
402 repo_group, recursive)
402 repo_group, recursive)
403
403
404 # initialize check function, we'll call that multiple times
404 # initialize check function, we'll call that multiple times
405 has_group_perm = HasUserGroupPermissionAny(*req_perms)
405 has_group_perm = HasUserGroupPermissionAny(*req_perms)
406
406
407 for obj in repo_group.recursive_groups_and_repos():
407 for obj in repo_group.recursive_groups_and_repos():
408 # iterated obj is an instance of a repos group or repository in
408 # iterated obj is an instance of a repos group or repository in
409 # that group, recursive option can be: none, repos, groups, all
409 # that group, recursive option can be: none, repos, groups, all
410 if recursive == 'all':
410 if recursive == 'all':
411 obj = obj
411 obj = obj
412 elif recursive == 'repos':
412 elif recursive == 'repos':
413 # skip groups, other than this one
413 # skip groups, other than this one
414 if isinstance(obj, RepoGroup) and not obj == repo_group:
414 if isinstance(obj, RepoGroup) and not obj == repo_group:
415 continue
415 continue
416 elif recursive == 'groups':
416 elif recursive == 'groups':
417 # skip repos
417 # skip repos
418 if isinstance(obj, Repository):
418 if isinstance(obj, Repository):
419 continue
419 continue
420 else: # recursive == 'none':
420 else: # recursive == 'none':
421 # DEFAULT option - don't apply to iterated objects
421 # DEFAULT option - don't apply to iterated objects
422 # also we do a break at the end of this loop. if we are not
422 # also we do a break at the end of this loop. if we are not
423 # in recursive mode
423 # in recursive mode
424 obj = repo_group
424 obj = repo_group
425
425
426 change_obj = obj.get_api_data()
426 change_obj = obj.get_api_data()
427
427
428 # update permissions
428 # update permissions
429 for member_id, perm, member_type in perm_updates:
429 for member_id, perm, member_type in perm_updates:
430 member_id = int(member_id)
430 member_id = int(member_id)
431 if member_type == 'user':
431 if member_type == 'user':
432 member_name = User.get(member_id).username
432 member_name = User.get(member_id).username
433 # this updates also current one if found
433 # this updates also current one if found
434 _set_perm_user(obj, user=member_id, perm=perm)
434 _set_perm_user(obj, user=member_id, perm=perm)
435 elif member_type == 'user_group':
435 elif member_type == 'user_group':
436 member_name = UserGroup.get(member_id).users_group_name
436 member_name = UserGroup.get(member_id).users_group_name
437 if not check_perms or has_group_perm(member_name,
437 if not check_perms or has_group_perm(member_name,
438 user=cur_user):
438 user=cur_user):
439 _set_perm_group(obj, users_group=member_id, perm=perm)
439 _set_perm_group(obj, users_group=member_id, perm=perm)
440 else:
440 else:
441 raise ValueError("member_type must be 'user' or 'user_group' "
441 raise ValueError("member_type must be 'user' or 'user_group' "
442 "got {} instead".format(member_type))
442 "got {} instead".format(member_type))
443
443
444 changes['updated'].append(
444 changes['updated'].append(
445 {'change_obj': change_obj, 'type': member_type,
445 {'change_obj': change_obj, 'type': member_type,
446 'id': member_id, 'name': member_name, 'new_perm': perm})
446 'id': member_id, 'name': member_name, 'new_perm': perm})
447
447
448 # set new permissions
448 # set new permissions
449 for member_id, perm, member_type in perm_additions:
449 for member_id, perm, member_type in perm_additions:
450 member_id = int(member_id)
450 member_id = int(member_id)
451 if member_type == 'user':
451 if member_type == 'user':
452 member_name = User.get(member_id).username
452 member_name = User.get(member_id).username
453 _set_perm_user(obj, user=member_id, perm=perm)
453 _set_perm_user(obj, user=member_id, perm=perm)
454 elif member_type == 'user_group':
454 elif member_type == 'user_group':
455 # check if we have permissions to alter this usergroup
455 # check if we have permissions to alter this usergroup
456 member_name = UserGroup.get(member_id).users_group_name
456 member_name = UserGroup.get(member_id).users_group_name
457 if not check_perms or has_group_perm(member_name,
457 if not check_perms or has_group_perm(member_name,
458 user=cur_user):
458 user=cur_user):
459 _set_perm_group(obj, users_group=member_id, perm=perm)
459 _set_perm_group(obj, users_group=member_id, perm=perm)
460 else:
460 else:
461 raise ValueError("member_type must be 'user' or 'user_group' "
461 raise ValueError("member_type must be 'user' or 'user_group' "
462 "got {} instead".format(member_type))
462 "got {} instead".format(member_type))
463
463
464 changes['added'].append(
464 changes['added'].append(
465 {'change_obj': change_obj, 'type': member_type,
465 {'change_obj': change_obj, 'type': member_type,
466 'id': member_id, 'name': member_name, 'new_perm': perm})
466 'id': member_id, 'name': member_name, 'new_perm': perm})
467
467
468 # delete permissions
468 # delete permissions
469 for member_id, perm, member_type in perm_deletions:
469 for member_id, perm, member_type in perm_deletions:
470 member_id = int(member_id)
470 member_id = int(member_id)
471 if member_type == 'user':
471 if member_type == 'user':
472 member_name = User.get(member_id).username
472 member_name = User.get(member_id).username
473 _revoke_perm_user(obj, user=member_id)
473 _revoke_perm_user(obj, user=member_id)
474 elif member_type == 'user_group':
474 elif member_type == 'user_group':
475 # check if we have permissions to alter this usergroup
475 # check if we have permissions to alter this usergroup
476 member_name = UserGroup.get(member_id).users_group_name
476 member_name = UserGroup.get(member_id).users_group_name
477 if not check_perms or has_group_perm(member_name,
477 if not check_perms or has_group_perm(member_name,
478 user=cur_user):
478 user=cur_user):
479 _revoke_perm_group(obj, user_group=member_id)
479 _revoke_perm_group(obj, user_group=member_id)
480 else:
480 else:
481 raise ValueError("member_type must be 'user' or 'user_group' "
481 raise ValueError("member_type must be 'user' or 'user_group' "
482 "got {} instead".format(member_type))
482 "got {} instead".format(member_type))
483
483
484 changes['deleted'].append(
484 changes['deleted'].append(
485 {'change_obj': change_obj, 'type': member_type,
485 {'change_obj': change_obj, 'type': member_type,
486 'id': member_id, 'name': member_name, 'new_perm': perm})
486 'id': member_id, 'name': member_name, 'new_perm': perm})
487
487
488 # if it's not recursive call for all,repos,groups
488 # if it's not recursive call for all,repos,groups
489 # break the loop and don't proceed with other changes
489 # break the loop and don't proceed with other changes
490 if recursive not in ['all', 'repos', 'groups']:
490 if recursive not in ['all', 'repos', 'groups']:
491 break
491 break
492
492
493 return changes
493 return changes
494
494
495 def update(self, repo_group, form_data):
495 def update(self, repo_group, form_data):
496 try:
496 try:
497 repo_group = self._get_repo_group(repo_group)
497 repo_group = self._get_repo_group(repo_group)
498 old_path = repo_group.full_path
498 old_path = repo_group.full_path
499
499
500 # change properties
500 # change properties
501 if 'group_description' in form_data:
501 if 'group_description' in form_data:
502 repo_group.group_description = form_data['group_description']
502 repo_group.group_description = form_data['group_description']
503
503
504 if 'enable_locking' in form_data:
504 if 'enable_locking' in form_data:
505 repo_group.enable_locking = form_data['enable_locking']
505 repo_group.enable_locking = form_data['enable_locking']
506
506
507 if 'group_parent_id' in form_data:
507 if 'group_parent_id' in form_data:
508 parent_group = (
508 parent_group = (
509 self._get_repo_group(form_data['group_parent_id']))
509 self._get_repo_group(form_data['group_parent_id']))
510 repo_group.group_parent_id = (
510 repo_group.group_parent_id = (
511 parent_group.group_id if parent_group else None)
511 parent_group.group_id if parent_group else None)
512 repo_group.parent_group = parent_group
512 repo_group.parent_group = parent_group
513
513
514 # mikhail: to update the full_path, we have to explicitly
514 # mikhail: to update the full_path, we have to explicitly
515 # update group_name
515 # update group_name
516 group_name = form_data.get('group_name', repo_group.name)
516 group_name = form_data.get('group_name', repo_group.name)
517 repo_group.group_name = repo_group.get_new_name(group_name)
517 repo_group.group_name = repo_group.get_new_name(group_name)
518
518
519 new_path = repo_group.full_path
519 new_path = repo_group.full_path
520
520
521 if 'user' in form_data:
521 if 'user' in form_data:
522 repo_group.user = User.get_by_username(form_data['user'])
522 repo_group.user = User.get_by_username(form_data['user'])
523
523
524 self.sa.add(repo_group)
524 self.sa.add(repo_group)
525
525
526 # iterate over all members of this groups and do fixes
526 # iterate over all members of this groups and do fixes
527 # set locking if given
527 # set locking if given
528 # if obj is a repoGroup also fix the name of the group according
528 # if obj is a repoGroup also fix the name of the group according
529 # to the parent
529 # to the parent
530 # if obj is a Repo fix it's name
530 # if obj is a Repo fix it's name
531 # this can be potentially heavy operation
531 # this can be potentially heavy operation
532 for obj in repo_group.recursive_groups_and_repos():
532 for obj in repo_group.recursive_groups_and_repos():
533 # set the value from it's parent
533 # set the value from it's parent
534 obj.enable_locking = repo_group.enable_locking
534 obj.enable_locking = repo_group.enable_locking
535 if isinstance(obj, RepoGroup):
535 if isinstance(obj, RepoGroup):
536 new_name = obj.get_new_name(obj.name)
536 new_name = obj.get_new_name(obj.name)
537 log.debug('Fixing group %s to new name %s',
537 log.debug('Fixing group %s to new name %s',
538 obj.group_name, new_name)
538 obj.group_name, new_name)
539 obj.group_name = new_name
539 obj.group_name = new_name
540
540
541 elif isinstance(obj, Repository):
541 elif isinstance(obj, Repository):
542 # we need to get all repositories from this new group and
542 # we need to get all repositories from this new group and
543 # rename them accordingly to new group path
543 # rename them accordingly to new group path
544 new_name = obj.get_new_name(obj.just_name)
544 new_name = obj.get_new_name(obj.just_name)
545 log.debug('Fixing repo %s to new name %s',
545 log.debug('Fixing repo %s to new name %s',
546 obj.repo_name, new_name)
546 obj.repo_name, new_name)
547 obj.repo_name = new_name
547 obj.repo_name = new_name
548
548
549 self.sa.add(obj)
549 self.sa.add(obj)
550
550
551 self._rename_group(old_path, new_path)
551 self._rename_group(old_path, new_path)
552
552
553 # Trigger update event.
553 # Trigger update event.
554 events.trigger(events.RepoGroupUpdateEvent(repo_group))
554 events.trigger(events.RepoGroupUpdateEvent(repo_group))
555
555
556 return repo_group
556 return repo_group
557 except Exception:
557 except Exception:
558 log.error(traceback.format_exc())
558 log.error(traceback.format_exc())
559 raise
559 raise
560
560
561 def delete(self, repo_group, force_delete=False, fs_remove=True):
561 def delete(self, repo_group, force_delete=False, fs_remove=True):
562 repo_group = self._get_repo_group(repo_group)
562 repo_group = self._get_repo_group(repo_group)
563 if not repo_group:
563 if not repo_group:
564 return False
564 return False
565 try:
565 try:
566 self.sa.delete(repo_group)
566 self.sa.delete(repo_group)
567 if fs_remove:
567 if fs_remove:
568 self._delete_filesystem_group(repo_group, force_delete)
568 self._delete_filesystem_group(repo_group, force_delete)
569 else:
569 else:
570 log.debug('skipping removal from filesystem')
570 log.debug('skipping removal from filesystem')
571
571
572 # Trigger delete event.
572 # Trigger delete event.
573 events.trigger(events.RepoGroupDeleteEvent(repo_group))
573 events.trigger(events.RepoGroupDeleteEvent(repo_group))
574 return True
574 return True
575
575
576 except Exception:
576 except Exception:
577 log.error('Error removing repo_group %s', repo_group)
577 log.error('Error removing repo_group %s', repo_group)
578 raise
578 raise
579
579
580 def grant_user_permission(self, repo_group, user, perm):
580 def grant_user_permission(self, repo_group, user, perm):
581 """
581 """
582 Grant permission for user on given repository group, or update
582 Grant permission for user on given repository group, or update
583 existing one if found
583 existing one if found
584
584
585 :param repo_group: Instance of RepoGroup, repositories_group_id,
585 :param repo_group: Instance of RepoGroup, repositories_group_id,
586 or repositories_group name
586 or repositories_group name
587 :param user: Instance of User, user_id or username
587 :param user: Instance of User, user_id or username
588 :param perm: Instance of Permission, or permission_name
588 :param perm: Instance of Permission, or permission_name
589 """
589 """
590
590
591 repo_group = self._get_repo_group(repo_group)
591 repo_group = self._get_repo_group(repo_group)
592 user = self._get_user(user)
592 user = self._get_user(user)
593 permission = self._get_perm(perm)
593 permission = self._get_perm(perm)
594
594
595 # check if we have that permission already
595 # check if we have that permission already
596 obj = self.sa.query(UserRepoGroupToPerm)\
596 obj = self.sa.query(UserRepoGroupToPerm)\
597 .filter(UserRepoGroupToPerm.user == user)\
597 .filter(UserRepoGroupToPerm.user == user)\
598 .filter(UserRepoGroupToPerm.group == repo_group)\
598 .filter(UserRepoGroupToPerm.group == repo_group)\
599 .scalar()
599 .scalar()
600 if obj is None:
600 if obj is None:
601 # create new !
601 # create new !
602 obj = UserRepoGroupToPerm()
602 obj = UserRepoGroupToPerm()
603 obj.group = repo_group
603 obj.group = repo_group
604 obj.user = user
604 obj.user = user
605 obj.permission = permission
605 obj.permission = permission
606 self.sa.add(obj)
606 self.sa.add(obj)
607 log.debug('Granted perm %s to %s on %s', perm, user, repo_group)
607 log.debug('Granted perm %s to %s on %s', perm, user, repo_group)
608 action_logger_generic(
608 action_logger_generic(
609 'granted permission: {} to user: {} on repogroup: {}'.format(
609 'granted permission: {} to user: {} on repogroup: {}'.format(
610 perm, user, repo_group), namespace='security.repogroup')
610 perm, user, repo_group), namespace='security.repogroup')
611 return obj
611 return obj
612
612
613 def revoke_user_permission(self, repo_group, user):
613 def revoke_user_permission(self, repo_group, user):
614 """
614 """
615 Revoke permission for user on given repository group
615 Revoke permission for user on given repository group
616
616
617 :param repo_group: Instance of RepoGroup, repositories_group_id,
617 :param repo_group: Instance of RepoGroup, repositories_group_id,
618 or repositories_group name
618 or repositories_group name
619 :param user: Instance of User, user_id or username
619 :param user: Instance of User, user_id or username
620 """
620 """
621
621
622 repo_group = self._get_repo_group(repo_group)
622 repo_group = self._get_repo_group(repo_group)
623 user = self._get_user(user)
623 user = self._get_user(user)
624
624
625 obj = self.sa.query(UserRepoGroupToPerm)\
625 obj = self.sa.query(UserRepoGroupToPerm)\
626 .filter(UserRepoGroupToPerm.user == user)\
626 .filter(UserRepoGroupToPerm.user == user)\
627 .filter(UserRepoGroupToPerm.group == repo_group)\
627 .filter(UserRepoGroupToPerm.group == repo_group)\
628 .scalar()
628 .scalar()
629 if obj:
629 if obj:
630 self.sa.delete(obj)
630 self.sa.delete(obj)
631 log.debug('Revoked perm on %s on %s', repo_group, user)
631 log.debug('Revoked perm on %s on %s', repo_group, user)
632 action_logger_generic(
632 action_logger_generic(
633 'revoked permission from user: {} on repogroup: {}'.format(
633 'revoked permission from user: {} on repogroup: {}'.format(
634 user, repo_group), namespace='security.repogroup')
634 user, repo_group), namespace='security.repogroup')
635
635
636 def grant_user_group_permission(self, repo_group, group_name, perm):
636 def grant_user_group_permission(self, repo_group, group_name, perm):
637 """
637 """
638 Grant permission for user group on given repository group, or update
638 Grant permission for user group on given repository group, or update
639 existing one if found
639 existing one if found
640
640
641 :param repo_group: Instance of RepoGroup, repositories_group_id,
641 :param repo_group: Instance of RepoGroup, repositories_group_id,
642 or repositories_group name
642 or repositories_group name
643 :param group_name: Instance of UserGroup, users_group_id,
643 :param group_name: Instance of UserGroup, users_group_id,
644 or user group name
644 or user group name
645 :param perm: Instance of Permission, or permission_name
645 :param perm: Instance of Permission, or permission_name
646 """
646 """
647 repo_group = self._get_repo_group(repo_group)
647 repo_group = self._get_repo_group(repo_group)
648 group_name = self._get_user_group(group_name)
648 group_name = self._get_user_group(group_name)
649 permission = self._get_perm(perm)
649 permission = self._get_perm(perm)
650
650
651 # check if we have that permission already
651 # check if we have that permission already
652 obj = self.sa.query(UserGroupRepoGroupToPerm)\
652 obj = self.sa.query(UserGroupRepoGroupToPerm)\
653 .filter(UserGroupRepoGroupToPerm.group == repo_group)\
653 .filter(UserGroupRepoGroupToPerm.group == repo_group)\
654 .filter(UserGroupRepoGroupToPerm.users_group == group_name)\
654 .filter(UserGroupRepoGroupToPerm.users_group == group_name)\
655 .scalar()
655 .scalar()
656
656
657 if obj is None:
657 if obj is None:
658 # create new
658 # create new
659 obj = UserGroupRepoGroupToPerm()
659 obj = UserGroupRepoGroupToPerm()
660
660
661 obj.group = repo_group
661 obj.group = repo_group
662 obj.users_group = group_name
662 obj.users_group = group_name
663 obj.permission = permission
663 obj.permission = permission
664 self.sa.add(obj)
664 self.sa.add(obj)
665 log.debug('Granted perm %s to %s on %s', perm, group_name, repo_group)
665 log.debug('Granted perm %s to %s on %s', perm, group_name, repo_group)
666 action_logger_generic(
666 action_logger_generic(
667 'granted permission: {} to usergroup: {} on repogroup: {}'.format(
667 'granted permission: {} to usergroup: {} on repogroup: {}'.format(
668 perm, group_name, repo_group), namespace='security.repogroup')
668 perm, group_name, repo_group), namespace='security.repogroup')
669 return obj
669 return obj
670
670
671 def revoke_user_group_permission(self, repo_group, group_name):
671 def revoke_user_group_permission(self, repo_group, group_name):
672 """
672 """
673 Revoke permission for user group on given repository group
673 Revoke permission for user group on given repository group
674
674
675 :param repo_group: Instance of RepoGroup, repositories_group_id,
675 :param repo_group: Instance of RepoGroup, repositories_group_id,
676 or repositories_group name
676 or repositories_group name
677 :param group_name: Instance of UserGroup, users_group_id,
677 :param group_name: Instance of UserGroup, users_group_id,
678 or user group name
678 or user group name
679 """
679 """
680 repo_group = self._get_repo_group(repo_group)
680 repo_group = self._get_repo_group(repo_group)
681 group_name = self._get_user_group(group_name)
681 group_name = self._get_user_group(group_name)
682
682
683 obj = self.sa.query(UserGroupRepoGroupToPerm)\
683 obj = self.sa.query(UserGroupRepoGroupToPerm)\
684 .filter(UserGroupRepoGroupToPerm.group == repo_group)\
684 .filter(UserGroupRepoGroupToPerm.group == repo_group)\
685 .filter(UserGroupRepoGroupToPerm.users_group == group_name)\
685 .filter(UserGroupRepoGroupToPerm.users_group == group_name)\
686 .scalar()
686 .scalar()
687 if obj:
687 if obj:
688 self.sa.delete(obj)
688 self.sa.delete(obj)
689 log.debug('Revoked perm to %s on %s', repo_group, group_name)
689 log.debug('Revoked perm to %s on %s', repo_group, group_name)
690 action_logger_generic(
690 action_logger_generic(
691 'revoked permission from usergroup: {} on repogroup: {}'.format(
691 'revoked permission from usergroup: {} on repogroup: {}'.format(
692 group_name, repo_group), namespace='security.repogroup')
692 group_name, repo_group), namespace='security.repogroup')
693
693
694 @classmethod
694 @classmethod
695 def update_commit_cache(cls, repo_groups=None):
695 def update_commit_cache(cls, repo_groups=None):
696 if not repo_groups:
696 if not repo_groups:
697 repo_groups = RepoGroup.getAll()
697 repo_groups = RepoGroup.getAll()
698 for repo_group in repo_groups:
698 for repo_group in repo_groups:
699 repo_group.update_commit_cache()
699 repo_group.update_commit_cache()
700
700
701
701
702
702
703 def get_repo_groups_as_dict(self, repo_group_list=None, admin=False,
703 def get_repo_groups_as_dict(self, repo_group_list=None, admin=False,
704 super_user_actions=False):
704 super_user_actions=False):
705
705
706 from pyramid.threadlocal import get_current_request
706 from pyramid.threadlocal import get_current_request
707 _render = get_current_request().get_partial_renderer(
707 _render = get_current_request().get_partial_renderer(
708 'rhodecode:templates/data_table/_dt_elements.mako')
708 'rhodecode:templates/data_table/_dt_elements.mako')
709 c = _render.get_call_context()
709 c = _render.get_call_context()
710 h = _render.get_helpers()
710 h = _render.get_helpers()
711
711
712 def quick_menu(repo_group_name):
712 def quick_menu(repo_group_name):
713 return _render('quick_repo_group_menu', repo_group_name)
713 return _render('quick_repo_group_menu', repo_group_name)
714
714
715 def repo_group_lnk(repo_group_name):
715 def repo_group_lnk(repo_group_name):
716 return _render('repo_group_name', repo_group_name)
716 return _render('repo_group_name', repo_group_name)
717
717
718 def last_change(last_change):
718 def last_change(last_change):
719 if admin and isinstance(last_change, datetime.datetime) and not last_change.tzinfo:
719 if admin and isinstance(last_change, datetime.datetime) and not last_change.tzinfo:
720 ts = time.time()
720 ts = time.time()
721 utc_offset = (datetime.datetime.fromtimestamp(ts)
721 utc_offset = (datetime.datetime.fromtimestamp(ts)
722 - datetime.datetime.utcfromtimestamp(ts)).total_seconds()
722 - datetime.datetime.utcfromtimestamp(ts)).total_seconds()
723 last_change = last_change + datetime.timedelta(seconds=utc_offset)
723 last_change = last_change + datetime.timedelta(seconds=utc_offset)
724 return _render("last_change", last_change)
724 return _render("last_change", last_change)
725
725
726 def desc(desc, personal):
726 def desc(desc, personal):
727 return _render(
727 return _render(
728 'repo_group_desc', desc, personal, c.visual.stylify_metatags)
728 'repo_group_desc', desc, personal, c.visual.stylify_metatags)
729
729
730 def repo_group_actions(repo_group_id, repo_group_name, gr_count):
730 def repo_group_actions(repo_group_id, repo_group_name, gr_count):
731 return _render(
731 return _render(
732 'repo_group_actions', repo_group_id, repo_group_name, gr_count)
732 'repo_group_actions', repo_group_id, repo_group_name, gr_count)
733
733
734 def repo_group_name(repo_group_name, children_groups):
734 def repo_group_name(repo_group_name, children_groups):
735 return _render("repo_group_name", repo_group_name, children_groups)
735 return _render("repo_group_name", repo_group_name, children_groups)
736
736
737 def user_profile(username):
737 def user_profile(username):
738 return _render('user_profile', username)
738 return _render('user_profile', username)
739
739
740 repo_group_data = []
740 repo_group_data = []
741 for group in repo_group_list:
741 for group in repo_group_list:
742 # NOTE(marcink): because we use only raw column we need to load it like that
742 # NOTE(marcink): because we use only raw column we need to load it like that
743 changeset_cache = RepoGroup._load_changeset_cache(
743 changeset_cache = RepoGroup._load_changeset_cache(
744 '', group._changeset_cache)
744 '', group._changeset_cache)
745 last_commit_change = RepoGroup._load_commit_change(changeset_cache)
745 last_commit_change = RepoGroup._load_commit_change(changeset_cache)
746 row = {
746 row = {
747 "menu": quick_menu(group.group_name),
747 "menu": quick_menu(group.group_name),
748 "name": repo_group_lnk(group.group_name),
748 "name": repo_group_lnk(group.group_name),
749 "name_raw": group.group_name,
749 "name_raw": group.group_name,
750
750
751 "last_change": last_change(last_commit_change),
751 "last_change": last_change(last_commit_change),
752 "last_change_raw": datetime_to_time(last_commit_change),
753
752
754 "last_changeset": "",
753 "last_changeset": "",
755 "last_changeset_raw": "",
754 "last_changeset_raw": "",
756
755
757 "desc": desc(group.group_description, group.personal),
756 "desc": desc(group.group_description, group.personal),
758 "top_level_repos": 0,
757 "top_level_repos": 0,
759 "owner": user_profile(group.User.username)
758 "owner": user_profile(group.User.username)
760 }
759 }
761 if admin:
760 if admin:
762 repo_count = group.repositories.count()
761 repo_count = group.repositories.count()
763 children_groups = map(
762 children_groups = map(
764 h.safe_unicode,
763 h.safe_unicode,
765 itertools.chain((g.name for g in group.parents),
764 itertools.chain((g.name for g in group.parents),
766 (x.name for x in [group])))
765 (x.name for x in [group])))
767 row.update({
766 row.update({
768 "action": repo_group_actions(
767 "action": repo_group_actions(
769 group.group_id, group.group_name, repo_count),
768 group.group_id, group.group_name, repo_count),
770 "top_level_repos": repo_count,
769 "top_level_repos": repo_count,
771 "name": repo_group_name(group.group_name, children_groups),
770 "name": repo_group_name(group.group_name, children_groups),
772
771
773 })
772 })
774 repo_group_data.append(row)
773 repo_group_data.append(row)
775
774
776 return repo_group_data
775 return repo_group_data
777
776
778 def get_repo_groups_data_table(
777 def get_repo_groups_data_table(
779 self, draw, start, limit,
778 self, draw, start, limit,
780 search_q, order_by, order_dir,
779 search_q, order_by, order_dir,
781 auth_user, repo_group_id):
780 auth_user, repo_group_id):
782 from rhodecode.model.scm import RepoGroupList
781 from rhodecode.model.scm import RepoGroupList
783
782
784 _perms = ['group.read', 'group.write', 'group.admin']
783 _perms = ['group.read', 'group.write', 'group.admin']
785 repo_groups = RepoGroup.query() \
784 repo_groups = RepoGroup.query() \
786 .filter(RepoGroup.group_parent_id == repo_group_id) \
785 .filter(RepoGroup.group_parent_id == repo_group_id) \
787 .all()
786 .all()
788 auth_repo_group_list = RepoGroupList(
787 auth_repo_group_list = RepoGroupList(
789 repo_groups, perm_set=_perms,
788 repo_groups, perm_set=_perms,
790 extra_kwargs=dict(user=auth_user))
789 extra_kwargs=dict(user=auth_user))
791
790
792 allowed_ids = [-1]
791 allowed_ids = [-1]
793 for repo_group in auth_repo_group_list:
792 for repo_group in auth_repo_group_list:
794 allowed_ids.append(repo_group.group_id)
793 allowed_ids.append(repo_group.group_id)
795
794
796 repo_groups_data_total_count = RepoGroup.query() \
795 repo_groups_data_total_count = RepoGroup.query() \
797 .filter(RepoGroup.group_parent_id == repo_group_id) \
796 .filter(RepoGroup.group_parent_id == repo_group_id) \
798 .filter(or_(
797 .filter(or_(
799 # generate multiple IN to fix limitation problems
798 # generate multiple IN to fix limitation problems
800 *in_filter_generator(RepoGroup.group_id, allowed_ids))
799 *in_filter_generator(RepoGroup.group_id, allowed_ids))
801 ) \
800 ) \
802 .count()
801 .count()
803
802
804 base_q = Session.query(
803 base_q = Session.query(
805 RepoGroup.group_name,
804 RepoGroup.group_name,
806 RepoGroup.group_name_hash,
805 RepoGroup.group_name_hash,
807 RepoGroup.group_description,
806 RepoGroup.group_description,
808 RepoGroup.group_id,
807 RepoGroup.group_id,
809 RepoGroup.personal,
808 RepoGroup.personal,
810 RepoGroup.updated_on,
809 RepoGroup.updated_on,
811 RepoGroup._changeset_cache,
810 RepoGroup._changeset_cache,
812 User,
811 User,
813 ) \
812 ) \
814 .filter(RepoGroup.group_parent_id == repo_group_id) \
813 .filter(RepoGroup.group_parent_id == repo_group_id) \
815 .filter(or_(
814 .filter(or_(
816 # generate multiple IN to fix limitation problems
815 # generate multiple IN to fix limitation problems
817 *in_filter_generator(RepoGroup.group_id, allowed_ids))
816 *in_filter_generator(RepoGroup.group_id, allowed_ids))
818 ) \
817 ) \
819 .join(User, User.user_id == RepoGroup.user_id) \
818 .join(User, User.user_id == RepoGroup.user_id) \
820 .group_by(RepoGroup, User)
819 .group_by(RepoGroup, User)
821
820
822 repo_groups_data_total_filtered_count = base_q.count()
821 repo_groups_data_total_filtered_count = base_q.count()
823
822
824 sort_defined = False
823 sort_defined = False
825
824
826 if order_by == 'group_name':
825 if order_by == 'group_name':
827 sort_col = func.lower(RepoGroup.group_name)
826 sort_col = func.lower(RepoGroup.group_name)
828 sort_defined = True
827 sort_defined = True
829 elif order_by == 'user_username':
828 elif order_by == 'user_username':
830 sort_col = User.username
829 sort_col = User.username
831 else:
830 else:
832 sort_col = getattr(RepoGroup, order_by, None)
831 sort_col = getattr(RepoGroup, order_by, None)
833
832
834 if sort_defined or sort_col:
833 if sort_defined or sort_col:
835 if order_dir == 'asc':
834 if order_dir == 'asc':
836 sort_col = sort_col.asc()
835 sort_col = sort_col.asc()
837 else:
836 else:
838 sort_col = sort_col.desc()
837 sort_col = sort_col.desc()
839
838
840 base_q = base_q.order_by(sort_col)
839 base_q = base_q.order_by(sort_col)
841 base_q = base_q.offset(start).limit(limit)
840 base_q = base_q.offset(start).limit(limit)
842
841
843 repo_group_list = base_q.all()
842 repo_group_list = base_q.all()
844
843
845 repo_groups_data = RepoGroupModel().get_repo_groups_as_dict(
844 repo_groups_data = RepoGroupModel().get_repo_groups_as_dict(
846 repo_group_list=repo_group_list, admin=False)
845 repo_group_list=repo_group_list, admin=False)
847
846
848 data = ({
847 data = ({
849 'draw': draw,
848 'draw': draw,
850 'data': repo_groups_data,
849 'data': repo_groups_data,
851 'recordsTotal': repo_groups_data_total_count,
850 'recordsTotal': repo_groups_data_total_count,
852 'recordsFiltered': repo_groups_data_total_filtered_count,
851 'recordsFiltered': repo_groups_data_total_filtered_count,
853 })
852 })
854 return data
853 return data
855
854
856 def _get_defaults(self, repo_group_name):
855 def _get_defaults(self, repo_group_name):
857 repo_group = RepoGroup.get_by_group_name(repo_group_name)
856 repo_group = RepoGroup.get_by_group_name(repo_group_name)
858
857
859 if repo_group is None:
858 if repo_group is None:
860 return None
859 return None
861
860
862 defaults = repo_group.get_dict()
861 defaults = repo_group.get_dict()
863 defaults['repo_group_name'] = repo_group.name
862 defaults['repo_group_name'] = repo_group.name
864 defaults['repo_group_description'] = repo_group.group_description
863 defaults['repo_group_description'] = repo_group.group_description
865 defaults['repo_group_enable_locking'] = repo_group.enable_locking
864 defaults['repo_group_enable_locking'] = repo_group.enable_locking
866
865
867 # we use -1 as this is how in HTML, we mark an empty group
866 # we use -1 as this is how in HTML, we mark an empty group
868 defaults['repo_group'] = defaults['group_parent_id'] or -1
867 defaults['repo_group'] = defaults['group_parent_id'] or -1
869
868
870 # fill owner
869 # fill owner
871 if repo_group.user:
870 if repo_group.user:
872 defaults.update({'user': repo_group.user.username})
871 defaults.update({'user': repo_group.user.username})
873 else:
872 else:
874 replacement_user = User.get_first_super_admin().username
873 replacement_user = User.get_first_super_admin().username
875 defaults.update({'user': replacement_user})
874 defaults.update({'user': replacement_user})
876
875
877 return defaults
876 return defaults
@@ -1,117 +1,117 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Repository groups administration')}
5 ${_('Repository groups administration')}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()"></%def>
11 <%def name="breadcrumbs_links()"></%def>
12
12
13 <%def name="menu_bar_nav()">
13 <%def name="menu_bar_nav()">
14 ${self.menu_items(active='admin')}
14 ${self.menu_items(active='admin')}
15 </%def>
15 </%def>
16
16
17 <%def name="menu_bar_subnav()">
17 <%def name="menu_bar_subnav()">
18 ${self.admin_menu(active='repository_groups')}
18 ${self.admin_menu(active='repository_groups')}
19 </%def>
19 </%def>
20
20
21 <%def name="main()">
21 <%def name="main()">
22 <div class="box">
22 <div class="box">
23
23
24 <div class="title">
24 <div class="title">
25 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
25 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
26 <span id="repo_group_count"></span>
26 <span id="repo_group_count"></span>
27
27
28 <ul class="links">
28 <ul class="links">
29 %if c.can_create_repo_group:
29 %if c.can_create_repo_group:
30 <li>
30 <li>
31 <a href="${h.route_path('repo_group_new')}" class="btn btn-small btn-success">${_(u'Add Repository Group')}</a>
31 <a href="${h.route_path('repo_group_new')}" class="btn btn-small btn-success">${_(u'Add Repository Group')}</a>
32 </li>
32 </li>
33 %endif
33 %endif
34 </ul>
34 </ul>
35 </div>
35 </div>
36 <div id="repos_list_wrap">
36 <div id="repos_list_wrap">
37 <table id="group_list_table" class="display"></table>
37 <table id="group_list_table" class="display"></table>
38 </div>
38 </div>
39 </div>
39 </div>
40
40
41 <script>
41 <script>
42 $(document).ready(function() {
42 $(document).ready(function() {
43 var $repoGroupsListTable = $('#group_list_table');
43 var $repoGroupsListTable = $('#group_list_table');
44
44
45 // repo group list
45 // repo group list
46 $repoGroupsListTable.DataTable({
46 $repoGroupsListTable.DataTable({
47 processing: true,
47 processing: true,
48 serverSide: true,
48 serverSide: true,
49 ajax: {
49 ajax: {
50 "url": "${h.route_path('repo_groups_data')}",
50 "url": "${h.route_path('repo_groups_data')}",
51 "dataSrc": function (json) {
51 "dataSrc": function (json) {
52 var filteredCount = json.recordsFiltered;
52 var filteredCount = json.recordsFiltered;
53 var filteredInactiveCount = json.recordsFilteredInactive;
53 var filteredInactiveCount = json.recordsFilteredInactive;
54 var totalInactive = json.recordsTotalInactive;
54 var totalInactive = json.recordsTotalInactive;
55 var total = json.recordsTotal;
55 var total = json.recordsTotal;
56
56
57 var _text = _gettext(
57 var _text = _gettext(
58 "{0} of {1} repository groups").format(
58 "{0} of {1} repository groups").format(
59 filteredCount, total);
59 filteredCount, total);
60
60
61 if (total === filteredCount) {
61 if (total === filteredCount) {
62 _text = _gettext("{0} repository groups").format(total);
62 _text = _gettext("{0} repository groups").format(total);
63 }
63 }
64 $('#repo_group_count').text(_text);
64 $('#repo_group_count').text(_text);
65 return json.data;
65 return json.data;
66 },
66 },
67 },
67 },
68
68
69 dom: 'rtp',
69 dom: 'rtp',
70 pageLength: ${c.visual.admin_grid_items},
70 pageLength: ${c.visual.admin_grid_items},
71 order: [[ 0, "asc" ]],
71 order: [[ 0, "asc" ]],
72 columns: [
72 columns: [
73 { data: {"_": "name",
73 { data: {"_": "name",
74 "sort": "name_raw"}, title: "${_('Name')}", className: "td-componentname" },
74 "sort": "name"}, title: "${_('Name')}", className: "td-componentname" },
75 { data: 'menu', "bSortable": false, className: "quick_repo_menu" },
75 { data: 'menu', "bSortable": false, className: "quick_repo_menu" },
76 { data: {"_": "desc",
76 { data: {"_": "desc",
77 "sort": "desc"}, title: "${_('Description')}", className: "td-description" },
77 "sort": "desc"}, title: "${_('Description')}", className: "td-description" },
78 { data: {"_": "last_change",
78 { data: {"_": "last_change",
79 "sort": "last_change_raw",
79 "sort": "last_change",
80 "type": Number}, title: "${_('Last Change')}", className: "td-time" },
80 "type": Number}, title: "${_('Last Change')}", className: "td-time" },
81 { data: {"_": "top_level_repos",
81 { data: {"_": "top_level_repos",
82 "sort": "top_level_repos"}, title: "${_('Number of top level repositories')}" },
82 "sort": "top_level_repos"}, title: "${_('Number of top level repositories')}" },
83 { data: {"_": "owner",
83 { data: {"_": "owner",
84 "sort": "owner"}, title: "${_('Owner')}", className: "td-user" },
84 "sort": "owner"}, title: "${_('Owner')}", className: "td-user" },
85 { data: {"_": "action",
85 { data: {"_": "action",
86 "sort": "action"}, title: "${_('Action')}", className: "td-action", orderable: false }
86 "sort": "action"}, title: "${_('Action')}", className: "td-action", orderable: false }
87 ],
87 ],
88 language: {
88 language: {
89 paginate: DEFAULT_GRID_PAGINATION,
89 paginate: DEFAULT_GRID_PAGINATION,
90 sProcessing: _gettext('loading...'),
90 sProcessing: _gettext('loading...'),
91 emptyTable: _gettext("No repository groups available yet.")
91 emptyTable: _gettext("No repository groups available yet.")
92 },
92 },
93 });
93 });
94
94
95 $repoGroupsListTable.on('xhr.dt', function(e, settings, json, xhr){
95 $repoGroupsListTable.on('xhr.dt', function(e, settings, json, xhr){
96 $repoGroupsListTable.css('opacity', 1);
96 $repoGroupsListTable.css('opacity', 1);
97 });
97 });
98
98
99 $repoGroupsListTable.on('preXhr.dt', function(e, settings, data){
99 $repoGroupsListTable.on('preXhr.dt', function(e, settings, data){
100 $repoGroupsListTable.css('opacity', 0.3);
100 $repoGroupsListTable.css('opacity', 0.3);
101 });
101 });
102
102
103 // filter
103 // filter
104 $('#q_filter').on('keyup',
104 $('#q_filter').on('keyup',
105 $.debounce(250, function() {
105 $.debounce(250, function() {
106 $repoGroupsListTable.DataTable().search(
106 $repoGroupsListTable.DataTable().search(
107 $('#q_filter').val()
107 $('#q_filter').val()
108 ).draw();
108 ).draw();
109 })
109 })
110 );
110 );
111
111
112 });
112 });
113
113
114 </script>
114 </script>
115
115
116 </%def>
116 </%def>
117
117
@@ -1,149 +1,149 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Repositories administration')}
5 ${_('Repositories administration')}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()"></%def>
11 <%def name="breadcrumbs_links()"></%def>
12
12
13 <%def name="menu_bar_nav()">
13 <%def name="menu_bar_nav()">
14 ${self.menu_items(active='admin')}
14 ${self.menu_items(active='admin')}
15 </%def>
15 </%def>
16
16
17 <%def name="menu_bar_subnav()">
17 <%def name="menu_bar_subnav()">
18 ${self.admin_menu(active='repositories')}
18 ${self.admin_menu(active='repositories')}
19 </%def>
19 </%def>
20
20
21 <%def name="main()">
21 <%def name="main()">
22 <div class="box">
22 <div class="box">
23
23
24 <div class="title">
24 <div class="title">
25 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
25 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
26 <span id="repo_count"></span>
26 <span id="repo_count"></span>
27
27
28 <ul class="links">
28 <ul class="links">
29 %if c.can_create_repo:
29 %if c.can_create_repo:
30 <li>
30 <li>
31 <a href="${h.route_path('repo_new')}" class="btn btn-small btn-success">${_(u'Add Repository')}</a>
31 <a href="${h.route_path('repo_new')}" class="btn btn-small btn-success">${_(u'Add Repository')}</a>
32 </li>
32 </li>
33 %endif
33 %endif
34 </ul>
34 </ul>
35 </div>
35 </div>
36 <div id="repos_list_wrap">
36 <div id="repos_list_wrap">
37 <table id="repo_list_table" class="display"></table>
37 <table id="repo_list_table" class="display"></table>
38 </div>
38 </div>
39
39
40 </div>
40 </div>
41
41
42 <script>
42 <script>
43 $(document).ready(function() {
43 $(document).ready(function() {
44 var $repoListTable = $('#repo_list_table');
44 var $repoListTable = $('#repo_list_table');
45
45
46 // repo list
46 // repo list
47 $repoListTable.DataTable({
47 $repoListTable.DataTable({
48 processing: true,
48 processing: true,
49 serverSide: true,
49 serverSide: true,
50 ajax: {
50 ajax: {
51 "url": "${h.route_path('repos_data')}",
51 "url": "${h.route_path('repos_data')}",
52 "dataSrc": function (json) {
52 "dataSrc": function (json) {
53 var filteredCount = json.recordsFiltered;
53 var filteredCount = json.recordsFiltered;
54 var total = json.recordsTotal;
54 var total = json.recordsTotal;
55
55
56 var _text = _gettext(
56 var _text = _gettext(
57 "{0} of {1} repositories").format(
57 "{0} of {1} repositories").format(
58 filteredCount, total);
58 filteredCount, total);
59
59
60 if (total === filteredCount) {
60 if (total === filteredCount) {
61 _text = _gettext("{0} repositories").format(total);
61 _text = _gettext("{0} repositories").format(total);
62 }
62 }
63 $('#repo_count').text(_text);
63 $('#repo_count').text(_text);
64
64
65 return json.data;
65 return json.data;
66 },
66 },
67 },
67 },
68 dom: 'rtp',
68 dom: 'rtp',
69 pageLength: ${c.visual.admin_grid_items},
69 pageLength: ${c.visual.admin_grid_items},
70 order: [[ 0, "asc" ]],
70 order: [[ 0, "asc" ]],
71 columns: [
71 columns: [
72 {
72 {
73 data: {
73 data: {
74 "_": "name",
74 "_": "name",
75 "sort": "name_raw"
75 "sort": "name"
76 }, title: "${_('Name')}", className: "td-componentname"
76 }, title: "${_('Name')}", className: "td-componentname"
77 },
77 },
78 {
78 {
79 data: 'menu', "bSortable": false, className: "quick_repo_menu"},
79 data: 'menu', "bSortable": false, className: "quick_repo_menu"},
80 {
80 {
81 data: {
81 data: {
82 "_": "desc",
82 "_": "desc",
83 "sort": "desc"
83 "sort": "desc"
84 }, title: "${_('Description')}", className: "td-description"
84 }, title: "${_('Description')}", className: "td-description"
85 },
85 },
86 {
86 {
87 data: {
87 data: {
88 "_": "last_change",
88 "_": "last_change",
89 "sort": "last_change_raw",
89 "sort": "last_change",
90 "type": Number
90 "type": Number
91 }, title: "${_('Last Change')}", className: "td-time"
91 }, title: "${_('Last Change')}", className: "td-time"
92 },
92 },
93 {
93 {
94 data: {
94 data: {
95 "_": "last_changeset",
95 "_": "last_changeset",
96 "sort": "last_changeset_raw",
96 "sort": "last_changeset_raw",
97 "type": Number
97 "type": Number
98 }, title: "${_('Commit')}", className: "td-commit", orderable: false
98 }, title: "${_('Commit')}", className: "td-commit", orderable: false
99 },
99 },
100 {
100 {
101 data: {
101 data: {
102 "_": "owner",
102 "_": "owner",
103 "sort": "owner"
103 "sort": "owner"
104 }, title: "${_('Owner')}", className: "td-user"
104 }, title: "${_('Owner')}", className: "td-user"
105 },
105 },
106 {
106 {
107 data: {
107 data: {
108 "_": "state",
108 "_": "state",
109 "sort": "state"
109 "sort": "state"
110 }, title: "${_('State')}", className: "td-tags td-state"
110 }, title: "${_('State')}", className: "td-tags td-state"
111 },
111 },
112 {
112 {
113 data: {
113 data: {
114 "_": "action",
114 "_": "action",
115 "sort": "action"
115 "sort": "action"
116 }, title: "${_('Action')}", className: "td-action", orderable: false
116 }, title: "${_('Action')}", className: "td-action", orderable: false
117 }
117 }
118 ],
118 ],
119 language: {
119 language: {
120 paginate: DEFAULT_GRID_PAGINATION,
120 paginate: DEFAULT_GRID_PAGINATION,
121 sProcessing: _gettext('loading...'),
121 sProcessing: _gettext('loading...'),
122 emptyTable:_gettext("No repositories present.")
122 emptyTable:_gettext("No repositories present.")
123 },
123 },
124 "initComplete": function( settings, json ) {
124 "initComplete": function( settings, json ) {
125 quick_repo_menu();
125 quick_repo_menu();
126 }
126 }
127 });
127 });
128
128
129 $repoListTable.on('xhr.dt', function(e, settings, json, xhr){
129 $repoListTable.on('xhr.dt', function(e, settings, json, xhr){
130 $repoListTable.css('opacity', 1);
130 $repoListTable.css('opacity', 1);
131 });
131 });
132
132
133 $repoListTable.on('preXhr.dt', function(e, settings, data){
133 $repoListTable.on('preXhr.dt', function(e, settings, data){
134 $repoListTable.css('opacity', 0.3);
134 $repoListTable.css('opacity', 0.3);
135 });
135 });
136
136
137 $('#q_filter').on('keyup',
137 $('#q_filter').on('keyup',
138 $.debounce(250, function() {
138 $.debounce(250, function() {
139 $repoListTable.DataTable().search(
139 $repoListTable.DataTable().search(
140 $('#q_filter').val()
140 $('#q_filter').val()
141 ).draw();
141 ).draw();
142 })
142 })
143 );
143 );
144
144
145 });
145 });
146
146
147 </script>
147 </script>
148
148
149 </%def>
149 </%def>
@@ -1,221 +1,223 b''
1 <%inherit file="/base/base.mako"/>
1 <%inherit file="/base/base.mako"/>
2
2
3
3
4 <%def name="menu_bar_subnav()">
4 <%def name="menu_bar_subnav()">
5 % if c.repo_group:
5 % if c.repo_group:
6 ${self.repo_group_menu(active='home')}
6 ${self.repo_group_menu(active='home')}
7 % endif
7 % endif
8 </%def>
8 </%def>
9
9
10
10
11 <%def name="main()">
11 <%def name="main()">
12 <div class="box">
12 <div class="box">
13 <!-- box / title -->
13 <!-- box / title -->
14 <div class="title">
14 <div class="title">
15
15
16 </div>
16 </div>
17 <!-- end box / title -->
17 <!-- end box / title -->
18 <div id="no_grid_data" class="table" style="display: none">
18 <div id="no_grid_data" class="table" style="display: none">
19 <h2 class="no-object-border">
19 <h2 class="no-object-border">
20 ${_('No repositories or repositories groups exists here.')}
20 ${_('No repositories or repositories groups exists here.')}
21 </h2>
21 </h2>
22 </div>
22 </div>
23
23
24 <div class="table">
24 <div class="table">
25 <div id="groups_list_wrap" style="min-height: 200px;">
25 <div id="groups_list_wrap" style="min-height: 200px;">
26 <table id="group_list_table" class="display" style="width: 100%;"></table>
26 <table id="group_list_table" class="display" style="width: 100%;"></table>
27 </div>
27 </div>
28 </div>
28 </div>
29
29
30 <div class="table">
30 <div class="table">
31 <div id="repos_list_wrap" style="min-height: 200px;">
31 <div id="repos_list_wrap" style="min-height: 200px;">
32 <table id="repo_list_table" class="display" style="width: 100%;"></table>
32 <table id="repo_list_table" class="display" style="width: 100%;"></table>
33 </div>
33 </div>
34 </div>
34 </div>
35
35
36 </div>
36 </div>
37 <script>
37 <script>
38 $(document).ready(function () {
38 $(document).ready(function () {
39
39
40 // repo group list
40 // repo group list
41 var $groupListTable = $('#group_list_table');
41 var $groupListTable = $('#group_list_table');
42
42
43 $groupListTable.DataTable({
43 $groupListTable.DataTable({
44 processing: true,
44 processing: true,
45 serverSide: true,
45 serverSide: true,
46 ajax: {
46 ajax: {
47 "url": "${h.route_path('main_page_repo_groups_data')}",
47 "url": "${h.route_path('main_page_repo_groups_data')}",
48 "data": function (d) {
48 "data": function (d) {
49 % if c.repo_group:
49 % if c.repo_group:
50 d.repo_group_id = ${c.repo_group.group_id}
50 d.repo_group_id = ${c.repo_group.group_id}
51 % endif
51 % endif
52 }
52 }
53 },
53 },
54 dom: 'rtp',
54 dom: 'rtp',
55 pageLength: ${c.visual.dashboard_items},
55 pageLength: ${c.visual.dashboard_items},
56 order: [[0, "asc"]],
56 order: [[0, "asc"]],
57 columns: [
57 columns: [
58 {
58 {
59 data: {
59 data: {
60 "_": "name",
60 "_": "name",
61 "sort": "name_raw"
61 "sort": "name"
62 }, title: "${_('Name')}", className: "truncate-wrap td-grid-name"
62 }, title: "${_('Name')}", className: "truncate-wrap td-grid-name"
63 },
63 },
64 {data: 'menu', "bSortable": false, className: "quick_repo_menu"},
64 {
65 data: 'menu', "bSortable": false, className: "quick_repo_menu"
66 },
65 {
67 {
66 data: {
68 data: {
67 "_": "desc",
69 "_": "desc",
68 "sort": "desc"
70 "sort": "desc"
69 }, title: "${_('Description')}", className: "td-description"
71 }, title: "${_('Description')}", className: "td-description"
70 },
72 },
71 {
73 {
72 data: {
74 data: {
73 "_": "last_change",
75 "_": "last_change",
74 "sort": "last_change_raw",
76 "sort": "last_change",
75 "type": Number
77 "type": Number
76 }, title: "${_('Last Change')}", className: "td-time"
78 }, title: "${_('Last Change')}", className: "td-time"
77 },
79 },
78 {
80 {
79 data: {
81 data: {
80 "_": "last_changeset",
82 "_": "last_changeset",
81 "sort": "last_changeset_raw",
83 "sort": "last_changeset_raw",
82 "type": Number
84 "type": Number
83 }, title: "", className: "td-hash"
85 }, title: "", className: "td-hash", orderable: false
84 },
86 },
85 {
87 {
86 data: {
88 data: {
87 "_": "owner",
89 "_": "owner",
88 "sort": "owner"
90 "sort": "owner"
89 }, title: "${_('Owner')}", className: "td-user"
91 }, title: "${_('Owner')}", className: "td-user"
90 }
92 }
91 ],
93 ],
92 language: {
94 language: {
93 paginate: DEFAULT_GRID_PAGINATION,
95 paginate: DEFAULT_GRID_PAGINATION,
94 sProcessing: _gettext('loading...'),
96 sProcessing: _gettext('loading...'),
95 emptyTable: _gettext("No repository groups present.")
97 emptyTable: _gettext("No repository groups present.")
96 },
98 },
97 "drawCallback": function (settings, json) {
99 "drawCallback": function (settings, json) {
98 // hide grid if it's empty
100 // hide grid if it's empty
99 if (settings.fnRecordsDisplay() === 0) {
101 if (settings.fnRecordsDisplay() === 0) {
100 $('#groups_list_wrap').hide();
102 $('#groups_list_wrap').hide();
101 // both hidden, show no-data
103 // both hidden, show no-data
102 if ($('#repos_list_wrap').is(':hidden')) {
104 if ($('#repos_list_wrap').is(':hidden')) {
103 $('#no_grid_data').show();
105 $('#no_grid_data').show();
104 }
106 }
105 } else {
107 } else {
106 $('#groups_list_wrap').show();
108 $('#groups_list_wrap').show();
107 }
109 }
108
110
109 timeagoActivate();
111 timeagoActivate();
110 tooltipActivate();
112 tooltipActivate();
111 quick_repo_menu();
113 quick_repo_menu();
112 // hide pagination for single page
114 // hide pagination for single page
113 if (settings._iDisplayLength >= settings.fnRecordsDisplay()) {
115 if (settings._iDisplayLength >= settings.fnRecordsDisplay()) {
114 $(settings.nTableWrapper).find('.dataTables_paginate').hide();
116 $(settings.nTableWrapper).find('.dataTables_paginate').hide();
115 }
117 }
116
118
117 },
119 },
118 });
120 });
119
121
120 $groupListTable.on('xhr.dt', function (e, settings, json, xhr) {
122 $groupListTable.on('xhr.dt', function (e, settings, json, xhr) {
121 $groupListTable.css('opacity', 1);
123 $groupListTable.css('opacity', 1);
122 });
124 });
123
125
124 $groupListTable.on('preXhr.dt', function (e, settings, data) {
126 $groupListTable.on('preXhr.dt', function (e, settings, data) {
125 $groupListTable.css('opacity', 0.3);
127 $groupListTable.css('opacity', 0.3);
126 });
128 });
127
129
128
130
129 ## // repo list
131 ## // repo list
130 var $repoListTable = $('#repo_list_table');
132 var $repoListTable = $('#repo_list_table');
131
133
132 $repoListTable.DataTable({
134 $repoListTable.DataTable({
133 processing: true,
135 processing: true,
134 serverSide: true,
136 serverSide: true,
135 ajax: {
137 ajax: {
136 "url": "${h.route_path('main_page_repos_data')}",
138 "url": "${h.route_path('main_page_repos_data')}",
137 "data": function (d) {
139 "data": function (d) {
138 % if c.repo_group:
140 % if c.repo_group:
139 d.repo_group_id = ${c.repo_group.group_id}
141 d.repo_group_id = ${c.repo_group.group_id}
140 % endif
142 % endif
141 }
143 }
142 },
144 },
143 order: [[0, "asc"]],
145 order: [[0, "asc"]],
144 dom: 'rtp',
146 dom: 'rtp',
145 pageLength: ${c.visual.dashboard_items},
147 pageLength: ${c.visual.dashboard_items},
146 columns: [
148 columns: [
147 {
149 {
148 data: {
150 data: {
149 "_": "name",
151 "_": "name",
150 "sort": "name_raw"
152 "sort": "name"
151 }, title: "${_('Name')}", className: "truncate-wrap td-grid-name"
153 }, title: "${_('Name')}", className: "truncate-wrap td-grid-name"
152 },
154 },
153 {
155 {
154 data: 'menu', "bSortable": false, className: "quick_repo_menu"
156 data: 'menu', "bSortable": false, className: "quick_repo_menu"
155 },
157 },
156 {
158 {
157 data: {
159 data: {
158 "_": "desc",
160 "_": "desc",
159 "sort": "desc"
161 "sort": "desc"
160 }, title: "${_('Description')}", className: "td-description"
162 }, title: "${_('Description')}", className: "td-description"
161 },
163 },
162 {
164 {
163 data: {
165 data: {
164 "_": "last_change",
166 "_": "last_change",
165 "sort": "last_change_raw",
167 "sort": "last_change",
166 "type": Number
168 "type": Number
167 }, title: "${_('Last Change')}", className: "td-time", orderable: false
169 }, title: "${_('Last Change')}", className: "td-time"
168 },
170 },
169 {
171 {
170 data: {
172 data: {
171 "_": "last_changeset",
173 "_": "last_changeset",
172 "sort": "last_changeset_raw",
174 "sort": "last_changeset_raw",
173 "type": Number
175 "type": Number
174 }, title: "${_('Commit')}", className: "td-hash"
176 }, title: "${_('Commit')}", className: "td-hash", orderable: false
175 },
177 },
176 {
178 {
177 data: {
179 data: {
178 "_": "owner",
180 "_": "owner",
179 "sort": "owner"
181 "sort": "owner"
180 }, title: "${_('Owner')}", className: "td-user"
182 }, title: "${_('Owner')}", className: "td-user"
181 }
183 }
182 ],
184 ],
183 language: {
185 language: {
184 paginate: DEFAULT_GRID_PAGINATION,
186 paginate: DEFAULT_GRID_PAGINATION,
185 sProcessing: _gettext('loading...'),
187 sProcessing: _gettext('loading...'),
186 emptyTable: _gettext("No repositories present.")
188 emptyTable: _gettext("No repositories present.")
187 },
189 },
188 "drawCallback": function (settings, json) {
190 "drawCallback": function (settings, json) {
189 // hide grid if it's empty
191 // hide grid if it's empty
190 if (settings.fnRecordsDisplay() == 0) {
192 if (settings.fnRecordsDisplay() == 0) {
191 $('#repos_list_wrap').hide()
193 $('#repos_list_wrap').hide()
192 // both hidden, show no-data
194 // both hidden, show no-data
193 if ($('#groups_list_wrap').is(':hidden')) {
195 if ($('#groups_list_wrap').is(':hidden')) {
194 $('#no_grid_data').show()
196 $('#no_grid_data').show()
195 }
197 }
196 } else {
198 } else {
197 $('#repos_list_wrap').show()
199 $('#repos_list_wrap').show()
198 }
200 }
199
201
200 timeagoActivate();
202 timeagoActivate();
201 tooltipActivate();
203 tooltipActivate();
202 quick_repo_menu();
204 quick_repo_menu();
203 // hide pagination for single page
205 // hide pagination for single page
204 if (settings._iDisplayLength >= settings.fnRecordsDisplay()) {
206 if (settings._iDisplayLength >= settings.fnRecordsDisplay()) {
205 $(settings.nTableWrapper).find('.dataTables_paginate').hide();
207 $(settings.nTableWrapper).find('.dataTables_paginate').hide();
206 }
208 }
207
209
208 },
210 },
209 });
211 });
210
212
211 $repoListTable.on('xhr.dt', function (e, settings, json, xhr) {
213 $repoListTable.on('xhr.dt', function (e, settings, json, xhr) {
212 $repoListTable.css('opacity', 1);
214 $repoListTable.css('opacity', 1);
213 });
215 });
214
216
215 $repoListTable.on('preXhr.dt', function (e, settings, data) {
217 $repoListTable.on('preXhr.dt', function (e, settings, data) {
216 $repoListTable.css('opacity', 0.3);
218 $repoListTable.css('opacity', 0.3);
217 });
219 });
218
220
219 });
221 });
220 </script>
222 </script>
221 </%def>
223 </%def>
General Comments 0
You need to be logged in to leave comments. Login now