##// END OF EJS Templates
repo-groups: moved to pyramid
marcink -
r2175:ea878558 default
parent child Browse files
Show More
@@ -0,0 +1,176 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
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
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
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/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import os
22 import pytest
23
24 from rhodecode.apps._base import ADMIN_PREFIX
25 from rhodecode.lib import helpers as h
26 from rhodecode.model.db import Repository, UserRepoToPerm, User
27 from rhodecode.model.meta import Session
28 from rhodecode.model.repo_group import RepoGroupModel
29 from rhodecode.tests import (
30 assert_session_flash, TEST_USER_REGULAR_LOGIN, TESTS_TMP_PATH, TestController)
31 from rhodecode.tests.fixture import Fixture
32
33 fixture = Fixture()
34
35
36 def route_path(name, params=None, **kwargs):
37 import urllib
38
39 base_url = {
40 'repo_groups': ADMIN_PREFIX + '/repo_groups',
41 'repo_group_new': ADMIN_PREFIX + '/repo_group/new',
42 'repo_group_create': ADMIN_PREFIX + '/repo_group/create',
43
44 }[name].format(**kwargs)
45
46 if params:
47 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
48 return base_url
49
50
51 def _get_permission_for_user(user, repo):
52 perm = UserRepoToPerm.query()\
53 .filter(UserRepoToPerm.repository ==
54 Repository.get_by_repo_name(repo))\
55 .filter(UserRepoToPerm.user == User.get_by_username(user))\
56 .all()
57 return perm
58
59
60 @pytest.mark.usefixtures("app")
61 class TestAdminRepositoryGroups(object):
62 def test_show_repo_groups(self, autologin_user):
63 response = self.app.get(route_path('repo_groups'))
64 response.mustcontain('data: []')
65
66 def test_show_repo_groups_after_creating_group(self, autologin_user):
67 fixture.create_repo_group('test_repo_group')
68 response = self.app.get(route_path('repo_groups'))
69 response.mustcontain('"name_raw": "test_repo_group"')
70 fixture.destroy_repo_group('test_repo_group')
71
72 def test_new(self, autologin_user):
73 self.app.get(route_path('repo_group_new'))
74
75 def test_new_with_parent_group(self, autologin_user, user_util):
76 gr = user_util.create_repo_group()
77
78 self.app.get(route_path('repo_group_new'),
79 params=dict(parent_group=gr.group_name))
80
81 def test_new_by_regular_user_no_permission(self, autologin_regular_user):
82 self.app.get(route_path('repo_group_new'), status=403)
83
84 @pytest.mark.parametrize('repo_group_name', [
85 'git_repo',
86 'git_repo_ąć',
87 'hg_repo',
88 '12345',
89 'hg_repo_ąć',
90 ])
91 def test_create(self, autologin_user, repo_group_name, csrf_token):
92 repo_group_name_unicode = repo_group_name.decode('utf8')
93 description = 'description for newly created repo group'
94
95 response = self.app.post(
96 route_path('repo_group_create'),
97 fixture._get_group_create_params(
98 group_name=repo_group_name,
99 group_description=description,
100 csrf_token=csrf_token))
101
102 # run the check page that triggers the flash message
103 repo_gr_url = h.route_path(
104 'repo_group_home', repo_group_name=repo_group_name)
105
106 assert_session_flash(
107 response,
108 'Created repository group <a href="%s">%s</a>' % (
109 repo_gr_url, repo_group_name_unicode))
110
111 # # test if the repo group was created in the database
112 new_repo_group = RepoGroupModel()._get_repo_group(
113 repo_group_name_unicode)
114 assert new_repo_group is not None
115
116 assert new_repo_group.group_name == repo_group_name_unicode
117 assert new_repo_group.group_description == description
118
119 # test if the repository is visible in the list ?
120 response = self.app.get(repo_gr_url)
121 response.mustcontain(repo_group_name)
122
123 # test if the repository group was created on filesystem
124 is_on_filesystem = os.path.isdir(
125 os.path.join(TESTS_TMP_PATH, repo_group_name))
126 if not is_on_filesystem:
127 self.fail('no repo group %s in filesystem' % repo_group_name)
128
129 RepoGroupModel().delete(repo_group_name_unicode)
130 Session().commit()
131
132 @pytest.mark.parametrize('repo_group_name', [
133 'git_repo',
134 'git_repo_ąć',
135 'hg_repo',
136 '12345',
137 'hg_repo_ąć',
138 ])
139 def test_create_subgroup(self, autologin_user, user_util, repo_group_name, csrf_token):
140 parent_group = user_util.create_repo_group()
141 parent_group_name = parent_group.group_name
142
143 expected_group_name = '{}/{}'.format(
144 parent_group_name, repo_group_name)
145 expected_group_name_unicode = expected_group_name.decode('utf8')
146
147 try:
148 response = self.app.post(
149 route_path('repo_group_create'),
150 fixture._get_group_create_params(
151 group_name=repo_group_name,
152 group_parent_id=parent_group.group_id,
153 group_description='Test desciption',
154 csrf_token=csrf_token))
155
156 assert_session_flash(
157 response,
158 u'Created repository group <a href="%s">%s</a>' % (
159 h.route_path('repo_group_home',
160 repo_group_name=expected_group_name),
161 expected_group_name_unicode))
162 finally:
163 RepoGroupModel().delete(expected_group_name_unicode)
164 Session().commit()
165
166 def test_user_with_creation_permissions_cannot_create_subgroups(
167 self, autologin_regular_user, user_util):
168
169 user_util.grant_user_permission(
170 TEST_USER_REGULAR_LOGIN, 'hg.repogroup.create.true')
171 parent_group = user_util.create_repo_group()
172 parent_group_id = parent_group.group_id
173 self.app.get(
174 route_path('repo_group_new',
175 params=dict(parent_group=parent_group_id), ),
176 status=403)
@@ -0,0 +1,204 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
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
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
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/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import logging
22 import formencode
23 import formencode.htmlfill
24
25 from pyramid.httpexceptions import HTTPFound, HTTPForbidden
26 from pyramid.view import view_config
27 from pyramid.renderers import render
28 from pyramid.response import Response
29
30 from rhodecode.apps._base import BaseAppView, DataGridAppView
31
32 from rhodecode.lib.ext_json import json
33 from rhodecode.lib.auth import (
34 LoginRequired, CSRFRequired, NotAnonymous,
35 HasPermissionAny, HasRepoGroupPermissionAny)
36 from rhodecode.lib import helpers as h, audit_logger
37 from rhodecode.lib.utils2 import safe_int, safe_unicode
38 from rhodecode.model.forms import RepoGroupForm
39 from rhodecode.model.repo_group import RepoGroupModel
40 from rhodecode.model.scm import RepoGroupList
41 from rhodecode.model.db import Session, RepoGroup
42
43 log = logging.getLogger(__name__)
44
45
46 class AdminRepoGroupsView(BaseAppView, DataGridAppView):
47
48 def load_default_context(self):
49 c = self._get_local_tmpl_context()
50 self._register_global_c(c)
51 return c
52
53 def _load_form_data(self, c):
54 allow_empty_group = False
55
56 if self._can_create_repo_group():
57 # we're global admin, we're ok and we can create TOP level groups
58 allow_empty_group = True
59
60 # override the choices for this form, we need to filter choices
61 # and display only those we have ADMIN right
62 groups_with_admin_rights = RepoGroupList(
63 RepoGroup.query().all(),
64 perm_set=['group.admin'])
65 c.repo_groups = RepoGroup.groups_choices(
66 groups=groups_with_admin_rights,
67 show_empty_group=allow_empty_group)
68
69 def _can_create_repo_group(self, parent_group_id=None):
70 is_admin = HasPermissionAny('hg.admin')('group create controller')
71 create_repo_group = HasPermissionAny(
72 'hg.repogroup.create.true')('group create controller')
73 if is_admin or (create_repo_group and not parent_group_id):
74 # we're global admin, or we have global repo group create
75 # permission
76 # we're ok and we can create TOP level groups
77 return True
78 elif parent_group_id:
79 # we check the permission if we can write to parent group
80 group = RepoGroup.get(parent_group_id)
81 group_name = group.group_name if group else None
82 if HasRepoGroupPermissionAny('group.admin')(
83 group_name, 'check if user is an admin of group'):
84 # we're an admin of passed in group, we're ok.
85 return True
86 else:
87 return False
88 return False
89
90 @LoginRequired()
91 @NotAnonymous()
92 # perms check inside
93 @view_config(
94 route_name='repo_groups', request_method='GET',
95 renderer='rhodecode:templates/admin/repo_groups/repo_groups.mako')
96 def repo_group_list(self):
97 c = self.load_default_context()
98
99 repo_group_list = RepoGroup.get_all_repo_groups()
100 repo_group_list_acl = RepoGroupList(
101 repo_group_list, perm_set=['group.admin'])
102 repo_group_data = RepoGroupModel().get_repo_groups_as_dict(
103 repo_group_list=repo_group_list_acl, admin=True)
104 c.data = json.dumps(repo_group_data)
105 return self._get_template_context(c)
106
107 @LoginRequired()
108 @NotAnonymous()
109 # perm checks inside
110 @view_config(
111 route_name='repo_group_new', request_method='GET',
112 renderer='rhodecode:templates/admin/repo_groups/repo_group_add.mako')
113 def repo_group_new(self):
114 c = self.load_default_context()
115
116 # perm check for admin, create_group perm or admin of parent_group
117 parent_group_id = safe_int(self.request.GET.get('parent_group'))
118 if not self._can_create_repo_group(parent_group_id):
119 raise HTTPForbidden()
120
121 self._load_form_data(c)
122
123 defaults = {} # Future proof for default of repo group
124 data = render(
125 'rhodecode:templates/admin/repo_groups/repo_group_add.mako',
126 self._get_template_context(c), self.request)
127 html = formencode.htmlfill.render(
128 data,
129 defaults=defaults,
130 encoding="UTF-8",
131 force_defaults=False
132 )
133 return Response(html)
134
135 @LoginRequired()
136 @NotAnonymous()
137 @CSRFRequired()
138 # perm checks inside
139 @view_config(
140 route_name='repo_group_create', request_method='POST',
141 renderer='rhodecode:templates/admin/repo_groups/repo_group_add.mako')
142 def repo_group_create(self):
143 c = self.load_default_context()
144 _ = self.request.translate
145
146 parent_group_id = safe_int(self.request.POST.get('group_parent_id'))
147 can_create = self._can_create_repo_group(parent_group_id)
148
149 self._load_form_data(c)
150 # permissions for can create group based on parent_id are checked
151 # here in the Form
152 available_groups = map(lambda k: safe_unicode(k[0]), c.repo_groups)
153 repo_group_form = RepoGroupForm(available_groups=available_groups,
154 can_create_in_root=can_create)()
155
156 repo_group_name = self.request.POST.get('group_name')
157 try:
158 owner = self._rhodecode_user
159 form_result = repo_group_form.to_python(dict(self.request.POST))
160 repo_group = RepoGroupModel().create(
161 group_name=form_result['group_name_full'],
162 group_description=form_result['group_description'],
163 owner=owner.user_id,
164 copy_permissions=form_result['group_copy_permissions']
165 )
166 Session().flush()
167
168 repo_group_data = repo_group.get_api_data()
169 audit_logger.store_web(
170 'repo_group.create', action_data={'data': repo_group_data},
171 user=self._rhodecode_user)
172
173 Session().commit()
174
175 _new_group_name = form_result['group_name_full']
176
177 repo_group_url = h.link_to(
178 _new_group_name,
179 h.route_path('repo_group_home', repo_group_name=_new_group_name))
180 h.flash(h.literal(_('Created repository group %s')
181 % repo_group_url), category='success')
182
183 except formencode.Invalid as errors:
184 data = render(
185 'rhodecode:templates/admin/repo_groups/repo_group_add.mako',
186 self._get_template_context(c), self.request)
187 html = formencode.htmlfill.render(
188 data,
189 defaults=errors.value,
190 errors=errors.error_dict or {},
191 prefix_error=False,
192 encoding="UTF-8",
193 force_defaults=False
194 )
195 return Response(html)
196 except Exception:
197 log.exception("Exception during creation of repository group")
198 h.flash(_('Error occurred during creation of repository group %s')
199 % repo_group_name, category='error')
200 raise HTTPFound(h.route_path('home'))
201
202 raise HTTPFound(
203 h.route_path('repo_group_home',
204 repo_group_name=form_result['group_name_full']))
@@ -0,0 +1,89 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
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
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
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/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import pytest
22
23 from rhodecode.tests import assert_session_flash
24
25
26 def route_path(name, params=None, **kwargs):
27 import urllib
28
29 base_url = {
30 'edit_repo_group_advanced':
31 '/{repo_group_name}/_settings/advanced',
32 'edit_repo_group_advanced_delete':
33 '/{repo_group_name}/_settings/advanced/delete',
34 }[name].format(**kwargs)
35
36 if params:
37 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
38 return base_url
39
40
41 @pytest.mark.usefixtures("app")
42 class TestRepoGroupsAdvancedView(object):
43
44 @pytest.mark.parametrize('repo_group_name', [
45 'gro',
46 '12345',
47 ])
48 def test_show_advanced_settings(self, autologin_user, user_util, repo_group_name):
49 user_util._test_name = repo_group_name
50 gr = user_util.create_repo_group()
51 self.app.get(
52 route_path('edit_repo_group_advanced',
53 repo_group_name=gr.group_name))
54
55 def test_show_advanced_settings_delete(self, autologin_user, user_util,
56 csrf_token):
57 gr = user_util.create_repo_group(auto_cleanup=False)
58 repo_group_name = gr.group_name
59
60 params = dict(
61 csrf_token=csrf_token
62 )
63 response = self.app.post(
64 route_path('edit_repo_group_advanced_delete',
65 repo_group_name=repo_group_name), params=params)
66 assert_session_flash(
67 response, 'Removed repository group `{}`'.format(repo_group_name))
68
69 def test_delete_not_possible_with_objects_inside(self, autologin_user,
70 repo_groups, csrf_token):
71 zombie_group, parent_group, child_group = repo_groups
72
73 response = self.app.get(
74 route_path('edit_repo_group_advanced',
75 repo_group_name=parent_group.group_name))
76
77 response.mustcontain(
78 'This repository group includes 1 children repository group')
79
80 params = dict(
81 csrf_token=csrf_token
82 )
83 response = self.app.post(
84 route_path('edit_repo_group_advanced_delete',
85 repo_group_name=parent_group.group_name), params=params)
86
87 assert_session_flash(
88 response, 'This repository group contains 1 subgroup '
89 'and cannot be deleted')
@@ -0,0 +1,49 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
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
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
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/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import pytest
22
23
24 def route_path(name, params=None, **kwargs):
25 import urllib
26
27 base_url = {
28 'edit_repo_group_perms':
29 '/{repo_group_name:}/_settings/permissions',
30 'edit_repo_group_perms_update':
31 '/{repo_group_name}/_settings/permissions/update',
32 }[name].format(**kwargs)
33
34 if params:
35 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
36 return base_url
37
38
39 @pytest.mark.usefixtures("app")
40 class TestRepoGroupsPermissionsView(object):
41
42 def test_edit_repo_group_perms(self, user_util, autologin_user):
43 repo_group = user_util.create_repo_group()
44 self.app.get(
45 route_path('edit_repo_group_perms',
46 repo_group_name=repo_group.group_name), status=200)
47
48 def test_update_permissions(self):
49 pass
@@ -0,0 +1,90 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
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
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
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/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import pytest
22
23 from rhodecode.tests import assert_session_flash
24
25
26 def route_path(name, params=None, **kwargs):
27 import urllib
28
29 base_url = {
30 'edit_repo_group': '/{repo_group_name}/_edit',
31 # Update is POST to the above url
32 }[name].format(**kwargs)
33
34 if params:
35 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
36 return base_url
37
38
39 @pytest.mark.usefixtures("app")
40 class TestRepoGroupsSettingsView(object):
41
42 @pytest.mark.parametrize('repo_group_name', [
43 'gro',
44 u'12345',
45 ])
46 def test_edit(self, user_util, autologin_user, repo_group_name):
47 user_util._test_name = repo_group_name
48 repo_group = user_util.create_repo_group()
49
50 self.app.get(
51 route_path('edit_repo_group', repo_group_name=repo_group.group_name),
52 status=200)
53
54 def test_update(self, csrf_token, autologin_user, user_util, rc_fixture):
55 repo_group = user_util.create_repo_group()
56 repo_group_name = repo_group.group_name
57
58 description = 'description for newly created repo group'
59 form_data = rc_fixture._get_group_create_params(
60 group_name=repo_group.group_name,
61 group_description=description,
62 csrf_token=csrf_token,
63 repo_group_name=repo_group.group_name,
64 repo_group_owner=repo_group.user.username)
65
66 response = self.app.post(
67 route_path('edit_repo_group',
68 repo_group_name=repo_group.group_name),
69 form_data,
70 status=302)
71
72 assert_session_flash(
73 response, 'Repository Group `{}` updated successfully'.format(
74 repo_group_name))
75
76 def test_update_fails_when_parent_pointing_to_self(
77 self, csrf_token, user_util, autologin_user, rc_fixture):
78 group = user_util.create_repo_group()
79 response = self.app.post(
80 route_path('edit_repo_group', repo_group_name=group.group_name),
81 rc_fixture._get_group_create_params(
82 repo_group_name=group.group_name,
83 repo_group_owner=group.user.username,
84 repo_group=group.group_id,
85 csrf_token=csrf_token),
86 status=200
87 )
88 response.mustcontain(
89 '<span class="error-message">"{}" is not one of -1'.format(
90 group.group_id))
@@ -0,0 +1,105 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2011-2017 RhodeCode GmbH
4 #
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
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
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/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import logging
22
23 from pyramid.view import view_config
24 from pyramid.httpexceptions import HTTPFound
25
26 from rhodecode.apps._base import RepoGroupAppView
27 from rhodecode.lib import helpers as h
28 from rhodecode.lib import audit_logger
29 from rhodecode.lib.auth import (
30 LoginRequired, CSRFRequired, HasRepoGroupPermissionAnyDecorator)
31 from rhodecode.model.repo_group import RepoGroupModel
32 from rhodecode.model.meta import Session
33
34 log = logging.getLogger(__name__)
35
36
37 class RepoGroupSettingsView(RepoGroupAppView):
38 def load_default_context(self):
39 c = self._get_local_tmpl_context()
40 self._register_global_c(c)
41 return c
42
43 @LoginRequired()
44 @HasRepoGroupPermissionAnyDecorator('group.admin')
45 @view_config(
46 route_name='edit_repo_group_advanced', request_method='GET',
47 renderer='rhodecode:templates/admin/repo_groups/repo_group_edit.mako')
48 def edit_repo_group_advanced(self):
49 c = self.load_default_context()
50 c.active = 'advanced'
51 c.repo_group = self.db_repo_group
52 return self._get_template_context(c)
53
54 @LoginRequired()
55 @HasRepoGroupPermissionAnyDecorator('group.admin')
56 @CSRFRequired()
57 @view_config(
58 route_name='edit_repo_group_advanced_delete', request_method='POST',
59 renderer='rhodecode:templates/admin/repo_groups/repo_group_edit.mako')
60 def edit_repo_group_delete(self):
61 _ = self.request.translate
62 _ungettext = self.request.plularize
63 c = self.load_default_context()
64 c.repo_group = self.db_repo_group
65
66 repos = c.repo_group.repositories.all()
67 if repos:
68 msg = _ungettext(
69 'This repository group contains %(num)d repository and cannot be deleted',
70 'This repository group contains %(num)d repositories and cannot be'
71 ' deleted',
72 len(repos)) % {'num': len(repos)}
73 h.flash(msg, category='warning')
74 raise HTTPFound(
75 h.route_path('edit_repo_group_advanced',
76 repo_group_name=self.db_repo_group_name))
77
78 children = c.repo_group.children.all()
79 if children:
80 msg = _ungettext(
81 'This repository group contains %(num)d subgroup and cannot be deleted',
82 'This repository group contains %(num)d subgroups and cannot be deleted',
83 len(children)) % {'num': len(children)}
84 h.flash(msg, category='warning')
85 raise HTTPFound(
86 h.route_path('edit_repo_group_advanced',
87 repo_group_name=self.db_repo_group_name))
88
89 try:
90 old_values = c.repo_group.get_api_data()
91 RepoGroupModel().delete(self.db_repo_group_name)
92
93 audit_logger.store_web(
94 'repo_group.delete', action_data={'old_data': old_values},
95 user=c.rhodecode_user)
96
97 Session().commit()
98 h.flash(_('Removed repository group `%s`') % self.db_repo_group_name,
99 category='success')
100 except Exception:
101 log.exception("Exception during deletion of repository group")
102 h.flash(_('Error occurred during deletion of repository group %s')
103 % self.db_repo_group_name, category='error')
104
105 raise HTTPFound(h.route_path('repo_groups'))
@@ -0,0 +1,100 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2011-2017 RhodeCode GmbH
4 #
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
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
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/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import logging
22
23 from pyramid.view import view_config
24 from pyramid.httpexceptions import HTTPFound
25
26 from rhodecode.apps._base import RepoGroupAppView
27 from rhodecode.lib import helpers as h
28 from rhodecode.lib import audit_logger
29 from rhodecode.lib.auth import (
30 LoginRequired, HasRepoGroupPermissionAnyDecorator, CSRFRequired)
31 from rhodecode.model.repo_group import RepoGroupModel
32 from rhodecode.model.forms import RepoGroupPermsForm
33 from rhodecode.model.meta import Session
34
35 log = logging.getLogger(__name__)
36
37
38 class RepoGroupPermissionsView(RepoGroupAppView):
39 def load_default_context(self):
40 c = self._get_local_tmpl_context()
41 self._register_global_c(c)
42 return c
43
44 @LoginRequired()
45 @HasRepoGroupPermissionAnyDecorator('group.admin')
46 @view_config(
47 route_name='edit_repo_group_perms', request_method='GET',
48 renderer='rhodecode:templates/admin/repo_groups/repo_group_edit.mako')
49 def edit_repo_group_permissions(self):
50 c = self.load_default_context()
51 c.active = 'permissions'
52 c.repo_group = self.db_repo_group
53 return self._get_template_context(c)
54
55 @LoginRequired()
56 @HasRepoGroupPermissionAnyDecorator('group.admin')
57 @CSRFRequired()
58 @view_config(
59 route_name='edit_repo_group_perms_update', request_method='POST',
60 renderer='rhodecode:templates/admin/repo_groups/repo_group_edit.mako')
61 def edit_repo_groups_permissions_update(self):
62 _ = self.request.translate
63 c = self.load_default_context()
64 c.active = 'perms'
65 c.repo_group = self.db_repo_group
66
67 valid_recursive_choices = ['none', 'repos', 'groups', 'all']
68 form = RepoGroupPermsForm(valid_recursive_choices)()\
69 .to_python(self.request.POST)
70
71 if not c.rhodecode_user.is_admin:
72 if self._revoke_perms_on_yourself(form):
73 msg = _('Cannot change permission for yourself as admin')
74 h.flash(msg, category='warning')
75 raise HTTPFound(
76 h.route_path('edit_repo_group_perms',
77 group_name=self.db_repo_group_name))
78
79 # iterate over all members(if in recursive mode) of this groups and
80 # set the permissions !
81 # this can be potentially heavy operation
82 changes = RepoGroupModel().update_permissions(
83 c.repo_group,
84 form['perm_additions'], form['perm_updates'], form['perm_deletions'],
85 form['recursive'])
86
87 action_data = {
88 'added': changes['added'],
89 'updated': changes['updated'],
90 'deleted': changes['deleted'],
91 }
92 audit_logger.store_web(
93 'repo_group.edit.permissions', action_data=action_data,
94 user=c.rhodecode_user)
95
96 Session().commit()
97 h.flash(_('Repository Group permissions updated'), category='success')
98 raise HTTPFound(
99 h.route_path('edit_repo_group_perms',
100 repo_group_name=self.db_repo_group_name))
@@ -0,0 +1,183 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2011-2017 RhodeCode GmbH
4 #
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
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
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/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import logging
22 import deform
23
24 from pyramid.view import view_config
25 from pyramid.httpexceptions import HTTPFound
26
27 from rhodecode.apps._base import RepoGroupAppView
28 from rhodecode.forms import RcForm
29 from rhodecode.lib import helpers as h
30 from rhodecode.lib import audit_logger
31 from rhodecode.lib.auth import (
32 LoginRequired, HasPermissionAll,
33 HasRepoGroupPermissionAny, HasRepoGroupPermissionAnyDecorator, CSRFRequired)
34 from rhodecode.model.db import Session, RepoGroup
35 from rhodecode.model.scm import RepoGroupList
36 from rhodecode.model.repo_group import RepoGroupModel
37 from rhodecode.model.validation_schema.schemas import repo_group_schema
38
39 log = logging.getLogger(__name__)
40
41
42 class RepoGroupSettingsView(RepoGroupAppView):
43 def load_default_context(self):
44 c = self._get_local_tmpl_context()
45 c.repo_group = self.db_repo_group
46 no_parrent = not c.repo_group.parent_group
47 can_create_in_root = self._can_create_repo_group()
48
49 show_root_location = False
50 if no_parrent or can_create_in_root:
51 # we're global admin, we're ok and we can create TOP level groups
52 # or in case this group is already at top-level we also allow
53 # creation in root
54 show_root_location = True
55
56 acl_groups = RepoGroupList(
57 RepoGroup.query().all(),
58 perm_set=['group.admin'])
59 c.repo_groups = RepoGroup.groups_choices(
60 groups=acl_groups,
61 show_empty_group=show_root_location)
62 # filter out current repo group
63 exclude_group_ids = [c.repo_group.group_id]
64 c.repo_groups = filter(lambda x: x[0] not in exclude_group_ids,
65 c.repo_groups)
66 c.repo_groups_choices = map(lambda k: k[0], c.repo_groups)
67
68 parent_group = c.repo_group.parent_group
69
70 add_parent_group = (parent_group and (
71 parent_group.group_id not in c.repo_groups_choices))
72 if add_parent_group:
73 c.repo_groups_choices.append(parent_group.group_id)
74 c.repo_groups.append(RepoGroup._generate_choice(parent_group))
75
76 self._register_global_c(c)
77 return c
78
79 def _can_create_repo_group(self, parent_group_id=None):
80 is_admin = HasPermissionAll('hg.admin')('group create controller')
81 create_repo_group = HasPermissionAll(
82 'hg.repogroup.create.true')('group create controller')
83 if is_admin or (create_repo_group and not parent_group_id):
84 # we're global admin, or we have global repo group create
85 # permission
86 # we're ok and we can create TOP level groups
87 return True
88 elif parent_group_id:
89 # we check the permission if we can write to parent group
90 group = RepoGroup.get(parent_group_id)
91 group_name = group.group_name if group else None
92 if HasRepoGroupPermissionAny('group.admin')(
93 group_name, 'check if user is an admin of group'):
94 # we're an admin of passed in group, we're ok.
95 return True
96 else:
97 return False
98 return False
99
100 def _get_schema(self, c, old_values=None):
101 return repo_group_schema.RepoGroupSettingsSchema().bind(
102 repo_group_repo_group_options=c.repo_groups_choices,
103 repo_group_repo_group_items=c.repo_groups,
104
105 # user caller
106 user=self._rhodecode_user,
107 old_values=old_values
108 )
109
110 @LoginRequired()
111 @HasRepoGroupPermissionAnyDecorator('group.admin')
112 @view_config(
113 route_name='edit_repo_group', request_method='GET',
114 renderer='rhodecode:templates/admin/repo_groups/repo_group_edit.mako')
115 def edit_settings(self):
116 c = self.load_default_context()
117 c.active = 'settings'
118
119 defaults = RepoGroupModel()._get_defaults(self.db_repo_group_name)
120 defaults['repo_group_owner'] = defaults['user']
121
122 schema = self._get_schema(c)
123 c.form = RcForm(schema, appstruct=defaults)
124 return self._get_template_context(c)
125
126 @LoginRequired()
127 @HasRepoGroupPermissionAnyDecorator('group.admin')
128 @CSRFRequired()
129 @view_config(
130 route_name='edit_repo_group', request_method='POST',
131 renderer='rhodecode:templates/admin/repo_groups/repo_group_edit.mako')
132 def edit_settings_update(self):
133 _ = self.request.translate
134 c = self.load_default_context()
135 c.active = 'settings'
136
137 old_repo_group_name = self.db_repo_group_name
138 new_repo_group_name = old_repo_group_name
139
140 old_values = RepoGroupModel()._get_defaults(self.db_repo_group_name)
141 schema = self._get_schema(c, old_values=old_values)
142
143 c.form = RcForm(schema)
144 pstruct = self.request.POST.items()
145
146 try:
147 schema_data = c.form.validate(pstruct)
148 except deform.ValidationFailure as err_form:
149 return self._get_template_context(c)
150
151 # data is now VALID, proceed with updates
152 # save validated data back into the updates dict
153 validated_updates = dict(
154 group_name=schema_data['repo_group']['repo_group_name_without_group'],
155 group_parent_id=schema_data['repo_group']['repo_group_id'],
156 user=schema_data['repo_group_owner'],
157 group_description=schema_data['repo_group_description'],
158 enable_locking=schema_data['repo_group_enable_locking'],
159 )
160
161 try:
162 RepoGroupModel().update(self.db_repo_group, validated_updates)
163
164 audit_logger.store_web(
165 'repo_group.edit', action_data={'old_data': old_values},
166 user=c.rhodecode_user)
167
168 Session().commit()
169
170 # use the new full name for redirect once we know we updated
171 # the name on filesystem and in DB
172 new_repo_group_name = schema_data['repo_group_name']
173
174 h.flash(_('Repository Group `{}` updated successfully').format(
175 old_repo_group_name), category='success')
176
177 except Exception:
178 log.exception("Exception during update or repository group")
179 h.flash(_('Error occurred during update of repository group %s')
180 % old_repo_group_name, category='error')
181
182 raise HTTPFound(
183 h.route_path('edit_repo_group', repo_group_name=new_repo_group_name))
@@ -260,6 +260,20 b' class RepoGroupAppView(BaseAppView):'
260 self.db_repo_group = request.db_repo_group
260 self.db_repo_group = request.db_repo_group
261 self.db_repo_group_name = self.db_repo_group.group_name
261 self.db_repo_group_name = self.db_repo_group.group_name
262
262
263 def _revoke_perms_on_yourself(self, form_result):
264 _updates = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
265 form_result['perm_updates'])
266 _additions = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
267 form_result['perm_additions'])
268 _deletions = filter(lambda u: self._rhodecode_user.user_id == int(u[0]),
269 form_result['perm_deletions'])
270 admin_perm = 'group.admin'
271 if _updates and _updates[0][1] != admin_perm or \
272 _additions and _additions[0][1] != admin_perm or \
273 _deletions and _deletions[0][1] != admin_perm:
274 return True
275 return False
276
263
277
264 class UserGroupAppView(BaseAppView):
278 class UserGroupAppView(BaseAppView):
265 def __init__(self, context, request):
279 def __init__(self, context, request):
@@ -295,6 +295,19 b' def admin_routes(config):'
295 name='repo_create',
295 name='repo_create',
296 pattern='/repos/create')
296 pattern='/repos/create')
297
297
298 # repo groups admin
299 config.add_route(
300 name='repo_groups',
301 pattern='/repo_groups')
302
303 config.add_route(
304 name='repo_group_new',
305 pattern='/repo_group/new')
306
307 config.add_route(
308 name='repo_group_create',
309 pattern='/repo_group/create')
310
298
311
299 def includeme(config):
312 def includeme(config):
300 settings = config.get_settings()
313 settings = config.get_settings()
@@ -22,7 +22,36 b' from rhodecode.apps._base import add_rou'
22
22
23 def includeme(config):
23 def includeme(config):
24
24
25 # Summary
25 # Settings
26 config.add_route(
27 name='edit_repo_group',
28 pattern='/{repo_group_name:.*?[^/]}/_edit',
29 repo_group_route=True)
30 # update is POST on edit_repo_group
31
32 # Settings advanced
33 config.add_route(
34 name='edit_repo_group_advanced',
35 pattern='/{repo_group_name:.*?[^/]}/_settings/advanced',
36 repo_group_route=True)
37
38 config.add_route(
39 name='edit_repo_group_advanced_delete',
40 pattern='/{repo_group_name:.*?[^/]}/_settings/advanced/delete',
41 repo_group_route=True)
42
43 # settings permissions
44 config.add_route(
45 name='edit_repo_group_perms',
46 pattern='/{repo_group_name:.*?[^/]}/_settings/permissions',
47 repo_group_route=True)
48
49 config.add_route(
50 name='edit_repo_group_perms_update',
51 pattern='/{repo_group_name:.*?[^/]}/_settings/permissions/update',
52 repo_group_route=True)
53
54 # Summary, NOTE(marcink): needs to be at the end for catch-all
26 add_route_with_slash(
55 add_route_with_slash(
27 config,
56 config,
28 name='repo_group_home',
57 name='repo_group_home',
@@ -30,4 +59,3 b' def includeme(config):'
30
59
31 # Scan module for configuration decorators.
60 # Scan module for configuration decorators.
32 config.scan('.views', ignore='.tests')
61 config.scan('.views', ignore='.tests')
33
@@ -0,0 +1,19 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
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
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
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/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
@@ -312,6 +312,7 b' def includeme(config):'
312 config.add_route(
312 config.add_route(
313 name='edit_repo',
313 name='edit_repo',
314 pattern='/{repo_name:.*?[^/]}/settings', repo_route=True)
314 pattern='/{repo_name:.*?[^/]}/settings', repo_route=True)
315 # update is POST on edit_repo
315
316
316 # Settings advanced
317 # Settings advanced
317 config.add_route(
318 config.add_route(
@@ -373,7 +374,6 b' def includeme(config):'
373 name='edit_repo_remote_pull',
374 name='edit_repo_remote_pull',
374 pattern='/{repo_name:.*?[^/]}/settings/remote/pull', repo_route=True)
375 pattern='/{repo_name:.*?[^/]}/settings/remote/pull', repo_route=True)
375
376
376
377 # Statistics
377 # Statistics
378 config.add_route(
378 config.add_route(
379 name='edit_repo_statistics',
379 name='edit_repo_statistics',
@@ -0,0 +1,19 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
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
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
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/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
@@ -197,7 +197,7 b' class TestAdminRepoSettings(object):'
197
197
198 assert_session_flash(
198 assert_session_flash(
199 response,
199 response,
200 msg='Repository %s updated successfully' % (backend.repo_name))
200 msg='Repository `%s` updated successfully' % (backend.repo_name))
201
201
202 repo = Repository.get_by_repo_name(backend.repo_name)
202 repo = Repository.get_by_repo_name(backend.repo_name)
203 assert repo.private is True
203 assert repo.private is True
@@ -218,7 +218,7 b' class TestAdminRepoSettings(object):'
218
218
219 assert_session_flash(
219 assert_session_flash(
220 response,
220 response,
221 msg='Repository %s updated successfully' % (backend.repo_name))
221 msg='Repository `%s` updated successfully' % (backend.repo_name))
222 assert backend.repo.private is False
222 assert backend.repo.private is False
223
223
224 # we turn off private now the repo default permission should stay None
224 # we turn off private now the repo default permission should stay None
@@ -164,7 +164,7 b' class RepoSettingsView(RepoAppView):'
164
164
165 Session().commit()
165 Session().commit()
166
166
167 h.flash(_('Repository {} updated successfully').format(
167 h.flash(_('Repository `{}` updated successfully').format(
168 old_repo_name), category='success')
168 old_repo_name), category='success')
169 except Exception:
169 except Exception:
170 log.exception("Exception during update of repository")
170 log.exception("Exception during update of repository")
@@ -172,53 +172,6 b' def make_map(config):'
172 # CUSTOM ROUTES HERE
172 # CUSTOM ROUTES HERE
173 #==========================================================================
173 #==========================================================================
174
174
175 # ADMIN REPOSITORY GROUPS ROUTES
176 with rmap.submapper(path_prefix=ADMIN_PREFIX,
177 controller='admin/repo_groups') as m:
178 m.connect('repo_groups', '/repo_groups',
179 action='create', conditions={'method': ['POST']})
180 m.connect('repo_groups', '/repo_groups',
181 action='index', conditions={'method': ['GET']})
182 m.connect('new_repo_group', '/repo_groups/new',
183 action='new', conditions={'method': ['GET']})
184 m.connect('update_repo_group', '/repo_groups/{group_name}',
185 action='update', conditions={'method': ['PUT'],
186 'function': check_group},
187 requirements=URL_NAME_REQUIREMENTS)
188
189 # EXTRAS REPO GROUP ROUTES
190 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
191 action='edit',
192 conditions={'method': ['GET'], 'function': check_group},
193 requirements=URL_NAME_REQUIREMENTS)
194 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
195 action='edit',
196 conditions={'method': ['PUT'], 'function': check_group},
197 requirements=URL_NAME_REQUIREMENTS)
198
199 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
200 action='edit_repo_group_advanced',
201 conditions={'method': ['GET'], 'function': check_group},
202 requirements=URL_NAME_REQUIREMENTS)
203 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
204 action='edit_repo_group_advanced',
205 conditions={'method': ['PUT'], 'function': check_group},
206 requirements=URL_NAME_REQUIREMENTS)
207
208 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
209 action='edit_repo_group_perms',
210 conditions={'method': ['GET'], 'function': check_group},
211 requirements=URL_NAME_REQUIREMENTS)
212 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
213 action='update_perms',
214 conditions={'method': ['PUT'], 'function': check_group},
215 requirements=URL_NAME_REQUIREMENTS)
216
217 m.connect('delete_repo_group', '/repo_groups/{group_name}',
218 action='delete', conditions={'method': ['DELETE'],
219 'function': check_group},
220 requirements=URL_NAME_REQUIREMENTS)
221
222 # ADMIN SETTINGS ROUTES
175 # ADMIN SETTINGS ROUTES
223 with rmap.submapper(path_prefix=ADMIN_PREFIX,
176 with rmap.submapper(path_prefix=ADMIN_PREFIX,
224 controller='admin/settings') as m:
177 controller='admin/settings') as m:
@@ -71,7 +71,7 b' def includeme(config):'
71
71
72 # repo group integrations
72 # repo group integrations
73 config.add_route('repo_group_integrations_home',
73 config.add_route('repo_group_integrations_home',
74 add_route_requirements('/{repo_group_name}/settings/integrations'),
74 add_route_requirements('/{repo_group_name}/_settings/integrations'),
75 repo_group_route=True)
75 repo_group_route=True)
76
76
77 config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView',
77 config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView',
@@ -81,7 +81,7 b' def includeme(config):'
81 route_name='repo_group_integrations_home')
81 route_name='repo_group_integrations_home')
82
82
83 config.add_route('repo_group_integrations_new',
83 config.add_route('repo_group_integrations_new',
84 add_route_requirements('/{repo_group_name}/settings/integrations/new'),
84 add_route_requirements('/{repo_group_name}/_settings/integrations/new'),
85 repo_group_route=True)
85 repo_group_route=True)
86 config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView',
86 config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView',
87 attr='new_integration',
87 attr='new_integration',
@@ -90,7 +90,7 b' def includeme(config):'
90 route_name='repo_group_integrations_new')
90 route_name='repo_group_integrations_new')
91
91
92 config.add_route('repo_group_integrations_list',
92 config.add_route('repo_group_integrations_list',
93 add_route_requirements('/{repo_group_name}/settings/integrations/{integration}'),
93 add_route_requirements('/{repo_group_name}/_settings/integrations/{integration}'),
94 repo_group_route=True,
94 repo_group_route=True,
95 custom_predicates=(valid_integration,))
95 custom_predicates=(valid_integration,))
96 config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView',
96 config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView',
@@ -100,7 +100,7 b' def includeme(config):'
100 route_name='repo_group_integrations_list')
100 route_name='repo_group_integrations_list')
101
101
102 config.add_route('repo_group_integrations_create',
102 config.add_route('repo_group_integrations_create',
103 add_route_requirements('/{repo_group_name}/settings/integrations/{integration}/new'),
103 add_route_requirements('/{repo_group_name}/_settings/integrations/{integration}/new'),
104 repo_group_route=True,
104 repo_group_route=True,
105 custom_predicates=(valid_integration,))
105 custom_predicates=(valid_integration,))
106 config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView',
106 config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView',
@@ -115,7 +115,7 b' def includeme(config):'
115 route_name='repo_group_integrations_create')
115 route_name='repo_group_integrations_create')
116
116
117 config.add_route('repo_group_integrations_edit',
117 config.add_route('repo_group_integrations_edit',
118 add_route_requirements('/{repo_group_name}/settings/integrations/{integration}/{integration_id}'),
118 add_route_requirements('/{repo_group_name}/_settings/integrations/{integration}/{integration_id}'),
119 repo_group_route=True,
119 repo_group_route=True,
120 custom_predicates=(valid_integration,))
120 custom_predicates=(valid_integration,))
121
121
@@ -145,7 +145,7 b' class TestRepoIntegrationsView(TestInteg'
145
145
146 class TestRepoGroupIntegrationsView(TestIntegrationsView):
146 class TestRepoGroupIntegrationsView(TestIntegrationsView):
147 def test_index_no_integrations(self, test_repo_group):
147 def test_index_no_integrations(self, test_repo_group):
148 url = '/{repo_group_name}/settings/integrations'.format(
148 url = '/{repo_group_name}/_settings/integrations'.format(
149 repo_group_name=test_repo_group.group_name)
149 repo_group_name=test_repo_group.group_name)
150 response = self.app.get(url)
150 response = self.app.get(url)
151
151
@@ -155,7 +155,7 b' class TestRepoGroupIntegrationsView(Test'
155 def test_index_with_integrations(
155 def test_index_with_integrations(
156 self, test_repo_group, repogroup_integration_stub):
156 self, test_repo_group, repogroup_integration_stub):
157
157
158 url = '/{repo_group_name}/settings/integrations'.format(
158 url = '/{repo_group_name}/_settings/integrations'.format(
159 repo_group_name=test_repo_group.group_name)
159 repo_group_name=test_repo_group.group_name)
160
160
161 stub_name = repogroup_integration_stub.name
161 stub_name = repogroup_integration_stub.name
@@ -167,7 +167,7 b' class TestRepoGroupIntegrationsView(Test'
167
167
168 def test_new_integration_page(self, test_repo_group):
168 def test_new_integration_page(self, test_repo_group):
169 repo_group_name = test_repo_group.group_name
169 repo_group_name = test_repo_group.group_name
170 url = '/{repo_group_name}/settings/integrations/new'.format(
170 url = '/{repo_group_name}/_settings/integrations/new'.format(
171 repo_group_name=test_repo_group.group_name)
171 repo_group_name=test_repo_group.group_name)
172
172
173 response = self.app.get(url)
173 response = self.app.get(url)
@@ -177,7 +177,7 b' class TestRepoGroupIntegrationsView(Test'
177 for integration_key, integration_obj in integration_type_registry.items():
177 for integration_key, integration_obj in integration_type_registry.items():
178 if not integration_obj.is_dummy:
178 if not integration_obj.is_dummy:
179 nurl = (
179 nurl = (
180 '/{repo_group_name}/settings/integrations/{integration}/new').format(
180 '/{repo_group_name}/_settings/integrations/{integration}/new').format(
181 repo_group_name=repo_group_name,
181 repo_group_name=repo_group_name,
182 integration=integration_key)
182 integration=integration_key)
183 response.mustcontain(nurl)
183 response.mustcontain(nurl)
@@ -188,7 +188,7 b' class TestRepoGroupIntegrationsView(Test'
188 self, test_repo_group, IntegrationType):
188 self, test_repo_group, IntegrationType):
189
189
190 repo_group_name = test_repo_group.group_name
190 repo_group_name = test_repo_group.group_name
191 url = ('/{repo_group_name}/settings/integrations/{integration_key}/new'
191 url = ('/{repo_group_name}/_settings/integrations/{integration_key}/new'
192 ).format(repo_group_name=repo_group_name,
192 ).format(repo_group_name=repo_group_name,
193 integration_key=IntegrationType.key)
193 integration_key=IntegrationType.key)
194
194
@@ -202,7 +202,7 b' class TestRepoGroupIntegrationsView(Test'
202 StubIntegrationType, csrf_token):
202 StubIntegrationType, csrf_token):
203
203
204 repo_group_name = test_repo_group.group_name
204 repo_group_name = test_repo_group.group_name
205 url = ('/{repo_group_name}/settings/integrations/{integration_key}/new'
205 url = ('/{repo_group_name}/_settings/integrations/{integration_key}/new'
206 ).format(repo_group_name=repo_group_name,
206 ).format(repo_group_name=repo_group_name,
207 integration_key=StubIntegrationType.key)
207 integration_key=StubIntegrationType.key)
208
208
@@ -232,9 +232,9 b' def _post_integration_test_helper(app, u'
232 ('repo:%s' % repo_name,
232 ('repo:%s' % repo_name,
233 '/%s/settings/integrations' % repo_name),
233 '/%s/settings/integrations' % repo_name),
234 ('repogroup:%s' % repo_group_name,
234 ('repogroup:%s' % repo_group_name,
235 '/%s/settings/integrations' % repo_group_name),
235 '/%s/_settings/integrations' % repo_group_name),
236 ('repogroup-recursive:%s' % repo_group_name,
236 ('repogroup-recursive:%s' % repo_group_name,
237 '/%s/settings/integrations' % repo_group_name),
237 '/%s/_settings/integrations' % repo_group_name),
238 ]
238 ]
239
239
240 for scope, destination in scopes_destinations:
240 for scope, destination in scopes_destinations:
@@ -254,8 +254,11 b' class RepoGroupModel(BaseModel):'
254 # functions can delete this
254 # functions can delete this
255 cleanup_group = self.check_exist_filesystem(group_name,
255 cleanup_group = self.check_exist_filesystem(group_name,
256 exc_on_failure=False)
256 exc_on_failure=False)
257 user = self._get_user(owner)
258 if not user:
259 raise ValueError('Owner %s not found as rhodecode user', owner)
260
257 try:
261 try:
258 user = self._get_user(owner)
259 new_repo_group = RepoGroup()
262 new_repo_group = RepoGroup()
260 new_repo_group.user = user
263 new_repo_group.user = user
261 new_repo_group.group_description = group_description or group_name
264 new_repo_group.group_description = group_description or group_name
@@ -736,3 +739,26 b' class RepoGroupModel(BaseModel):'
736 repo_group_data.append(row)
739 repo_group_data.append(row)
737
740
738 return repo_group_data
741 return repo_group_data
742
743 def _get_defaults(self, repo_group_name):
744 repo_group = RepoGroup.get_by_group_name(repo_group_name)
745
746 if repo_group is None:
747 return None
748
749 defaults = repo_group.get_dict()
750 defaults['repo_group_name'] = repo_group.name
751 defaults['repo_group_description'] = repo_group.group_description
752 defaults['repo_group_enable_locking'] = repo_group.enable_locking
753
754 # we use -1 as this is how in HTML, we mark an empty group
755 defaults['repo_group'] = defaults['group_parent_id'] or -1
756
757 # fill owner
758 if repo_group.user:
759 defaults.update({'user': repo_group.user.username})
760 else:
761 replacement_user = User.get_first_super_admin().username
762 defaults.update({'user': replacement_user})
763
764 return defaults
@@ -20,6 +20,7 b''
20
20
21
21
22 import colander
22 import colander
23 import deform.widget
23
24
24 from rhodecode.translation import _
25 from rhodecode.translation import _
25 from rhodecode.model.validation_schema import validators, preparers, types
26 from rhodecode.model.validation_schema import validators, preparers, types
@@ -143,6 +144,19 b' def deferred_repo_group_name_validator(n'
143 return validators.valid_name_validator
144 return validators.valid_name_validator
144
145
145
146
147 @colander.deferred
148 def deferred_repo_group_validator(node, kw):
149 options = kw.get(
150 'repo_group_repo_group_options')
151 return colander.OneOf([x for x in options])
152
153
154 @colander.deferred
155 def deferred_repo_group_widget(node, kw):
156 items = kw.get('repo_group_repo_group_items')
157 return deform.widget.Select2Widget(values=items)
158
159
146 class GroupType(colander.Mapping):
160 class GroupType(colander.Mapping):
147 def _validate(self, node, value):
161 def _validate(self, node, value):
148 try:
162 try:
@@ -208,15 +222,15 b' class RepoGroupSchema(colander.Schema):'
208 validator=deferred_repo_group_owner_validator)
222 validator=deferred_repo_group_owner_validator)
209
223
210 repo_group_description = colander.SchemaNode(
224 repo_group_description = colander.SchemaNode(
211 colander.String(), missing='')
225 colander.String(), missing='', widget=deform.widget.TextAreaWidget())
212
226
213 repo_group_copy_permissions = colander.SchemaNode(
227 repo_group_copy_permissions = colander.SchemaNode(
214 types.StringBooleanType(),
228 types.StringBooleanType(),
215 missing=False)
229 missing=False, widget=deform.widget.CheckboxWidget())
216
230
217 repo_group_enable_locking = colander.SchemaNode(
231 repo_group_enable_locking = colander.SchemaNode(
218 types.StringBooleanType(),
232 types.StringBooleanType(),
219 missing=False)
233 missing=False, widget=deform.widget.CheckboxWidget())
220
234
221 def deserialize(self, cstruct):
235 def deserialize(self, cstruct):
222 """
236 """
@@ -238,3 +252,33 b' class RepoGroupSchema(colander.Schema):'
238 third.deserialize({'unique_repo_group_name': validated_name})
252 third.deserialize({'unique_repo_group_name': validated_name})
239
253
240 return appstruct
254 return appstruct
255
256
257 class RepoGroupSettingsSchema(RepoGroupSchema):
258 repo_group = colander.SchemaNode(
259 colander.Integer(),
260 validator=deferred_repo_group_validator,
261 widget=deferred_repo_group_widget,
262 missing='')
263
264 def deserialize(self, cstruct):
265 """
266 Custom deserialize that allows to chain validation, and verify
267 permissions, and as last step uniqueness
268 """
269
270 # first pass, to validate given data
271 appstruct = super(RepoGroupSchema, self).deserialize(cstruct)
272 validated_name = appstruct['repo_group_name']
273
274 # second pass to validate permissions to repo_group
275 second = RepoGroupAccessSchema().bind(**self.bindings)
276 appstruct_second = second.deserialize({'repo_group': validated_name})
277 # save result
278 appstruct['repo_group'] = appstruct_second['repo_group']
279
280 # thirds to validate uniqueness
281 third = RepoGroupNameUniqueSchema().bind(**self.bindings)
282 third.deserialize({'unique_repo_group_name': validated_name})
283
284 return appstruct
@@ -20,11 +20,11 b' function registerRCRoutes() {'
20 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
20 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
21 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
21 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
22 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
22 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
23 pyroutes.register('repo_group_integrations_home', '/%(repo_group_name)s/settings/integrations', ['repo_group_name']);
23 pyroutes.register('repo_group_integrations_home', '/%(repo_group_name)s/_settings/integrations', ['repo_group_name']);
24 pyroutes.register('repo_group_integrations_new', '/%(repo_group_name)s/settings/integrations/new', ['repo_group_name']);
24 pyroutes.register('repo_group_integrations_new', '/%(repo_group_name)s/_settings/integrations/new', ['repo_group_name']);
25 pyroutes.register('repo_group_integrations_list', '/%(repo_group_name)s/settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
25 pyroutes.register('repo_group_integrations_list', '/%(repo_group_name)s/_settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
26 pyroutes.register('repo_group_integrations_create', '/%(repo_group_name)s/settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
26 pyroutes.register('repo_group_integrations_create', '/%(repo_group_name)s/_settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
27 pyroutes.register('repo_group_integrations_edit', '/%(repo_group_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
27 pyroutes.register('repo_group_integrations_edit', '/%(repo_group_name)s/_settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
28 pyroutes.register('repo_integrations_home', '/%(repo_name)s/settings/integrations', ['repo_name']);
28 pyroutes.register('repo_integrations_home', '/%(repo_name)s/settings/integrations', ['repo_name']);
29 pyroutes.register('repo_integrations_new', '/%(repo_name)s/settings/integrations/new', ['repo_name']);
29 pyroutes.register('repo_integrations_new', '/%(repo_name)s/settings/integrations/new', ['repo_name']);
30 pyroutes.register('repo_integrations_list', '/%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
30 pyroutes.register('repo_integrations_list', '/%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
@@ -100,6 +100,9 b' function registerRCRoutes() {'
100 pyroutes.register('repos', '/_admin/repos', []);
100 pyroutes.register('repos', '/_admin/repos', []);
101 pyroutes.register('repo_new', '/_admin/repos/new', []);
101 pyroutes.register('repo_new', '/_admin/repos/new', []);
102 pyroutes.register('repo_create', '/_admin/repos/create', []);
102 pyroutes.register('repo_create', '/_admin/repos/create', []);
103 pyroutes.register('repo_groups', '/_admin/repo_groups', []);
104 pyroutes.register('repo_group_new', '/_admin/repo_group/new', []);
105 pyroutes.register('repo_group_create', '/_admin/repo_group/create', []);
103 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
106 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
104 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
107 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
105 pyroutes.register('channelstream_proxy', '/_channelstream', []);
108 pyroutes.register('channelstream_proxy', '/_channelstream', []);
@@ -223,6 +226,11 b' function registerRCRoutes() {'
223 pyroutes.register('atom_feed_home', '/%(repo_name)s/feed/atom', ['repo_name']);
226 pyroutes.register('atom_feed_home', '/%(repo_name)s/feed/atom', ['repo_name']);
224 pyroutes.register('repo_summary', '/%(repo_name)s', ['repo_name']);
227 pyroutes.register('repo_summary', '/%(repo_name)s', ['repo_name']);
225 pyroutes.register('repo_summary_slash', '/%(repo_name)s/', ['repo_name']);
228 pyroutes.register('repo_summary_slash', '/%(repo_name)s/', ['repo_name']);
229 pyroutes.register('edit_repo_group', '/%(repo_group_name)s/_edit', ['repo_group_name']);
230 pyroutes.register('edit_repo_group_advanced', '/%(repo_group_name)s/_settings/advanced', ['repo_group_name']);
231 pyroutes.register('edit_repo_group_advanced_delete', '/%(repo_group_name)s/_settings/advanced/delete', ['repo_group_name']);
232 pyroutes.register('edit_repo_group_perms', '/%(repo_group_name)s/_settings/permissions', ['repo_group_name']);
233 pyroutes.register('edit_repo_group_perms_update', '/%(repo_group_name)s/_settings/permissions/update', ['repo_group_name']);
226 pyroutes.register('repo_group_home', '/%(repo_group_name)s', ['repo_group_name']);
234 pyroutes.register('repo_group_home', '/%(repo_group_name)s', ['repo_group_name']);
227 pyroutes.register('repo_group_home_slash', '/%(repo_group_name)s/', ['repo_group_name']);
235 pyroutes.register('repo_group_home_slash', '/%(repo_group_name)s/', ['repo_group_name']);
228 pyroutes.register('user_group_members_data', '/_admin/user_groups/%(user_group_id)s/members', ['user_group_id']);
236 pyroutes.register('user_group_members_data', '/_admin/user_groups/%(user_group_id)s/members', ['user_group_id']);
@@ -14,9 +14,9 b''
14 %elif c.repo_group:
14 %elif c.repo_group:
15 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
15 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
16 &raquo;
16 &raquo;
17 ${h.link_to(_('Repository Groups'),h.url('repo_groups'))}
17 ${h.link_to(_('Repository Groups'),h.route_path('repo_groups'))}
18 &raquo;
18 &raquo;
19 ${h.link_to(c.repo_group.group_name,h.url('edit_repo_group', group_name=c.repo_group.group_name))}
19 ${h.link_to(c.repo_group.group_name,h.route_path('edit_repo_group', repo_group_name=c.repo_group.group_name))}
20 &raquo;
20 &raquo;
21 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_group_integrations_home', repo_group_name=c.repo_group.group_name))}
21 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_group_integrations_home', repo_group_name=c.repo_group.group_name))}
22 &raquo;
22 &raquo;
@@ -7,9 +7,9 b''
7 %elif c.repo_group:
7 %elif c.repo_group:
8 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
8 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
9 &raquo;
9 &raquo;
10 ${h.link_to(_('Repository Groups'),h.url('repo_groups'))}
10 ${h.link_to(_('Repository Groups'),h.route_path('repo_groups'))}
11 &raquo;
11 &raquo;
12 ${h.link_to(c.repo_group.group_name,h.url('edit_repo_group', group_name=c.repo_group.group_name))}
12 ${h.link_to(c.repo_group.group_name,h.route_path('edit_repo_group', repo_group_name=c.repo_group.group_name))}
13 %else:
13 %else:
14 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
14 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
15 &raquo;
15 &raquo;
@@ -10,9 +10,9 b''
10 %elif c.repo_group:
10 %elif c.repo_group:
11 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
11 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
12 &raquo;
12 &raquo;
13 ${h.link_to(_('Repository Groups'),h.url('repo_groups'))}
13 ${h.link_to(_('Repository Groups'),h.route_path('repo_groups'))}
14 &raquo;
14 &raquo;
15 ${h.link_to(c.repo_group.group_name,h.url('edit_repo_group', group_name=c.repo_group.group_name))}
15 ${h.link_to(c.repo_group.group_name,h.route_path('edit_repo_group', repo_group_name=c.repo_group.group_name))}
16 &raquo;
16 &raquo;
17 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_group_integrations_home', repo_group_name=c.repo_group.group_name))}
17 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_group_integrations_home', repo_group_name=c.repo_group.group_name))}
18 %else:
18 %else:
@@ -11,7 +11,7 b''
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
13 &raquo;
13 &raquo;
14 ${h.link_to(_('Repository groups'),h.url('repo_groups'))}
14 ${h.link_to(_('Repository groups'),h.route_path('repo_groups'))}
15 &raquo;
15 &raquo;
16 ${_('Add Repository Group')}
16 ${_('Add Repository Group')}
17 </%def>
17 </%def>
@@ -27,7 +27,7 b''
27 ${self.breadcrumbs()}
27 ${self.breadcrumbs()}
28 </div>
28 </div>
29 <!-- end box / title -->
29 <!-- end box / title -->
30 ${h.secure_form(h.url('repo_groups'), request=request)}
30 ${h.secure_form(h.route_path('repo_group_create'), request=request)}
31 <div class="form">
31 <div class="form">
32 <!-- fields -->
32 <!-- fields -->
33 <div class="fields">
33 <div class="fields">
@@ -11,7 +11,7 b''
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
13 &raquo;
13 &raquo;
14 ${h.link_to(_('Repository Groups'),h.url('repo_groups'))}
14 ${h.link_to(_('Repository Groups'),h.route_path('repo_groups'))}
15 %if c.repo_group.parent_group:
15 %if c.repo_group.parent_group:
16 &raquo; ${h.link_to(c.repo_group.parent_group.name, h.route_path('repo_group_home', repo_group_name=c.repo_group.parent_group.group_name))}
16 &raquo; ${h.link_to(c.repo_group.parent_group.name, h.route_path('repo_group_home', repo_group_name=c.repo_group.parent_group.group_name))}
17 %endif
17 %endif
@@ -21,7 +21,7 b''
21 <%def name="breadcrumbs_side_links()">
21 <%def name="breadcrumbs_side_links()">
22 <ul class="links">
22 <ul class="links">
23 <li>
23 <li>
24 <a href="${h.url('new_repo_group', parent_group=c.repo_group.group_id)}" class="btn btn-small btn-success">${_(u'Add Child Group')}</a>
24 <a href="${h.route_path('repo_group_new', _query=dict(parent_group=c.repo_group.group_id))}" class="btn btn-small btn-success">${_(u'Add Child Group')}</a>
25 </li>
25 </li>
26 </ul>
26 </ul>
27 </%def>
27 </%def>
@@ -45,9 +45,9 b''
45 ##main
45 ##main
46 <div class="sidebar">
46 <div class="sidebar">
47 <ul class="nav nav-pills nav-stacked">
47 <ul class="nav nav-pills nav-stacked">
48 <li class="${'active' if c.active=='settings' else ''}"><a href="${h.url('edit_repo_group', group_name=c.repo_group.group_name)}">${_('Settings')}</a></li>
48 <li class="${'active' if c.active=='settings' else ''}"><a href="${h.route_path('edit_repo_group', repo_group_name=c.repo_group.group_name)}">${_('Settings')}</a></li>
49 <li class="${'active' if c.active=='perms' else ''}"><a href="${h.url('edit_repo_group_perms', group_name=c.repo_group.group_name)}">${_('Permissions')}</a></li>
49 <li class="${'active' if c.active=='permissions' else ''}"><a href="${h.route_path('edit_repo_group_perms', repo_group_name=c.repo_group.group_name)}">${_('Permissions')}</a></li>
50 <li class="${'active' if c.active=='advanced' else ''}"><a href="${h.url('edit_repo_group_advanced', group_name=c.repo_group.group_name)}">${_('Advanced')}</a></li>
50 <li class="${'active' if c.active=='advanced' else ''}"><a href="${h.route_path('edit_repo_group_advanced', repo_group_name=c.repo_group.group_name)}">${_('Advanced')}</a></li>
51 <li class="${'active' if c.active=='integrations' else ''}"><a href="${h.route_path('repo_group_integrations_home', repo_group_name=c.repo_group.group_name)}">${_('Integrations')}</a></li>
51 <li class="${'active' if c.active=='integrations' else ''}"><a href="${h.route_path('repo_group_integrations_home', repo_group_name=c.repo_group.group_name)}">${_('Integrations')}</a></li>
52 </ul>
52 </ul>
53 </div>
53 </div>
@@ -28,7 +28,7 b''
28 <h3 class="panel-title">${_('Delete repository group')}</h3>
28 <h3 class="panel-title">${_('Delete repository group')}</h3>
29 </div>
29 </div>
30 <div class="panel-body">
30 <div class="panel-body">
31 ${h.secure_form(h.url('delete_repo_group', group_name=c.repo_group.group_name),method='delete', request=request)}
31 ${h.secure_form(h.route_path('edit_repo_group_advanced_delete', repo_group_name=c.repo_group.group_name), request=request)}
32 <table class="display">
32 <table class="display">
33
33
34 <tr>
34 <tr>
@@ -5,14 +5,14 b''
5 <h3 class="panel-title">${_('Repository Group Permissions')}</h3>
5 <h3 class="panel-title">${_('Repository Group Permissions')}</h3>
6 </div>
6 </div>
7 <div class="panel-body">
7 <div class="panel-body">
8 ${h.secure_form(h.url('edit_repo_group_perms', group_name=c.repo_group.group_name),method='put', request=request)}
8 ${h.secure_form(h.route_path('edit_repo_group_perms_update', repo_group_name=c.repo_group.group_name), request=request)}
9 <table id="permissions_manage" class="rctable permissions">
9 <table id="permissions_manage" class="rctable permissions">
10 <tr>
10 <tr>
11 <th class="td-radio">${_('None')}</th>
11 <th class="td-radio">${_('None')}</th>
12 <th class="td-radio">${_('Read')}</th>
12 <th class="td-radio">${_('Read')}</th>
13 <th class="td-radio">${_('Write')}</th>
13 <th class="td-radio">${_('Write')}</th>
14 <th class="td-radio">${_('Admin')}</th>
14 <th class="td-radio">${_('Admin')}</th>
15 <th class="td-user">${_('User/User Group')}</th>
15 <th class="td-owner">${_('User/User Group')}</th>
16 <th></th>
16 <th></th>
17 </tr>
17 </tr>
18 ## USERS
18 ## USERS
@@ -25,7 +25,6 b''
25 <td class="td-radio">${h.radio('admin_perm_%s' % _user.user_id,'repository.admin', 'repository.admin', disabled="disabled")}</td>
25 <td class="td-radio">${h.radio('admin_perm_%s' % _user.user_id,'repository.admin', 'repository.admin', disabled="disabled")}</td>
26 <td class="td-user">
26 <td class="td-user">
27 ${base.gravatar(_user.email, 16)}
27 ${base.gravatar(_user.email, 16)}
28 <span class="user">
29 ${h.link_to_user(_user.username)}
28 ${h.link_to_user(_user.username)}
30 %if getattr(_user, 'admin_row', None):
29 %if getattr(_user, 'admin_row', None):
31 (${_('super admin')})
30 (${_('super admin')})
@@ -33,18 +32,17 b''
33 %if getattr(_user, 'owner_row', None):
32 %if getattr(_user, 'owner_row', None):
34 (${_('owner')})
33 (${_('owner')})
35 %endif
34 %endif
36 </span>
37 </td>
35 </td>
38 <td></td>
36 <td></td>
39 </tr>
37 </tr>
40 %else:
38 %else:
39 <tr>
41 ##forbid revoking permission from yourself, except if you're an super admin
40 ##forbid revoking permission from yourself, except if you're an super admin
42 <tr>
43 %if c.rhodecode_user.user_id != _user.user_id or c.rhodecode_user.is_admin:
41 %if c.rhodecode_user.user_id != _user.user_id or c.rhodecode_user.is_admin:
44 <td class="td-radio">${h.radio('u_perm_%s' % _user.user_id,'group.none')}</td>
42 <td class="td-radio">${h.radio('u_perm_%s' % _user.user_id,'group.none', checked=_user.permission=='group.none')}</td>
45 <td class="td-radio">${h.radio('u_perm_%s' % _user.user_id,'group.read')}</td>
43 <td class="td-radio">${h.radio('u_perm_%s' % _user.user_id,'group.read', checked=_user.permission=='group.read')}</td>
46 <td class="td-radio">${h.radio('u_perm_%s' % _user.user_id,'group.write')}</td>
44 <td class="td-radio">${h.radio('u_perm_%s' % _user.user_id,'group.write', checked=_user.permission=='group.write')}</td>
47 <td class="td-radio">${h.radio('u_perm_%s' % _user.user_id,'group.admin')}</td>
45 <td class="td-radio">${h.radio('u_perm_%s' % _user.user_id,'group.admin', checked=_user.permission=='group.admin')}</td>
48 <td class="td-user">
46 <td class="td-user">
49 ${base.gravatar(_user.email, 16)}
47 ${base.gravatar(_user.email, 16)}
50 <span class="user">
48 <span class="user">
@@ -89,10 +87,10 b''
89 ## USER GROUPS
87 ## USER GROUPS
90 %for _user_group in c.repo_group.permission_user_groups():
88 %for _user_group in c.repo_group.permission_user_groups():
91 <tr id="id${id(_user_group.users_group_name)}">
89 <tr id="id${id(_user_group.users_group_name)}">
92 <td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'group.none')}</td>
90 <td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'group.none', checked=_user_group.permission=='group.none')}</td>
93 <td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'group.read')}</td>
91 <td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'group.read', checked=_user_group.permission=='group.read')}</td>
94 <td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'group.write')}</td>
92 <td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'group.write', checked=_user_group.permission=='group.write')}</td>
95 <td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'group.admin')}</td>
93 <td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'group.admin', checked=_user_group.permission=='group.admin')}</td>
96 <td class="td-componentname">
94 <td class="td-componentname">
97 <i class="icon-group" ></i>
95 <i class="icon-group" ></i>
98 %if h.HasPermissionAny('hg.admin')():
96 %if h.HasPermissionAny('hg.admin')():
@@ -6,7 +6,7 b''
6 <h3 class="panel-title">${_('Settings for Repository Group: %s') % c.repo_group.name}</h3>
6 <h3 class="panel-title">${_('Settings for Repository Group: %s') % c.repo_group.name}</h3>
7 </div>
7 </div>
8 <div class="panel-body">
8 <div class="panel-body">
9 ${h.secure_form(h.url('update_repo_group',group_name=c.repo_group.group_name),method='put', request=request)}
9 ${h.secure_form(h.route_path('edit_repo_group', repo_group_name=c.repo_group.group_name), request=request)}
10 <div class="form">
10 <div class="form">
11 <!-- fields -->
11 <!-- fields -->
12 <div class="fields">
12 <div class="fields">
@@ -15,13 +15,26 b''
15 <label for="group_name">${_('Group Name')}:</label>
15 <label for="group_name">${_('Group Name')}:</label>
16 </div>
16 </div>
17 <div class="input">
17 <div class="input">
18 ${h.text('group_name',class_='medium')}
18 ${c.form['repo_group_name'].render(css_class='medium', oid='group_name')|n}
19 ${c.form.render_error(request, c.form['repo_group_name'])|n}
20 </div>
21 </div>
22
23 <div class="field">
24 <div class="label">
25 <label for="repo_group">${_('Group parent')}:</label>
26 </div>
27 <div class="select">
28 ${c.form['repo_group'].render(css_class='medium', oid='repo_group')|n}
29 ${c.form.render_error(request, c.form['repo_group'])|n}
30
31 <p class="help-block">${_('Optional select a parent group to move this repository group into.')}</p>
19 </div>
32 </div>
20 </div>
33 </div>
21
34
22 <div class="field badged-field">
35 <div class="field badged-field">
23 <div class="label">
36 <div class="label">
24 <label for="user">${_('Owner')}:</label>
37 <label for="repo_group_owner">${_('Owner')}:</label>
25 </div>
38 </div>
26 <div class="input">
39 <div class="input">
27 <div class="badge-input-container">
40 <div class="badge-input-container">
@@ -29,20 +42,22 b''
29 ${base.gravatar_with_user(c.repo_group.user.email, show_disabled=not c.repo_group.user.active)}
42 ${base.gravatar_with_user(c.repo_group.user.email, show_disabled=not c.repo_group.user.active)}
30 </div>
43 </div>
31 <div class="badge-input-wrap">
44 <div class="badge-input-wrap">
32 ${h.text('user', class_="medium", autocomplete="off")}
45 ${c.form['repo_group_owner'].render(css_class='medium', oid='repo_group_owner')|n}
33 </div>
46 </div>
34 </div>
47 </div>
35 <form:error name="user"/>
48 ${c.form.render_error(request, c.form['repo_group_owner'])|n}
36 <p class="help-block">${_('Change owner of this repository group.')}</p>
49 <p class="help-block">${_('Change owner of this repository group.')}</p>
37 </div>
50 </div>
38 </div>
51 </div>
39
52
40 <div class="field">
53 <div class="field">
41 <div class="label label-textarea">
54 <div class="label label-textarea">
42 <label for="group_description">${_('Description')}:</label>
55 <label for="repo_group_description">${_('Description')}:</label>
43 </div>
56 </div>
44 <div class="textarea text-area editor">
57 <div class="textarea text-area editor">
45 ${h.textarea('group_description',cols=23,rows=5,class_="medium")}
58 ${c.form['repo_group_description'].render(css_class='medium', oid='repo_group_description')|n}
59 ${c.form.render_error(request, c.form['repo_group_description'])|n}
60
46 <% metatags_url = h.literal('''<a href="#metatagsShow" onclick="$('#meta-tags-desc').toggle();return false">meta-tags</a>''') %>
61 <% metatags_url = h.literal('''<a href="#metatagsShow" onclick="$('#meta-tags-desc').toggle();return false">meta-tags</a>''') %>
47 <span class="help-block">${_('Plain text format with support of {metatags}').format(metatags=metatags_url)|n}</span>
62 <span class="help-block">${_('Plain text format with support of {metatags}').format(metatags=metatags_url)|n}</span>
48 <span id="meta-tags-desc" style="display: none">
63 <span id="meta-tags-desc" style="display: none">
@@ -53,22 +68,17 b''
53 </div>
68 </div>
54
69
55 <div class="field">
70 <div class="field">
56 <div class="label">
57 <label for="group_parent_id">${_('Group parent')}:</label>
58 </div>
59 <div class="select">
60 ${h.select('group_parent_id','',c.repo_groups,class_="medium")}
61 </div>
62 </div>
63 <div class="field">
64 <div class="label label-checkbox">
71 <div class="label label-checkbox">
65 <label for="enable_locking">${_('Enable Repository Locking')}:</label>
72 <label for="repo_group_enable_locking">${_('Enable Repository Locking')}:</label>
66 </div>
73 </div>
67 <div class="checkboxes">
74 <div class="checkboxes">
68 ${h.checkbox('enable_locking',value="True")}
75 ${c.form['repo_group_enable_locking'].render(css_class='medium', oid='repo_group_enable_locking')|n}
76 ${c.form.render_error(request, c.form['repo_group_enable_locking'])|n}
77
69 <span class="help-block">${_('Repository locking will be enabled on all subgroups and repositories inside this repository group. Pulling from a repository locks it, and it is unlocked by pushing back by the same user.')}</span>
78 <span class="help-block">${_('Repository locking will be enabled on all subgroups and repositories inside this repository group. Pulling from a repository locks it, and it is unlocked by pushing back by the same user.')}</span>
70 </div>
79 </div>
71 </div>
80 </div>
81
72 <div class="buttons">
82 <div class="buttons">
73 ${h.submit('save',_('Save'),class_="btn")}
83 ${h.submit('save',_('Save'),class_="btn")}
74 ${h.reset('reset',_('Reset'),class_="btn")}
84 ${h.reset('reset',_('Reset'),class_="btn")}
@@ -80,11 +90,6 b''
80 </div>
90 </div>
81 <script>
91 <script>
82 $(document).ready(function(){
92 $(document).ready(function(){
83 $("#group_parent_id").select2({
93 UsersAutoComplete('repo_group_owner', '${c.rhodecode_user.user_id}');
84 'containerCssClass': "drop-menu",
85 'dropdownCssClass': "drop-menu-dropdown",
86 'dropdownAutoWidth': true
87 });
88 UsersAutoComplete('user', '${c.rhodecode_user.user_id}');
89 })
94 })
90 </script>
95 </script>
@@ -24,7 +24,7 b''
24 <ul class="links">
24 <ul class="links">
25 %if h.HasPermissionAny('hg.admin','hg.repogroup.create.true')():
25 %if h.HasPermissionAny('hg.admin','hg.repogroup.create.true')():
26 <li>
26 <li>
27 <a href="${h.url('new_repo_group')}" class="btn btn-small btn-success">${_(u'Add Repository Group')}</a>
27 <a href="${h.route_path('repo_group_new')}" class="btn btn-small btn-success">${_(u'Add Repository Group')}</a>
28 </li>
28 </li>
29 %endif
29 %endif
30 </ul>
30 </ul>
@@ -74,7 +74,7 b''
74 <ul class="admin_menu submenu">
74 <ul class="admin_menu submenu">
75 <li><a href="${h.route_path('admin_audit_logs')}">${_('Admin audit logs')}</a></li>
75 <li><a href="${h.route_path('admin_audit_logs')}">${_('Admin audit logs')}</a></li>
76 <li><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
76 <li><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
77 <li><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
77 <li><a href="${h.route_path('repo_groups')}">${_('Repository groups')}</a></li>
78 <li><a href="${h.route_path('users')}">${_('Users')}</a></li>
78 <li><a href="${h.route_path('users')}">${_('Users')}</a></li>
79 <li><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li>
79 <li><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li>
80 <li><a href="${h.route_path('admin_permissions_application')}">${_('Permissions')}</a></li>
80 <li><a href="${h.route_path('admin_permissions_application')}">${_('Permissions')}</a></li>
@@ -148,7 +148,7 b''
148 <li class="local-admin-repos"><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
148 <li class="local-admin-repos"><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
149 %endif
149 %endif
150 %if repository_groups:
150 %if repository_groups:
151 <li class="local-admin-repo-groups"><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
151 <li class="local-admin-repo-groups"><a href="${h.route_path('repo_groups')}">${_('Repository groups')}</a></li>
152 %endif
152 %endif
153 %if user_groups:
153 %if user_groups:
154 <li class="local-admin-user-groups"><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li>
154 <li class="local-admin-user-groups"><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li>
@@ -209,7 +209,7 b''
209 %if section == 'repositories':
209 %if section == 'repositories':
210 <a href="${h.route_path('edit_repo_perms',repo_name=k,_anchor='permissions_manage')}">${_('edit')}</a>
210 <a href="${h.route_path('edit_repo_perms',repo_name=k,_anchor='permissions_manage')}">${_('edit')}</a>
211 %elif section == 'repositories_groups':
211 %elif section == 'repositories_groups':
212 <a href="${h.url('edit_repo_group_perms',group_name=k,anchor='permissions_manage')}">${_('edit')}</a>
212 <a href="${h.route_path('edit_repo_group_perms',repo_group_name=k,_anchor='permissions_manage')}">${_('edit')}</a>
213 %elif section == 'user_groups':
213 %elif section == 'user_groups':
214 ##<a href="${h.route_path('edit_user_group',user_group_id=k)}">${_('edit')}</a>
214 ##<a href="${h.route_path('edit_user_group',user_group_id=k)}">${_('edit')}</a>
215 %endif
215 %endif
@@ -237,10 +237,10 b''
237
237
238 <%def name="repo_group_actions(repo_group_id, repo_group_name, gr_count)">
238 <%def name="repo_group_actions(repo_group_id, repo_group_name, gr_count)">
239 <div class="grid_edit">
239 <div class="grid_edit">
240 <a href="${h.url('edit_repo_group',group_name=repo_group_name)}" title="${_('Edit')}">Edit</a>
240 <a href="${h.route_path('edit_repo_group',repo_group_name=repo_group_name)}" title="${_('Edit')}">Edit</a>
241 </div>
241 </div>
242 <div class="grid_delete">
242 <div class="grid_delete">
243 ${h.secure_form(h.url('delete_repo_group', group_name=repo_group_name),method='delete', request=request)}
243 ${h.secure_form(h.route_path('edit_repo_group_advanced_delete', repo_group_name=repo_group_name), request=request)}
244 ${h.submit('remove_%s' % repo_group_name,_('Delete'),class_="btn btn-link btn-danger",
244 ${h.submit('remove_%s' % repo_group_name,_('Delete'),class_="btn btn-link btn-danger",
245 onclick="return confirm('"+_ungettext('Confirm to delete this group: %s with %s repository','Confirm to delete this group: %s with %s repositories',gr_count) % (repo_group_name, gr_count)+"');")}
245 onclick="return confirm('"+_ungettext('Confirm to delete this group: %s with %s repository','Confirm to delete this group: %s with %s repositories',gr_count) % (repo_group_name, gr_count)+"');")}
246 ${h.end_form()}
246 ${h.end_form()}
@@ -31,7 +31,7 b''
31 %endif
31 %endif
32
32
33 %if is_admin or create_repo_group:
33 %if is_admin or create_repo_group:
34 <a href="${h.url('new_repo_group')}" class="btn btn-small btn-default">${_(u'Add Repository Group')}</a>
34 <a href="${h.route_path('repo_group_new')}" class="btn btn-small btn-default">${_(u'Add Repository Group')}</a>
35 %endif
35 %endif
36 %else:
36 %else:
37 ##we're inside other repository group other terms apply
37 ##we're inside other repository group other terms apply
@@ -39,10 +39,10 b''
39 <a href="${h.route_path('repo_new',_query=dict(parent_group=c.repo_group.group_id))}" class="btn btn-small btn-success btn-primary">${_('Add Repository')}</a>
39 <a href="${h.route_path('repo_new',_query=dict(parent_group=c.repo_group.group_id))}" class="btn btn-small btn-success btn-primary">${_('Add Repository')}</a>
40 %endif
40 %endif
41 %if is_admin or group_admin:
41 %if is_admin or group_admin:
42 <a href="${h.url('new_repo_group', parent_group=c.repo_group.group_id)}" class="btn btn-small btn-default">${_(u'Add Repository Group')}</a>
42 <a href="${h.route_path('repo_group_new',_query=dict(parent_group=c.repo_group.group_id))}" class="btn btn-small btn-default">${_(u'Add Repository Group')}</a>
43 %endif
43 %endif
44 %if is_admin or group_admin:
44 %if is_admin or group_admin:
45 <a href="${h.url('edit_repo_group',group_name=c.repo_group.group_name)}" title="${_('You have admin right to this group, and can edit it')}" class="btn btn-small btn-primary">${_('Edit Repository Group')}</a>
45 <a href="${h.route_path('edit_repo_group',repo_group_name=c.repo_group.group_name)}" title="${_('You have admin right to this group, and can edit it')}" class="btn btn-small btn-primary">${_('Edit Repository Group')}</a>
46 %endif
46 %endif
47 %endif
47 %endif
48 </div>
48 </div>
@@ -20,7 +20,7 b''
20
20
21 import pytest
21 import pytest
22
22
23 from rhodecode.tests import TestController, url
23 from rhodecode.tests import TestController
24 from rhodecode.tests.fixture import Fixture
24 from rhodecode.tests.fixture import Fixture
25
25
26
26
@@ -32,6 +32,8 b' def route_path(name, params=None, **kwar'
32 'home': '/',
32 'home': '/',
33 'repos':
33 'repos':
34 ADMIN_PREFIX + '/repos',
34 ADMIN_PREFIX + '/repos',
35 'repo_groups':
36 ADMIN_PREFIX + '/repo_groups',
35 'user_groups':
37 'user_groups':
36 ADMIN_PREFIX + '/user_groups',
38 ADMIN_PREFIX + '/user_groups',
37 'user_groups_data':
39 'user_groups_data':
@@ -65,7 +67,7 b' class TestAdminDelegatedUser(TestControl'
65 response = self.app.get(route_path('repos'), status=200)
67 response = self.app.get(route_path('repos'), status=200)
66 response.mustcontain('data: []')
68 response.mustcontain('data: []')
67
69
68 response = self.app.get(url('repo_groups'), status=200)
70 response = self.app.get(route_path('repo_groups'), status=200)
69 response.mustcontain('data: []')
71 response.mustcontain('data: []')
70
72
71 response = self.app.get(route_path('user_groups_data'),
73 response = self.app.get(route_path('user_groups_data'),
@@ -100,7 +102,7 b' class TestAdminDelegatedUser(TestControl'
100 response = self.app.get(route_path('repos'), status=200)
102 response = self.app.get(route_path('repos'), status=200)
101 response.mustcontain('"name_raw": "{}"'.format(repo_name))
103 response.mustcontain('"name_raw": "{}"'.format(repo_name))
102
104
103 response = self.app.get(url('repo_groups'), status=200)
105 response = self.app.get(route_path('repo_groups'), status=200)
104 response.mustcontain('"name_raw": "{}"'.format(repo_group_name))
106 response.mustcontain('"name_raw": "{}"'.format(repo_group_name))
105
107
106 response = self.app.get(route_path('user_groups_data'),
108 response = self.app.get(route_path('user_groups_data'),
@@ -142,7 +144,7 b' class TestAdminDelegatedUser(TestControl'
142 response = self.app.get(route_path('repos'), status=200)
144 response = self.app.get(route_path('repos'), status=200)
143 response.mustcontain('"name_raw": "{}"'.format(repo_name))
145 response.mustcontain('"name_raw": "{}"'.format(repo_name))
144
146
145 response = self.app.get(url('repo_groups'), status=200)
147 response = self.app.get(route_path('repo_groups'), status=200)
146 response.mustcontain('"name_raw": "{}"'.format(repo_group_name))
148 response.mustcontain('"name_raw": "{}"'.format(repo_group_name))
147
149
148 response = self.app.get(route_path('user_groups_data'),
150 response = self.app.get(route_path('user_groups_data'),
@@ -37,7 +37,6 b' from rhodecode.model.repo_group import R'
37 from rhodecode.model.scm import ScmModel
37 from rhodecode.model.scm import ScmModel
38 from rhodecode.model.settings import UiSetting, SettingsModel
38 from rhodecode.model.settings import UiSetting, SettingsModel
39 from rhodecode.tests.fixture import Fixture
39 from rhodecode.tests.fixture import Fixture
40 from rhodecode.tests import TEST_USER_ADMIN_LOGIN
41
40
42
41
43 fixture = Fixture()
42 fixture = Fixture()
@@ -221,23 +220,6 b' def platform_encodes_filenames():'
221 return path_with_latin1 != read_path
220 return path_with_latin1 != read_path
222
221
223
222
224 @pytest.fixture
225 def repo_groups(request):
226 session = meta.Session()
227 zombie_group = fixture.create_repo_group('zombie')
228 parent_group = fixture.create_repo_group('parent')
229 child_group = fixture.create_repo_group('parent/child')
230 groups_in_db = session.query(db.RepoGroup).all()
231 assert len(groups_in_db) == 3
232 assert child_group.group_parent_id == parent_group.group_id
233
234 @request.addfinalizer
235 def cleanup():
236 fixture.destroy_repo_group(zombie_group)
237 fixture.destroy_repo_group(child_group)
238 fixture.destroy_repo_group(parent_group)
239
240 return (zombie_group, parent_group, child_group)
241
223
242
224
243 def test_repo2db_mapper_groups(repo_groups):
225 def test_repo2db_mapper_groups(repo_groups):
@@ -59,7 +59,7 b' def _update_repo(name, **kwargs):'
59 return r
59 return r
60
60
61
61
62 class TestRepoGroups:
62 class TestRepoGroups(object):
63
63
64 @pytest.fixture(autouse=True)
64 @pytest.fixture(autouse=True)
65 def prepare(self, request):
65 def prepare(self, request):
@@ -1830,3 +1830,29 b' def disable_anonymous_user(request, pylo'
1830 @request.addfinalizer
1830 @request.addfinalizer
1831 def cleanup():
1831 def cleanup():
1832 set_anonymous_access(True)
1832 set_anonymous_access(True)
1833
1834
1835 @pytest.fixture
1836 def rc_fixture(request):
1837 return Fixture()
1838
1839
1840 @pytest.fixture
1841 def repo_groups(request):
1842 fixture = Fixture()
1843
1844 session = Session()
1845 zombie_group = fixture.create_repo_group('zombie')
1846 parent_group = fixture.create_repo_group('parent')
1847 child_group = fixture.create_repo_group('parent/child')
1848 groups_in_db = session.query(RepoGroup).all()
1849 assert len(groups_in_db) == 3
1850 assert child_group.group_parent_id == parent_group.group_id
1851
1852 @request.addfinalizer
1853 def cleanup():
1854 fixture.destroy_repo_group(zombie_group)
1855 fixture.destroy_repo_group(child_group)
1856 fixture.destroy_repo_group(parent_group)
1857
1858 return zombie_group, parent_group, child_group
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now