##// 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 260 self.db_repo_group = request.db_repo_group
261 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 278 class UserGroupAppView(BaseAppView):
265 279 def __init__(self, context, request):
@@ -295,6 +295,19 b' def admin_routes(config):'
295 295 name='repo_create',
296 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 312 def includeme(config):
300 313 settings = config.get_settings()
@@ -22,7 +22,36 b' from rhodecode.apps._base import add_rou'
22 22
23 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 55 add_route_with_slash(
27 56 config,
28 57 name='repo_group_home',
@@ -30,4 +59,3 b' def includeme(config):'
30 59
31 60 # Scan module for configuration decorators.
32 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 312 config.add_route(
313 313 name='edit_repo',
314 314 pattern='/{repo_name:.*?[^/]}/settings', repo_route=True)
315 # update is POST on edit_repo
315 316
316 317 # Settings advanced
317 318 config.add_route(
@@ -373,7 +374,6 b' def includeme(config):'
373 374 name='edit_repo_remote_pull',
374 375 pattern='/{repo_name:.*?[^/]}/settings/remote/pull', repo_route=True)
375 376
376
377 377 # Statistics
378 378 config.add_route(
379 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 198 assert_session_flash(
199 199 response,
200 msg='Repository %s updated successfully' % (backend.repo_name))
200 msg='Repository `%s` updated successfully' % (backend.repo_name))
201 201
202 202 repo = Repository.get_by_repo_name(backend.repo_name)
203 203 assert repo.private is True
@@ -218,7 +218,7 b' class TestAdminRepoSettings(object):'
218 218
219 219 assert_session_flash(
220 220 response,
221 msg='Repository %s updated successfully' % (backend.repo_name))
221 msg='Repository `%s` updated successfully' % (backend.repo_name))
222 222 assert backend.repo.private is False
223 223
224 224 # we turn off private now the repo default permission should stay None
@@ -164,7 +164,7 b' class RepoSettingsView(RepoAppView):'
164 164
165 165 Session().commit()
166 166
167 h.flash(_('Repository {} updated successfully').format(
167 h.flash(_('Repository `{}` updated successfully').format(
168 168 old_repo_name), category='success')
169 169 except Exception:
170 170 log.exception("Exception during update of repository")
@@ -172,53 +172,6 b' def make_map(config):'
172 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 175 # ADMIN SETTINGS ROUTES
223 176 with rmap.submapper(path_prefix=ADMIN_PREFIX,
224 177 controller='admin/settings') as m:
@@ -71,7 +71,7 b' def includeme(config):'
71 71
72 72 # repo group integrations
73 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 75 repo_group_route=True)
76 76
77 77 config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView',
@@ -81,7 +81,7 b' def includeme(config):'
81 81 route_name='repo_group_integrations_home')
82 82
83 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 85 repo_group_route=True)
86 86 config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView',
87 87 attr='new_integration',
@@ -90,7 +90,7 b' def includeme(config):'
90 90 route_name='repo_group_integrations_new')
91 91
92 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 94 repo_group_route=True,
95 95 custom_predicates=(valid_integration,))
96 96 config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView',
@@ -100,7 +100,7 b' def includeme(config):'
100 100 route_name='repo_group_integrations_list')
101 101
102 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 104 repo_group_route=True,
105 105 custom_predicates=(valid_integration,))
106 106 config.add_view('rhodecode.integrations.views.RepoGroupIntegrationsView',
@@ -115,7 +115,7 b' def includeme(config):'
115 115 route_name='repo_group_integrations_create')
116 116
117 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 119 repo_group_route=True,
120 120 custom_predicates=(valid_integration,))
121 121
@@ -145,7 +145,7 b' class TestRepoIntegrationsView(TestInteg'
145 145
146 146 class TestRepoGroupIntegrationsView(TestIntegrationsView):
147 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 149 repo_group_name=test_repo_group.group_name)
150 150 response = self.app.get(url)
151 151
@@ -155,7 +155,7 b' class TestRepoGroupIntegrationsView(Test'
155 155 def test_index_with_integrations(
156 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 159 repo_group_name=test_repo_group.group_name)
160 160
161 161 stub_name = repogroup_integration_stub.name
@@ -167,7 +167,7 b' class TestRepoGroupIntegrationsView(Test'
167 167
168 168 def test_new_integration_page(self, test_repo_group):
169 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 171 repo_group_name=test_repo_group.group_name)
172 172
173 173 response = self.app.get(url)
@@ -177,7 +177,7 b' class TestRepoGroupIntegrationsView(Test'
177 177 for integration_key, integration_obj in integration_type_registry.items():
178 178 if not integration_obj.is_dummy:
179 179 nurl = (
180 '/{repo_group_name}/settings/integrations/{integration}/new').format(
180 '/{repo_group_name}/_settings/integrations/{integration}/new').format(
181 181 repo_group_name=repo_group_name,
182 182 integration=integration_key)
183 183 response.mustcontain(nurl)
@@ -188,7 +188,7 b' class TestRepoGroupIntegrationsView(Test'
188 188 self, test_repo_group, IntegrationType):
189 189
190 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 192 ).format(repo_group_name=repo_group_name,
193 193 integration_key=IntegrationType.key)
194 194
@@ -202,7 +202,7 b' class TestRepoGroupIntegrationsView(Test'
202 202 StubIntegrationType, csrf_token):
203 203
204 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 206 ).format(repo_group_name=repo_group_name,
207 207 integration_key=StubIntegrationType.key)
208 208
@@ -232,9 +232,9 b' def _post_integration_test_helper(app, u'
232 232 ('repo:%s' % repo_name,
233 233 '/%s/settings/integrations' % repo_name),
234 234 ('repogroup:%s' % repo_group_name,
235 '/%s/settings/integrations' % repo_group_name),
235 '/%s/_settings/integrations' % repo_group_name),
236 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 240 for scope, destination in scopes_destinations:
@@ -254,8 +254,11 b' class RepoGroupModel(BaseModel):'
254 254 # functions can delete this
255 255 cleanup_group = self.check_exist_filesystem(group_name,
256 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 261 try:
258 user = self._get_user(owner)
259 262 new_repo_group = RepoGroup()
260 263 new_repo_group.user = user
261 264 new_repo_group.group_description = group_description or group_name
@@ -736,3 +739,26 b' class RepoGroupModel(BaseModel):'
736 739 repo_group_data.append(row)
737 740
738 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 22 import colander
23 import deform.widget
23 24
24 25 from rhodecode.translation import _
25 26 from rhodecode.model.validation_schema import validators, preparers, types
@@ -143,6 +144,19 b' def deferred_repo_group_name_validator(n'
143 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 160 class GroupType(colander.Mapping):
147 161 def _validate(self, node, value):
148 162 try:
@@ -208,15 +222,15 b' class RepoGroupSchema(colander.Schema):'
208 222 validator=deferred_repo_group_owner_validator)
209 223
210 224 repo_group_description = colander.SchemaNode(
211 colander.String(), missing='')
225 colander.String(), missing='', widget=deform.widget.TextAreaWidget())
212 226
213 227 repo_group_copy_permissions = colander.SchemaNode(
214 228 types.StringBooleanType(),
215 missing=False)
229 missing=False, widget=deform.widget.CheckboxWidget())
216 230
217 231 repo_group_enable_locking = colander.SchemaNode(
218 232 types.StringBooleanType(),
219 missing=False)
233 missing=False, widget=deform.widget.CheckboxWidget())
220 234
221 235 def deserialize(self, cstruct):
222 236 """
@@ -238,3 +252,33 b' class RepoGroupSchema(colander.Schema):'
238 252 third.deserialize({'unique_repo_group_name': validated_name})
239 253
240 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 20 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
21 21 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
22 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']);
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']);
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']);
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']);
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']);
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 28 pyroutes.register('repo_integrations_home', '/%(repo_name)s/settings/integrations', ['repo_name']);
29 29 pyroutes.register('repo_integrations_new', '/%(repo_name)s/settings/integrations/new', ['repo_name']);
30 30 pyroutes.register('repo_integrations_list', '/%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
@@ -100,6 +100,9 b' function registerRCRoutes() {'
100 100 pyroutes.register('repos', '/_admin/repos', []);
101 101 pyroutes.register('repo_new', '/_admin/repos/new', []);
102 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 106 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
104 107 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
105 108 pyroutes.register('channelstream_proxy', '/_channelstream', []);
@@ -223,6 +226,11 b' function registerRCRoutes() {'
223 226 pyroutes.register('atom_feed_home', '/%(repo_name)s/feed/atom', ['repo_name']);
224 227 pyroutes.register('repo_summary', '/%(repo_name)s', ['repo_name']);
225 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 234 pyroutes.register('repo_group_home', '/%(repo_group_name)s', ['repo_group_name']);
227 235 pyroutes.register('repo_group_home_slash', '/%(repo_group_name)s/', ['repo_group_name']);
228 236 pyroutes.register('user_group_members_data', '/_admin/user_groups/%(user_group_id)s/members', ['user_group_id']);
@@ -14,9 +14,9 b''
14 14 %elif c.repo_group:
15 15 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
16 16 &raquo;
17 ${h.link_to(_('Repository Groups'),h.url('repo_groups'))}
17 ${h.link_to(_('Repository Groups'),h.route_path('repo_groups'))}
18 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 20 &raquo;
21 21 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_group_integrations_home', repo_group_name=c.repo_group.group_name))}
22 22 &raquo;
@@ -7,9 +7,9 b''
7 7 %elif c.repo_group:
8 8 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
9 9 &raquo;
10 ${h.link_to(_('Repository Groups'),h.url('repo_groups'))}
10 ${h.link_to(_('Repository Groups'),h.route_path('repo_groups'))}
11 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 13 %else:
14 14 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
15 15 &raquo;
@@ -10,9 +10,9 b''
10 10 %elif c.repo_group:
11 11 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
12 12 &raquo;
13 ${h.link_to(_('Repository Groups'),h.url('repo_groups'))}
13 ${h.link_to(_('Repository Groups'),h.route_path('repo_groups'))}
14 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 16 &raquo;
17 17 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_group_integrations_home', repo_group_name=c.repo_group.group_name))}
18 18 %else:
@@ -11,7 +11,7 b''
11 11 <%def name="breadcrumbs_links()">
12 12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
13 13 &raquo;
14 ${h.link_to(_('Repository groups'),h.url('repo_groups'))}
14 ${h.link_to(_('Repository groups'),h.route_path('repo_groups'))}
15 15 &raquo;
16 16 ${_('Add Repository Group')}
17 17 </%def>
@@ -27,7 +27,7 b''
27 27 ${self.breadcrumbs()}
28 28 </div>
29 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 31 <div class="form">
32 32 <!-- fields -->
33 33 <div class="fields">
@@ -11,7 +11,7 b''
11 11 <%def name="breadcrumbs_links()">
12 12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
13 13 &raquo;
14 ${h.link_to(_('Repository Groups'),h.url('repo_groups'))}
14 ${h.link_to(_('Repository Groups'),h.route_path('repo_groups'))}
15 15 %if c.repo_group.parent_group:
16 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 17 %endif
@@ -21,7 +21,7 b''
21 21 <%def name="breadcrumbs_side_links()">
22 22 <ul class="links">
23 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 25 </li>
26 26 </ul>
27 27 </%def>
@@ -45,9 +45,9 b''
45 45 ##main
46 46 <div class="sidebar">
47 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>
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>
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>
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=='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.route_path('edit_repo_group_advanced', repo_group_name=c.repo_group.group_name)}">${_('Advanced')}</a></li>
51 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 52 </ul>
53 53 </div>
@@ -28,7 +28,7 b''
28 28 <h3 class="panel-title">${_('Delete repository group')}</h3>
29 29 </div>
30 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 32 <table class="display">
33 33
34 34 <tr>
@@ -5,14 +5,14 b''
5 5 <h3 class="panel-title">${_('Repository Group Permissions')}</h3>
6 6 </div>
7 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 9 <table id="permissions_manage" class="rctable permissions">
10 10 <tr>
11 11 <th class="td-radio">${_('None')}</th>
12 12 <th class="td-radio">${_('Read')}</th>
13 13 <th class="td-radio">${_('Write')}</th>
14 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 16 <th></th>
17 17 </tr>
18 18 ## USERS
@@ -25,7 +25,6 b''
25 25 <td class="td-radio">${h.radio('admin_perm_%s' % _user.user_id,'repository.admin', 'repository.admin', disabled="disabled")}</td>
26 26 <td class="td-user">
27 27 ${base.gravatar(_user.email, 16)}
28 <span class="user">
29 28 ${h.link_to_user(_user.username)}
30 29 %if getattr(_user, 'admin_row', None):
31 30 (${_('super admin')})
@@ -33,18 +32,17 b''
33 32 %if getattr(_user, 'owner_row', None):
34 33 (${_('owner')})
35 34 %endif
36 </span>
37 35 </td>
38 36 <td></td>
39 37 </tr>
40 38 %else:
41 ##forbid revoking permission from yourself, except if you're an super admin
42 39 <tr>
40 ##forbid revoking permission from yourself, except if you're an super admin
43 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>
45 <td class="td-radio">${h.radio('u_perm_%s' % _user.user_id,'group.read')}</td>
46 <td class="td-radio">${h.radio('u_perm_%s' % _user.user_id,'group.write')}</td>
47 <td class="td-radio">${h.radio('u_perm_%s' % _user.user_id,'group.admin')}</td>
42 <td class="td-radio">${h.radio('u_perm_%s' % _user.user_id,'group.none', checked=_user.permission=='group.none')}</td>
43 <td class="td-radio">${h.radio('u_perm_%s' % _user.user_id,'group.read', checked=_user.permission=='group.read')}</td>
44 <td class="td-radio">${h.radio('u_perm_%s' % _user.user_id,'group.write', checked=_user.permission=='group.write')}</td>
45 <td class="td-radio">${h.radio('u_perm_%s' % _user.user_id,'group.admin', checked=_user.permission=='group.admin')}</td>
48 46 <td class="td-user">
49 47 ${base.gravatar(_user.email, 16)}
50 48 <span class="user">
@@ -89,10 +87,10 b''
89 87 ## USER GROUPS
90 88 %for _user_group in c.repo_group.permission_user_groups():
91 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>
93 <td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'group.read')}</td>
94 <td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'group.write')}</td>
95 <td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'group.admin')}</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>
91 <td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'group.read', checked=_user_group.permission=='group.read')}</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>
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 94 <td class="td-componentname">
97 95 <i class="icon-group" ></i>
98 96 %if h.HasPermissionAny('hg.admin')():
@@ -6,7 +6,7 b''
6 6 <h3 class="panel-title">${_('Settings for Repository Group: %s') % c.repo_group.name}</h3>
7 7 </div>
8 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 10 <div class="form">
11 11 <!-- fields -->
12 12 <div class="fields">
@@ -15,13 +15,26 b''
15 15 <label for="group_name">${_('Group Name')}:</label>
16 16 </div>
17 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 32 </div>
20 33 </div>
21 34
22 35 <div class="field badged-field">
23 36 <div class="label">
24 <label for="user">${_('Owner')}:</label>
37 <label for="repo_group_owner">${_('Owner')}:</label>
25 38 </div>
26 39 <div class="input">
27 40 <div class="badge-input-container">
@@ -29,20 +42,22 b''
29 42 ${base.gravatar_with_user(c.repo_group.user.email, show_disabled=not c.repo_group.user.active)}
30 43 </div>
31 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 46 </div>
34 47 </div>
35 <form:error name="user"/>
48 ${c.form.render_error(request, c.form['repo_group_owner'])|n}
36 49 <p class="help-block">${_('Change owner of this repository group.')}</p>
37 50 </div>
38 51 </div>
39 52
40 53 <div class="field">
41 54 <div class="label label-textarea">
42 <label for="group_description">${_('Description')}:</label>
55 <label for="repo_group_description">${_('Description')}:</label>
43 56 </div>
44 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 61 <% metatags_url = h.literal('''<a href="#metatagsShow" onclick="$('#meta-tags-desc').toggle();return false">meta-tags</a>''') %>
47 62 <span class="help-block">${_('Plain text format with support of {metatags}').format(metatags=metatags_url)|n}</span>
48 63 <span id="meta-tags-desc" style="display: none">
@@ -53,22 +68,17 b''
53 68 </div>
54 69
55 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 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 73 </div>
67 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 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 79 </div>
71 80 </div>
81
72 82 <div class="buttons">
73 83 ${h.submit('save',_('Save'),class_="btn")}
74 84 ${h.reset('reset',_('Reset'),class_="btn")}
@@ -80,11 +90,6 b''
80 90 </div>
81 91 <script>
82 92 $(document).ready(function(){
83 $("#group_parent_id").select2({
84 'containerCssClass': "drop-menu",
85 'dropdownCssClass': "drop-menu-dropdown",
86 'dropdownAutoWidth': true
87 });
88 UsersAutoComplete('user', '${c.rhodecode_user.user_id}');
93 UsersAutoComplete('repo_group_owner', '${c.rhodecode_user.user_id}');
89 94 })
90 95 </script>
@@ -24,7 +24,7 b''
24 24 <ul class="links">
25 25 %if h.HasPermissionAny('hg.admin','hg.repogroup.create.true')():
26 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 28 </li>
29 29 %endif
30 30 </ul>
@@ -74,7 +74,7 b''
74 74 <ul class="admin_menu submenu">
75 75 <li><a href="${h.route_path('admin_audit_logs')}">${_('Admin audit logs')}</a></li>
76 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 78 <li><a href="${h.route_path('users')}">${_('Users')}</a></li>
79 79 <li><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li>
80 80 <li><a href="${h.route_path('admin_permissions_application')}">${_('Permissions')}</a></li>
@@ -148,7 +148,7 b''
148 148 <li class="local-admin-repos"><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
149 149 %endif
150 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 152 %endif
153 153 %if user_groups:
154 154 <li class="local-admin-user-groups"><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li>
@@ -209,7 +209,7 b''
209 209 %if section == 'repositories':
210 210 <a href="${h.route_path('edit_repo_perms',repo_name=k,_anchor='permissions_manage')}">${_('edit')}</a>
211 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 213 %elif section == 'user_groups':
214 214 ##<a href="${h.route_path('edit_user_group',user_group_id=k)}">${_('edit')}</a>
215 215 %endif
@@ -237,10 +237,10 b''
237 237
238 238 <%def name="repo_group_actions(repo_group_id, repo_group_name, gr_count)">
239 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 241 </div>
242 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 244 ${h.submit('remove_%s' % repo_group_name,_('Delete'),class_="btn btn-link btn-danger",
245 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 246 ${h.end_form()}
@@ -31,7 +31,7 b''
31 31 %endif
32 32
33 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 35 %endif
36 36 %else:
37 37 ##we're inside other repository group other terms apply
@@ -39,10 +39,10 b''
39 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 40 %endif
41 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 43 %endif
44 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 46 %endif
47 47 %endif
48 48 </div>
@@ -20,7 +20,7 b''
20 20
21 21 import pytest
22 22
23 from rhodecode.tests import TestController, url
23 from rhodecode.tests import TestController
24 24 from rhodecode.tests.fixture import Fixture
25 25
26 26
@@ -32,6 +32,8 b' def route_path(name, params=None, **kwar'
32 32 'home': '/',
33 33 'repos':
34 34 ADMIN_PREFIX + '/repos',
35 'repo_groups':
36 ADMIN_PREFIX + '/repo_groups',
35 37 'user_groups':
36 38 ADMIN_PREFIX + '/user_groups',
37 39 'user_groups_data':
@@ -65,7 +67,7 b' class TestAdminDelegatedUser(TestControl'
65 67 response = self.app.get(route_path('repos'), status=200)
66 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 71 response.mustcontain('data: []')
70 72
71 73 response = self.app.get(route_path('user_groups_data'),
@@ -100,7 +102,7 b' class TestAdminDelegatedUser(TestControl'
100 102 response = self.app.get(route_path('repos'), status=200)
101 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 106 response.mustcontain('"name_raw": "{}"'.format(repo_group_name))
105 107
106 108 response = self.app.get(route_path('user_groups_data'),
@@ -142,7 +144,7 b' class TestAdminDelegatedUser(TestControl'
142 144 response = self.app.get(route_path('repos'), status=200)
143 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 148 response.mustcontain('"name_raw": "{}"'.format(repo_group_name))
147 149
148 150 response = self.app.get(route_path('user_groups_data'),
@@ -37,7 +37,6 b' from rhodecode.model.repo_group import R'
37 37 from rhodecode.model.scm import ScmModel
38 38 from rhodecode.model.settings import UiSetting, SettingsModel
39 39 from rhodecode.tests.fixture import Fixture
40 from rhodecode.tests import TEST_USER_ADMIN_LOGIN
41 40
42 41
43 42 fixture = Fixture()
@@ -221,23 +220,6 b' def platform_encodes_filenames():'
221 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 225 def test_repo2db_mapper_groups(repo_groups):
@@ -59,7 +59,7 b' def _update_repo(name, **kwargs):'
59 59 return r
60 60
61 61
62 class TestRepoGroups:
62 class TestRepoGroups(object):
63 63
64 64 @pytest.fixture(autouse=True)
65 65 def prepare(self, request):
@@ -1830,3 +1830,29 b' def disable_anonymous_user(request, pylo'
1830 1830 @request.addfinalizer
1831 1831 def cleanup():
1832 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
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now