##// END OF EJS Templates
repo-permissions: moved permissions into pyramid....
marcink -
r1734:ddacc559 default
parent child Browse files
Show More
@@ -0,0 +1,98 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 import deform
24 from pyramid.httpexceptions import HTTPFound
25 from pyramid.view import view_config
26
27 from rhodecode.apps._base import RepoAppView
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, HasRepoPermissionAnyDecorator,
33 HasRepoPermissionAllDecorator, CSRFRequired)
34 from rhodecode.model.db import RepositoryField, RepoGroup
35 from rhodecode.model.forms import RepoPermsForm
36 from rhodecode.model.meta import Session
37 from rhodecode.model.repo import RepoModel
38 from rhodecode.model.scm import RepoGroupList, ScmModel
39 from rhodecode.model.validation_schema.schemas import repo_schema
40
41 log = logging.getLogger(__name__)
42
43
44 class RepoSettingsPermissionsView(RepoAppView):
45
46 def load_default_context(self):
47 c = self._get_local_tmpl_context()
48
49 # TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead
50 c.repo_info = self.db_repo
51
52 self._register_global_c(c)
53 return c
54
55 @LoginRequired()
56 @HasRepoPermissionAnyDecorator('repository.admin')
57 @view_config(
58 route_name='edit_repo_perms', request_method='GET',
59 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
60 def edit_permissions(self):
61 c = self.load_default_context()
62 c.active = 'permissions'
63 return self._get_template_context(c)
64
65 @LoginRequired()
66 @HasRepoPermissionAllDecorator('repository.admin')
67 @CSRFRequired()
68 @view_config(
69 route_name='edit_repo_perms', request_method='POST',
70 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
71 def edit_permissions_update(self):
72 _ = self.request.translate
73 c = self.load_default_context()
74 c.active = 'permissions'
75 data = self.request.POST
76 # store private flag outside of HTML to verify if we can modify
77 # default user permissions, prevents submition of FAKE post data
78 # into the form for private repos
79 data['repo_private'] = self.db_repo.private
80 form = RepoPermsForm()().to_python(data)
81 changes = RepoModel().update_permissions(
82 self.db_repo_name, form['perm_additions'], form['perm_updates'],
83 form['perm_deletions'])
84
85 action_data = {
86 'added': changes['added'],
87 'updated': changes['updated'],
88 'deleted': changes['deleted'],
89 }
90 audit_logger.store(
91 'repo.edit.permissions', action_data=action_data,
92 user=self._rhodecode_user, repo=self.db_repo)
93
94 Session().commit()
95 h.flash(_('Repository permissions updated'), category='success')
96
97 raise HTTPFound(
98 self.request.route_path('edit_repo_perms', repo_name=self.db_repo_name))
@@ -1,61 +1,66 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 def includeme(config):
22 def includeme(config):
23
23
24 # Settings
24 # Settings
25 config.add_route(
25 config.add_route(
26 name='edit_repo',
26 name='edit_repo',
27 pattern='/{repo_name:.*?[^/]}/settings', repo_route=True)
27 pattern='/{repo_name:.*?[^/]}/settings', repo_route=True)
28
28
29 # Caches
29 # Caches
30 config.add_route(
30 config.add_route(
31 name='edit_repo_caches',
31 name='edit_repo_caches',
32 pattern='/{repo_name:.*?[^/]}/settings/caches', repo_route=True)
32 pattern='/{repo_name:.*?[^/]}/settings/caches', repo_route=True)
33
33
34 # Permissions
35 config.add_route(
36 name='edit_repo_perms',
37 pattern='/{repo_name:.*?[^/]}/settings/permissions', repo_route=True)
38
34 # Repo Review Rules
39 # Repo Review Rules
35 config.add_route(
40 config.add_route(
36 name='repo_reviewers',
41 name='repo_reviewers',
37 pattern='/{repo_name:.*?[^/]}/settings/review/rules', repo_route=True)
42 pattern='/{repo_name:.*?[^/]}/settings/review/rules', repo_route=True)
38
43
39 # Maintenance
44 # Maintenance
40 config.add_route(
45 config.add_route(
41 name='repo_maintenance',
46 name='repo_maintenance',
42 pattern='/{repo_name:.*?[^/]}/maintenance', repo_route=True)
47 pattern='/{repo_name:.*?[^/]}/maintenance', repo_route=True)
43
48
44 config.add_route(
49 config.add_route(
45 name='repo_maintenance_execute',
50 name='repo_maintenance_execute',
46 pattern='/{repo_name:.*?[^/]}/maintenance/execute', repo_route=True)
51 pattern='/{repo_name:.*?[^/]}/maintenance/execute', repo_route=True)
47
52
48 # Strip
53 # Strip
49 config.add_route(
54 config.add_route(
50 name='strip',
55 name='strip',
51 pattern='/{repo_name:.*?[^/]}/strip', repo_route=True)
56 pattern='/{repo_name:.*?[^/]}/strip', repo_route=True)
52
57
53 config.add_route(
58 config.add_route(
54 name='strip_check',
59 name='strip_check',
55 pattern='/{repo_name:.*?[^/]}/strip_check', repo_route=True)
60 pattern='/{repo_name:.*?[^/]}/strip_check', repo_route=True)
56
61
57 config.add_route(
62 config.add_route(
58 name='strip_execute',
63 name='strip_execute',
59 pattern='/{repo_name:.*?[^/]}/strip_execute', repo_route=True)
64 pattern='/{repo_name:.*?[^/]}/strip_execute', repo_route=True)
60 # Scan module for configuration decorators.
65 # Scan module for configuration decorators.
61 config.scan()
66 config.scan()
@@ -1,231 +1,232 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import mock
21 import mock
22 import pytest
22 import pytest
23
23
24 from rhodecode.lib.utils2 import str2bool
24 from rhodecode.lib.utils2 import str2bool
25 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
25 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
26 from rhodecode.model.db import Repository, UserRepoToPerm, Permission, User
26 from rhodecode.model.db import Repository, UserRepoToPerm, Permission, User
27 from rhodecode.model.meta import Session
27 from rhodecode.model.meta import Session
28 from rhodecode.tests import (
28 from rhodecode.tests import (
29 url, HG_REPO, TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN,
29 url, HG_REPO, TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN,
30 assert_session_flash)
30 assert_session_flash)
31 from rhodecode.tests.fixture import Fixture
31 from rhodecode.tests.fixture import Fixture
32
32
33 fixture = Fixture()
33 fixture = Fixture()
34
34
35
35
36 def route_path(name, params=None, **kwargs):
36 def route_path(name, params=None, **kwargs):
37 import urllib
37 import urllib
38
38
39 base_url = {
39 base_url = {
40 'edit_repo': '/{repo_name}/settings',
40 'edit_repo': '/{repo_name}/settings',
41 'edit_repo_caches': '/{repo_name}/settings/caches',
41 'edit_repo_caches': '/{repo_name}/settings/caches',
42 'edit_repo_perms': '/{repo_name}/settings/permissions',
42 }[name].format(**kwargs)
43 }[name].format(**kwargs)
43
44
44 if params:
45 if params:
45 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
46 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
46 return base_url
47 return base_url
47
48
48
49
49 def _get_permission_for_user(user, repo):
50 def _get_permission_for_user(user, repo):
50 perm = UserRepoToPerm.query()\
51 perm = UserRepoToPerm.query()\
51 .filter(UserRepoToPerm.repository ==
52 .filter(UserRepoToPerm.repository ==
52 Repository.get_by_repo_name(repo))\
53 Repository.get_by_repo_name(repo))\
53 .filter(UserRepoToPerm.user == User.get_by_username(user))\
54 .filter(UserRepoToPerm.user == User.get_by_username(user))\
54 .all()
55 .all()
55 return perm
56 return perm
56
57
57
58
58 @pytest.mark.usefixtures('autologin_user', 'app')
59 @pytest.mark.usefixtures('autologin_user', 'app')
59 class TestAdminRepoSettings(object):
60 class TestAdminRepoSettings(object):
60 @pytest.mark.parametrize('urlname', [
61 @pytest.mark.parametrize('urlname', [
61 'edit_repo',
62 'edit_repo',
62 'edit_repo_caches'
63 'edit_repo_caches',
64 'edit_repo_perms',
63 ])
65 ])
64 def test_show_page(self, urlname, app, backend):
66 def test_show_page(self, urlname, app, backend):
65 app.get(route_path(urlname, repo_name=backend.repo_name), status=200)
67 app.get(route_path(urlname, repo_name=backend.repo_name), status=200)
66
68
67 def test_edit_accessible_when_missing_requirements(
69 def test_edit_accessible_when_missing_requirements(
68 self, backend_hg, autologin_user):
70 self, backend_hg, autologin_user):
69 scm_patcher = mock.patch.object(
71 scm_patcher = mock.patch.object(
70 Repository, 'scm_instance', side_effect=RepositoryRequirementError)
72 Repository, 'scm_instance', side_effect=RepositoryRequirementError)
71 with scm_patcher:
73 with scm_patcher:
72 self.app.get(route_path('edit_repo', repo_name=backend_hg.repo_name))
74 self.app.get(route_path('edit_repo', repo_name=backend_hg.repo_name))
73
75
74 @pytest.mark.parametrize('urlname', [
76 @pytest.mark.parametrize('urlname', [
75 'edit_repo_perms',
76 'edit_repo_advanced',
77 'edit_repo_advanced',
77 'repo_vcs_settings',
78 'repo_vcs_settings',
78 'edit_repo_fields',
79 'edit_repo_fields',
79 'repo_settings_issuetracker',
80 'repo_settings_issuetracker',
80 'edit_repo_remote',
81 'edit_repo_remote',
81 'edit_repo_statistics',
82 'edit_repo_statistics',
82 ])
83 ])
83 def test_show_page_pylons(self, urlname, app):
84 def test_show_page_pylons(self, urlname, app):
84 app.get(url(urlname, repo_name=HG_REPO))
85 app.get(url(urlname, repo_name=HG_REPO))
85
86
86 @pytest.mark.parametrize('update_settings', [
87 @pytest.mark.parametrize('update_settings', [
87 {'repo_description': 'alter-desc'},
88 {'repo_description': 'alter-desc'},
88 {'repo_owner': TEST_USER_REGULAR_LOGIN},
89 {'repo_owner': TEST_USER_REGULAR_LOGIN},
89 {'repo_private': 'true'},
90 {'repo_private': 'true'},
90 {'repo_enable_locking': 'true'},
91 {'repo_enable_locking': 'true'},
91 {'repo_enable_downloads': 'true'},
92 {'repo_enable_downloads': 'true'},
92 ])
93 ])
93 def test_update_repo_settings(self, update_settings, csrf_token, backend, user_util):
94 def test_update_repo_settings(self, update_settings, csrf_token, backend, user_util):
94 repo = user_util.create_repo(repo_type=backend.alias)
95 repo = user_util.create_repo(repo_type=backend.alias)
95 repo_name = repo.repo_name
96 repo_name = repo.repo_name
96
97
97 params = fixture._get_repo_create_params(
98 params = fixture._get_repo_create_params(
98 csrf_token=csrf_token,
99 csrf_token=csrf_token,
99 repo_name=repo_name,
100 repo_name=repo_name,
100 repo_type=backend.alias,
101 repo_type=backend.alias,
101 repo_owner=TEST_USER_ADMIN_LOGIN,
102 repo_owner=TEST_USER_ADMIN_LOGIN,
102 repo_description='DESC',
103 repo_description='DESC',
103
104
104 repo_private='false',
105 repo_private='false',
105 repo_enable_locking='false',
106 repo_enable_locking='false',
106 repo_enable_downloads='false')
107 repo_enable_downloads='false')
107 params.update(update_settings)
108 params.update(update_settings)
108 self.app.post(
109 self.app.post(
109 route_path('edit_repo', repo_name=repo_name),
110 route_path('edit_repo', repo_name=repo_name),
110 params=params, status=302)
111 params=params, status=302)
111
112
112 repo = Repository.get_by_repo_name(repo_name)
113 repo = Repository.get_by_repo_name(repo_name)
113 assert repo.user.username == \
114 assert repo.user.username == \
114 update_settings.get('repo_owner', repo.user.username)
115 update_settings.get('repo_owner', repo.user.username)
115
116
116 assert repo.description == \
117 assert repo.description == \
117 update_settings.get('repo_description', repo.description)
118 update_settings.get('repo_description', repo.description)
118
119
119 assert repo.private == \
120 assert repo.private == \
120 str2bool(update_settings.get(
121 str2bool(update_settings.get(
121 'repo_private', repo.private))
122 'repo_private', repo.private))
122
123
123 assert repo.enable_locking == \
124 assert repo.enable_locking == \
124 str2bool(update_settings.get(
125 str2bool(update_settings.get(
125 'repo_enable_locking', repo.enable_locking))
126 'repo_enable_locking', repo.enable_locking))
126
127
127 assert repo.enable_downloads == \
128 assert repo.enable_downloads == \
128 str2bool(update_settings.get(
129 str2bool(update_settings.get(
129 'repo_enable_downloads', repo.enable_downloads))
130 'repo_enable_downloads', repo.enable_downloads))
130
131
131 def test_update_repo_name_via_settings(self, csrf_token, user_util, backend):
132 def test_update_repo_name_via_settings(self, csrf_token, user_util, backend):
132 repo = user_util.create_repo(repo_type=backend.alias)
133 repo = user_util.create_repo(repo_type=backend.alias)
133 repo_name = repo.repo_name
134 repo_name = repo.repo_name
134
135
135 repo_group = user_util.create_repo_group()
136 repo_group = user_util.create_repo_group()
136 repo_group_name = repo_group.group_name
137 repo_group_name = repo_group.group_name
137 new_name = repo_group_name + '_' + repo_name
138 new_name = repo_group_name + '_' + repo_name
138
139
139 params = fixture._get_repo_create_params(
140 params = fixture._get_repo_create_params(
140 csrf_token=csrf_token,
141 csrf_token=csrf_token,
141 repo_name=new_name,
142 repo_name=new_name,
142 repo_type=backend.alias,
143 repo_type=backend.alias,
143 repo_owner=TEST_USER_ADMIN_LOGIN,
144 repo_owner=TEST_USER_ADMIN_LOGIN,
144 repo_description='DESC',
145 repo_description='DESC',
145 repo_private='false',
146 repo_private='false',
146 repo_enable_locking='false',
147 repo_enable_locking='false',
147 repo_enable_downloads='false')
148 repo_enable_downloads='false')
148 self.app.post(
149 self.app.post(
149 route_path('edit_repo', repo_name=repo_name),
150 route_path('edit_repo', repo_name=repo_name),
150 params=params, status=302)
151 params=params, status=302)
151 repo = Repository.get_by_repo_name(new_name)
152 repo = Repository.get_by_repo_name(new_name)
152 assert repo.repo_name == new_name
153 assert repo.repo_name == new_name
153
154
154 def test_update_repo_group_via_settings(self, csrf_token, user_util, backend):
155 def test_update_repo_group_via_settings(self, csrf_token, user_util, backend):
155 repo = user_util.create_repo(repo_type=backend.alias)
156 repo = user_util.create_repo(repo_type=backend.alias)
156 repo_name = repo.repo_name
157 repo_name = repo.repo_name
157
158
158 repo_group = user_util.create_repo_group()
159 repo_group = user_util.create_repo_group()
159 repo_group_name = repo_group.group_name
160 repo_group_name = repo_group.group_name
160 repo_group_id = repo_group.group_id
161 repo_group_id = repo_group.group_id
161
162
162 new_name = repo_group_name + '/' + repo_name
163 new_name = repo_group_name + '/' + repo_name
163 params = fixture._get_repo_create_params(
164 params = fixture._get_repo_create_params(
164 csrf_token=csrf_token,
165 csrf_token=csrf_token,
165 repo_name=repo_name,
166 repo_name=repo_name,
166 repo_type=backend.alias,
167 repo_type=backend.alias,
167 repo_owner=TEST_USER_ADMIN_LOGIN,
168 repo_owner=TEST_USER_ADMIN_LOGIN,
168 repo_description='DESC',
169 repo_description='DESC',
169 repo_group=repo_group_id,
170 repo_group=repo_group_id,
170 repo_private='false',
171 repo_private='false',
171 repo_enable_locking='false',
172 repo_enable_locking='false',
172 repo_enable_downloads='false')
173 repo_enable_downloads='false')
173 self.app.post(
174 self.app.post(
174 route_path('edit_repo', repo_name=repo_name),
175 route_path('edit_repo', repo_name=repo_name),
175 params=params, status=302)
176 params=params, status=302)
176 repo = Repository.get_by_repo_name(new_name)
177 repo = Repository.get_by_repo_name(new_name)
177 assert repo.repo_name == new_name
178 assert repo.repo_name == new_name
178
179
179 def test_set_private_flag_sets_default_user_permissions_to_none(
180 def test_set_private_flag_sets_default_user_permissions_to_none(
180 self, autologin_user, backend, csrf_token):
181 self, autologin_user, backend, csrf_token):
181
182
182 # initially repository perm should be read
183 # initially repository perm should be read
183 perm = _get_permission_for_user(user='default', repo=backend.repo_name)
184 perm = _get_permission_for_user(user='default', repo=backend.repo_name)
184 assert len(perm) == 1
185 assert len(perm) == 1
185 assert perm[0].permission.permission_name == 'repository.read'
186 assert perm[0].permission.permission_name == 'repository.read'
186 assert not backend.repo.private
187 assert not backend.repo.private
187
188
188 response = self.app.post(
189 response = self.app.post(
189 route_path('edit_repo', repo_name=backend.repo_name),
190 route_path('edit_repo', repo_name=backend.repo_name),
190 params=fixture._get_repo_create_params(
191 params=fixture._get_repo_create_params(
191 repo_private='true',
192 repo_private='true',
192 repo_name=backend.repo_name,
193 repo_name=backend.repo_name,
193 repo_type=backend.alias,
194 repo_type=backend.alias,
194 repo_owner=TEST_USER_ADMIN_LOGIN,
195 repo_owner=TEST_USER_ADMIN_LOGIN,
195 csrf_token=csrf_token), status=302)
196 csrf_token=csrf_token), status=302)
196
197
197 assert_session_flash(
198 assert_session_flash(
198 response,
199 response,
199 msg='Repository %s updated successfully' % (backend.repo_name))
200 msg='Repository %s updated successfully' % (backend.repo_name))
200
201
201 repo = Repository.get_by_repo_name(backend.repo_name)
202 repo = Repository.get_by_repo_name(backend.repo_name)
202 assert repo.private is True
203 assert repo.private is True
203
204
204 # now the repo default permission should be None
205 # now the repo default permission should be None
205 perm = _get_permission_for_user(user='default', repo=backend.repo_name)
206 perm = _get_permission_for_user(user='default', repo=backend.repo_name)
206 assert len(perm) == 1
207 assert len(perm) == 1
207 assert perm[0].permission.permission_name == 'repository.none'
208 assert perm[0].permission.permission_name == 'repository.none'
208
209
209 response = self.app.post(
210 response = self.app.post(
210 route_path('edit_repo', repo_name=backend.repo_name),
211 route_path('edit_repo', repo_name=backend.repo_name),
211 params=fixture._get_repo_create_params(
212 params=fixture._get_repo_create_params(
212 repo_private='false',
213 repo_private='false',
213 repo_name=backend.repo_name,
214 repo_name=backend.repo_name,
214 repo_type=backend.alias,
215 repo_type=backend.alias,
215 repo_owner=TEST_USER_ADMIN_LOGIN,
216 repo_owner=TEST_USER_ADMIN_LOGIN,
216 csrf_token=csrf_token), status=302)
217 csrf_token=csrf_token), status=302)
217
218
218 assert_session_flash(
219 assert_session_flash(
219 response,
220 response,
220 msg='Repository %s updated successfully' % (backend.repo_name))
221 msg='Repository %s updated successfully' % (backend.repo_name))
221 assert backend.repo.private is False
222 assert backend.repo.private is False
222
223
223 # 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
224 perm = _get_permission_for_user(user='default', repo=backend.repo_name)
225 perm = _get_permission_for_user(user='default', repo=backend.repo_name)
225 assert len(perm) == 1
226 assert len(perm) == 1
226 assert perm[0].permission.permission_name == 'repository.none'
227 assert perm[0].permission.permission_name == 'repository.none'
227
228
228 # update this permission back
229 # update this permission back
229 perm[0].permission = Permission.get_by_key('repository.read')
230 perm[0].permission = Permission.get_by_key('repository.read')
230 Session().add(perm[0])
231 Session().add(perm[0])
231 Session().commit()
232 Session().commit()
@@ -1,1098 +1,1088 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 Routes configuration
22 Routes configuration
23
23
24 The more specific and detailed routes should be defined first so they
24 The more specific and detailed routes should be defined first so they
25 may take precedent over the more generic routes. For more information
25 may take precedent over the more generic routes. For more information
26 refer to the routes manual at http://routes.groovie.org/docs/
26 refer to the routes manual at http://routes.groovie.org/docs/
27
27
28 IMPORTANT: if you change any routing here, make sure to take a look at lib/base.py
28 IMPORTANT: if you change any routing here, make sure to take a look at lib/base.py
29 and _route_name variable which uses some of stored naming here to do redirects.
29 and _route_name variable which uses some of stored naming here to do redirects.
30 """
30 """
31 import os
31 import os
32 import re
32 import re
33 from routes import Mapper
33 from routes import Mapper
34
34
35 # prefix for non repository related links needs to be prefixed with `/`
35 # prefix for non repository related links needs to be prefixed with `/`
36 ADMIN_PREFIX = '/_admin'
36 ADMIN_PREFIX = '/_admin'
37 STATIC_FILE_PREFIX = '/_static'
37 STATIC_FILE_PREFIX = '/_static'
38
38
39 # Default requirements for URL parts
39 # Default requirements for URL parts
40 URL_NAME_REQUIREMENTS = {
40 URL_NAME_REQUIREMENTS = {
41 # group name can have a slash in them, but they must not end with a slash
41 # group name can have a slash in them, but they must not end with a slash
42 'group_name': r'.*?[^/]',
42 'group_name': r'.*?[^/]',
43 'repo_group_name': r'.*?[^/]',
43 'repo_group_name': r'.*?[^/]',
44 # repo names can have a slash in them, but they must not end with a slash
44 # repo names can have a slash in them, but they must not end with a slash
45 'repo_name': r'.*?[^/]',
45 'repo_name': r'.*?[^/]',
46 # file path eats up everything at the end
46 # file path eats up everything at the end
47 'f_path': r'.*',
47 'f_path': r'.*',
48 # reference types
48 # reference types
49 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
49 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
50 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
50 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
51 }
51 }
52
52
53
53
54 def add_route_requirements(route_path, requirements):
54 def add_route_requirements(route_path, requirements):
55 """
55 """
56 Adds regex requirements to pyramid routes using a mapping dict
56 Adds regex requirements to pyramid routes using a mapping dict
57
57
58 >>> add_route_requirements('/{action}/{id}', {'id': r'\d+'})
58 >>> add_route_requirements('/{action}/{id}', {'id': r'\d+'})
59 '/{action}/{id:\d+}'
59 '/{action}/{id:\d+}'
60
60
61 """
61 """
62 for key, regex in requirements.items():
62 for key, regex in requirements.items():
63 route_path = route_path.replace('{%s}' % key, '{%s:%s}' % (key, regex))
63 route_path = route_path.replace('{%s}' % key, '{%s:%s}' % (key, regex))
64 return route_path
64 return route_path
65
65
66
66
67 class JSRoutesMapper(Mapper):
67 class JSRoutesMapper(Mapper):
68 """
68 """
69 Wrapper for routes.Mapper to make pyroutes compatible url definitions
69 Wrapper for routes.Mapper to make pyroutes compatible url definitions
70 """
70 """
71 _named_route_regex = re.compile(r'^[a-z-_0-9A-Z]+$')
71 _named_route_regex = re.compile(r'^[a-z-_0-9A-Z]+$')
72 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
72 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
73 def __init__(self, *args, **kw):
73 def __init__(self, *args, **kw):
74 super(JSRoutesMapper, self).__init__(*args, **kw)
74 super(JSRoutesMapper, self).__init__(*args, **kw)
75 self._jsroutes = []
75 self._jsroutes = []
76
76
77 def connect(self, *args, **kw):
77 def connect(self, *args, **kw):
78 """
78 """
79 Wrapper for connect to take an extra argument jsroute=True
79 Wrapper for connect to take an extra argument jsroute=True
80
80
81 :param jsroute: boolean, if True will add the route to the pyroutes list
81 :param jsroute: boolean, if True will add the route to the pyroutes list
82 """
82 """
83 if kw.pop('jsroute', False):
83 if kw.pop('jsroute', False):
84 if not self._named_route_regex.match(args[0]):
84 if not self._named_route_regex.match(args[0]):
85 raise Exception('only named routes can be added to pyroutes')
85 raise Exception('only named routes can be added to pyroutes')
86 self._jsroutes.append(args[0])
86 self._jsroutes.append(args[0])
87
87
88 super(JSRoutesMapper, self).connect(*args, **kw)
88 super(JSRoutesMapper, self).connect(*args, **kw)
89
89
90 def _extract_route_information(self, route):
90 def _extract_route_information(self, route):
91 """
91 """
92 Convert a route into tuple(name, path, args), eg:
92 Convert a route into tuple(name, path, args), eg:
93 ('show_user', '/profile/%(username)s', ['username'])
93 ('show_user', '/profile/%(username)s', ['username'])
94 """
94 """
95 routepath = route.routepath
95 routepath = route.routepath
96 def replace(matchobj):
96 def replace(matchobj):
97 if matchobj.group(1):
97 if matchobj.group(1):
98 return "%%(%s)s" % matchobj.group(1).split(':')[0]
98 return "%%(%s)s" % matchobj.group(1).split(':')[0]
99 else:
99 else:
100 return "%%(%s)s" % matchobj.group(2)
100 return "%%(%s)s" % matchobj.group(2)
101
101
102 routepath = self._argument_prog.sub(replace, routepath)
102 routepath = self._argument_prog.sub(replace, routepath)
103 return (
103 return (
104 route.name,
104 route.name,
105 routepath,
105 routepath,
106 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
106 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
107 for arg in self._argument_prog.findall(route.routepath)]
107 for arg in self._argument_prog.findall(route.routepath)]
108 )
108 )
109
109
110 def jsroutes(self):
110 def jsroutes(self):
111 """
111 """
112 Return a list of pyroutes.js compatible routes
112 Return a list of pyroutes.js compatible routes
113 """
113 """
114 for route_name in self._jsroutes:
114 for route_name in self._jsroutes:
115 yield self._extract_route_information(self._routenames[route_name])
115 yield self._extract_route_information(self._routenames[route_name])
116
116
117
117
118 def make_map(config):
118 def make_map(config):
119 """Create, configure and return the routes Mapper"""
119 """Create, configure and return the routes Mapper"""
120 rmap = JSRoutesMapper(directory=config['pylons.paths']['controllers'],
120 rmap = JSRoutesMapper(directory=config['pylons.paths']['controllers'],
121 always_scan=config['debug'])
121 always_scan=config['debug'])
122 rmap.minimization = False
122 rmap.minimization = False
123 rmap.explicit = False
123 rmap.explicit = False
124
124
125 from rhodecode.lib.utils2 import str2bool
125 from rhodecode.lib.utils2 import str2bool
126 from rhodecode.model import repo, repo_group
126 from rhodecode.model import repo, repo_group
127
127
128 def check_repo(environ, match_dict):
128 def check_repo(environ, match_dict):
129 """
129 """
130 check for valid repository for proper 404 handling
130 check for valid repository for proper 404 handling
131
131
132 :param environ:
132 :param environ:
133 :param match_dict:
133 :param match_dict:
134 """
134 """
135 repo_name = match_dict.get('repo_name')
135 repo_name = match_dict.get('repo_name')
136
136
137 if match_dict.get('f_path'):
137 if match_dict.get('f_path'):
138 # fix for multiple initial slashes that causes errors
138 # fix for multiple initial slashes that causes errors
139 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
139 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
140 repo_model = repo.RepoModel()
140 repo_model = repo.RepoModel()
141 by_name_match = repo_model.get_by_repo_name(repo_name)
141 by_name_match = repo_model.get_by_repo_name(repo_name)
142 # if we match quickly from database, short circuit the operation,
142 # if we match quickly from database, short circuit the operation,
143 # and validate repo based on the type.
143 # and validate repo based on the type.
144 if by_name_match:
144 if by_name_match:
145 return True
145 return True
146
146
147 by_id_match = repo_model.get_repo_by_id(repo_name)
147 by_id_match = repo_model.get_repo_by_id(repo_name)
148 if by_id_match:
148 if by_id_match:
149 repo_name = by_id_match.repo_name
149 repo_name = by_id_match.repo_name
150 match_dict['repo_name'] = repo_name
150 match_dict['repo_name'] = repo_name
151 return True
151 return True
152
152
153 return False
153 return False
154
154
155 def check_group(environ, match_dict):
155 def check_group(environ, match_dict):
156 """
156 """
157 check for valid repository group path for proper 404 handling
157 check for valid repository group path for proper 404 handling
158
158
159 :param environ:
159 :param environ:
160 :param match_dict:
160 :param match_dict:
161 """
161 """
162 repo_group_name = match_dict.get('group_name')
162 repo_group_name = match_dict.get('group_name')
163 repo_group_model = repo_group.RepoGroupModel()
163 repo_group_model = repo_group.RepoGroupModel()
164 by_name_match = repo_group_model.get_by_group_name(repo_group_name)
164 by_name_match = repo_group_model.get_by_group_name(repo_group_name)
165 if by_name_match:
165 if by_name_match:
166 return True
166 return True
167
167
168 return False
168 return False
169
169
170 def check_user_group(environ, match_dict):
170 def check_user_group(environ, match_dict):
171 """
171 """
172 check for valid user group for proper 404 handling
172 check for valid user group for proper 404 handling
173
173
174 :param environ:
174 :param environ:
175 :param match_dict:
175 :param match_dict:
176 """
176 """
177 return True
177 return True
178
178
179 def check_int(environ, match_dict):
179 def check_int(environ, match_dict):
180 return match_dict.get('id').isdigit()
180 return match_dict.get('id').isdigit()
181
181
182
182
183 #==========================================================================
183 #==========================================================================
184 # CUSTOM ROUTES HERE
184 # CUSTOM ROUTES HERE
185 #==========================================================================
185 #==========================================================================
186
186
187 # MAIN PAGE
187 # MAIN PAGE
188 rmap.connect('home', '/', controller='home', action='index', jsroute=True)
188 rmap.connect('home', '/', controller='home', action='index', jsroute=True)
189
189
190 # ping and pylons error test
190 # ping and pylons error test
191 rmap.connect('ping', '%s/ping' % (ADMIN_PREFIX,), controller='home', action='ping')
191 rmap.connect('ping', '%s/ping' % (ADMIN_PREFIX,), controller='home', action='ping')
192 rmap.connect('error_test', '%s/error_test' % (ADMIN_PREFIX,), controller='home', action='error_test')
192 rmap.connect('error_test', '%s/error_test' % (ADMIN_PREFIX,), controller='home', action='error_test')
193
193
194 # ADMIN REPOSITORY ROUTES
194 # ADMIN REPOSITORY ROUTES
195 with rmap.submapper(path_prefix=ADMIN_PREFIX,
195 with rmap.submapper(path_prefix=ADMIN_PREFIX,
196 controller='admin/repos') as m:
196 controller='admin/repos') as m:
197 m.connect('repos', '/repos',
197 m.connect('repos', '/repos',
198 action='create', conditions={'method': ['POST']})
198 action='create', conditions={'method': ['POST']})
199 m.connect('repos', '/repos',
199 m.connect('repos', '/repos',
200 action='index', conditions={'method': ['GET']})
200 action='index', conditions={'method': ['GET']})
201 m.connect('new_repo', '/create_repository', jsroute=True,
201 m.connect('new_repo', '/create_repository', jsroute=True,
202 action='create_repository', conditions={'method': ['GET']})
202 action='create_repository', conditions={'method': ['GET']})
203 m.connect('delete_repo', '/repos/{repo_name}',
203 m.connect('delete_repo', '/repos/{repo_name}',
204 action='delete', conditions={'method': ['DELETE']},
204 action='delete', conditions={'method': ['DELETE']},
205 requirements=URL_NAME_REQUIREMENTS)
205 requirements=URL_NAME_REQUIREMENTS)
206 m.connect('repo', '/repos/{repo_name}',
206 m.connect('repo', '/repos/{repo_name}',
207 action='show', conditions={'method': ['GET'],
207 action='show', conditions={'method': ['GET'],
208 'function': check_repo},
208 'function': check_repo},
209 requirements=URL_NAME_REQUIREMENTS)
209 requirements=URL_NAME_REQUIREMENTS)
210
210
211 # ADMIN REPOSITORY GROUPS ROUTES
211 # ADMIN REPOSITORY GROUPS ROUTES
212 with rmap.submapper(path_prefix=ADMIN_PREFIX,
212 with rmap.submapper(path_prefix=ADMIN_PREFIX,
213 controller='admin/repo_groups') as m:
213 controller='admin/repo_groups') as m:
214 m.connect('repo_groups', '/repo_groups',
214 m.connect('repo_groups', '/repo_groups',
215 action='create', conditions={'method': ['POST']})
215 action='create', conditions={'method': ['POST']})
216 m.connect('repo_groups', '/repo_groups',
216 m.connect('repo_groups', '/repo_groups',
217 action='index', conditions={'method': ['GET']})
217 action='index', conditions={'method': ['GET']})
218 m.connect('new_repo_group', '/repo_groups/new',
218 m.connect('new_repo_group', '/repo_groups/new',
219 action='new', conditions={'method': ['GET']})
219 action='new', conditions={'method': ['GET']})
220 m.connect('update_repo_group', '/repo_groups/{group_name}',
220 m.connect('update_repo_group', '/repo_groups/{group_name}',
221 action='update', conditions={'method': ['PUT'],
221 action='update', conditions={'method': ['PUT'],
222 'function': check_group},
222 'function': check_group},
223 requirements=URL_NAME_REQUIREMENTS)
223 requirements=URL_NAME_REQUIREMENTS)
224
224
225 # EXTRAS REPO GROUP ROUTES
225 # EXTRAS REPO GROUP ROUTES
226 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
226 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
227 action='edit',
227 action='edit',
228 conditions={'method': ['GET'], 'function': check_group},
228 conditions={'method': ['GET'], 'function': check_group},
229 requirements=URL_NAME_REQUIREMENTS)
229 requirements=URL_NAME_REQUIREMENTS)
230 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
230 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
231 action='edit',
231 action='edit',
232 conditions={'method': ['PUT'], 'function': check_group},
232 conditions={'method': ['PUT'], 'function': check_group},
233 requirements=URL_NAME_REQUIREMENTS)
233 requirements=URL_NAME_REQUIREMENTS)
234
234
235 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
235 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
236 action='edit_repo_group_advanced',
236 action='edit_repo_group_advanced',
237 conditions={'method': ['GET'], 'function': check_group},
237 conditions={'method': ['GET'], 'function': check_group},
238 requirements=URL_NAME_REQUIREMENTS)
238 requirements=URL_NAME_REQUIREMENTS)
239 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
239 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
240 action='edit_repo_group_advanced',
240 action='edit_repo_group_advanced',
241 conditions={'method': ['PUT'], 'function': check_group},
241 conditions={'method': ['PUT'], 'function': check_group},
242 requirements=URL_NAME_REQUIREMENTS)
242 requirements=URL_NAME_REQUIREMENTS)
243
243
244 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
244 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
245 action='edit_repo_group_perms',
245 action='edit_repo_group_perms',
246 conditions={'method': ['GET'], 'function': check_group},
246 conditions={'method': ['GET'], 'function': check_group},
247 requirements=URL_NAME_REQUIREMENTS)
247 requirements=URL_NAME_REQUIREMENTS)
248 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
248 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
249 action='update_perms',
249 action='update_perms',
250 conditions={'method': ['PUT'], 'function': check_group},
250 conditions={'method': ['PUT'], 'function': check_group},
251 requirements=URL_NAME_REQUIREMENTS)
251 requirements=URL_NAME_REQUIREMENTS)
252
252
253 m.connect('delete_repo_group', '/repo_groups/{group_name}',
253 m.connect('delete_repo_group', '/repo_groups/{group_name}',
254 action='delete', conditions={'method': ['DELETE'],
254 action='delete', conditions={'method': ['DELETE'],
255 'function': check_group},
255 'function': check_group},
256 requirements=URL_NAME_REQUIREMENTS)
256 requirements=URL_NAME_REQUIREMENTS)
257
257
258 # ADMIN USER ROUTES
258 # ADMIN USER ROUTES
259 with rmap.submapper(path_prefix=ADMIN_PREFIX,
259 with rmap.submapper(path_prefix=ADMIN_PREFIX,
260 controller='admin/users') as m:
260 controller='admin/users') as m:
261 m.connect('users', '/users',
261 m.connect('users', '/users',
262 action='create', conditions={'method': ['POST']})
262 action='create', conditions={'method': ['POST']})
263 m.connect('new_user', '/users/new',
263 m.connect('new_user', '/users/new',
264 action='new', conditions={'method': ['GET']})
264 action='new', conditions={'method': ['GET']})
265 m.connect('update_user', '/users/{user_id}',
265 m.connect('update_user', '/users/{user_id}',
266 action='update', conditions={'method': ['PUT']})
266 action='update', conditions={'method': ['PUT']})
267 m.connect('delete_user', '/users/{user_id}',
267 m.connect('delete_user', '/users/{user_id}',
268 action='delete', conditions={'method': ['DELETE']})
268 action='delete', conditions={'method': ['DELETE']})
269 m.connect('edit_user', '/users/{user_id}/edit',
269 m.connect('edit_user', '/users/{user_id}/edit',
270 action='edit', conditions={'method': ['GET']}, jsroute=True)
270 action='edit', conditions={'method': ['GET']}, jsroute=True)
271 m.connect('user', '/users/{user_id}',
271 m.connect('user', '/users/{user_id}',
272 action='show', conditions={'method': ['GET']})
272 action='show', conditions={'method': ['GET']})
273 m.connect('force_password_reset_user', '/users/{user_id}/password_reset',
273 m.connect('force_password_reset_user', '/users/{user_id}/password_reset',
274 action='reset_password', conditions={'method': ['POST']})
274 action='reset_password', conditions={'method': ['POST']})
275 m.connect('create_personal_repo_group', '/users/{user_id}/create_repo_group',
275 m.connect('create_personal_repo_group', '/users/{user_id}/create_repo_group',
276 action='create_personal_repo_group', conditions={'method': ['POST']})
276 action='create_personal_repo_group', conditions={'method': ['POST']})
277
277
278 # EXTRAS USER ROUTES
278 # EXTRAS USER ROUTES
279 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
279 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
280 action='edit_advanced', conditions={'method': ['GET']})
280 action='edit_advanced', conditions={'method': ['GET']})
281 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
281 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
282 action='update_advanced', conditions={'method': ['PUT']})
282 action='update_advanced', conditions={'method': ['PUT']})
283
283
284 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
284 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
285 action='edit_global_perms', conditions={'method': ['GET']})
285 action='edit_global_perms', conditions={'method': ['GET']})
286 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
286 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
287 action='update_global_perms', conditions={'method': ['PUT']})
287 action='update_global_perms', conditions={'method': ['PUT']})
288
288
289 m.connect('edit_user_perms_summary', '/users/{user_id}/edit/permissions_summary',
289 m.connect('edit_user_perms_summary', '/users/{user_id}/edit/permissions_summary',
290 action='edit_perms_summary', conditions={'method': ['GET']})
290 action='edit_perms_summary', conditions={'method': ['GET']})
291
291
292 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
292 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
293 action='edit_emails', conditions={'method': ['GET']})
293 action='edit_emails', conditions={'method': ['GET']})
294 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
294 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
295 action='add_email', conditions={'method': ['PUT']})
295 action='add_email', conditions={'method': ['PUT']})
296 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
296 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
297 action='delete_email', conditions={'method': ['DELETE']})
297 action='delete_email', conditions={'method': ['DELETE']})
298
298
299 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
299 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
300 action='edit_ips', conditions={'method': ['GET']})
300 action='edit_ips', conditions={'method': ['GET']})
301 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
301 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
302 action='add_ip', conditions={'method': ['PUT']})
302 action='add_ip', conditions={'method': ['PUT']})
303 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
303 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
304 action='delete_ip', conditions={'method': ['DELETE']})
304 action='delete_ip', conditions={'method': ['DELETE']})
305
305
306 # ADMIN USER GROUPS REST ROUTES
306 # ADMIN USER GROUPS REST ROUTES
307 with rmap.submapper(path_prefix=ADMIN_PREFIX,
307 with rmap.submapper(path_prefix=ADMIN_PREFIX,
308 controller='admin/user_groups') as m:
308 controller='admin/user_groups') as m:
309 m.connect('users_groups', '/user_groups',
309 m.connect('users_groups', '/user_groups',
310 action='create', conditions={'method': ['POST']})
310 action='create', conditions={'method': ['POST']})
311 m.connect('users_groups', '/user_groups',
311 m.connect('users_groups', '/user_groups',
312 action='index', conditions={'method': ['GET']})
312 action='index', conditions={'method': ['GET']})
313 m.connect('new_users_group', '/user_groups/new',
313 m.connect('new_users_group', '/user_groups/new',
314 action='new', conditions={'method': ['GET']})
314 action='new', conditions={'method': ['GET']})
315 m.connect('update_users_group', '/user_groups/{user_group_id}',
315 m.connect('update_users_group', '/user_groups/{user_group_id}',
316 action='update', conditions={'method': ['PUT']})
316 action='update', conditions={'method': ['PUT']})
317 m.connect('delete_users_group', '/user_groups/{user_group_id}',
317 m.connect('delete_users_group', '/user_groups/{user_group_id}',
318 action='delete', conditions={'method': ['DELETE']})
318 action='delete', conditions={'method': ['DELETE']})
319 m.connect('edit_users_group', '/user_groups/{user_group_id}/edit',
319 m.connect('edit_users_group', '/user_groups/{user_group_id}/edit',
320 action='edit', conditions={'method': ['GET']},
320 action='edit', conditions={'method': ['GET']},
321 function=check_user_group)
321 function=check_user_group)
322
322
323 # EXTRAS USER GROUP ROUTES
323 # EXTRAS USER GROUP ROUTES
324 m.connect('edit_user_group_global_perms',
324 m.connect('edit_user_group_global_perms',
325 '/user_groups/{user_group_id}/edit/global_permissions',
325 '/user_groups/{user_group_id}/edit/global_permissions',
326 action='edit_global_perms', conditions={'method': ['GET']})
326 action='edit_global_perms', conditions={'method': ['GET']})
327 m.connect('edit_user_group_global_perms',
327 m.connect('edit_user_group_global_perms',
328 '/user_groups/{user_group_id}/edit/global_permissions',
328 '/user_groups/{user_group_id}/edit/global_permissions',
329 action='update_global_perms', conditions={'method': ['PUT']})
329 action='update_global_perms', conditions={'method': ['PUT']})
330 m.connect('edit_user_group_perms_summary',
330 m.connect('edit_user_group_perms_summary',
331 '/user_groups/{user_group_id}/edit/permissions_summary',
331 '/user_groups/{user_group_id}/edit/permissions_summary',
332 action='edit_perms_summary', conditions={'method': ['GET']})
332 action='edit_perms_summary', conditions={'method': ['GET']})
333
333
334 m.connect('edit_user_group_perms',
334 m.connect('edit_user_group_perms',
335 '/user_groups/{user_group_id}/edit/permissions',
335 '/user_groups/{user_group_id}/edit/permissions',
336 action='edit_perms', conditions={'method': ['GET']})
336 action='edit_perms', conditions={'method': ['GET']})
337 m.connect('edit_user_group_perms',
337 m.connect('edit_user_group_perms',
338 '/user_groups/{user_group_id}/edit/permissions',
338 '/user_groups/{user_group_id}/edit/permissions',
339 action='update_perms', conditions={'method': ['PUT']})
339 action='update_perms', conditions={'method': ['PUT']})
340
340
341 m.connect('edit_user_group_advanced',
341 m.connect('edit_user_group_advanced',
342 '/user_groups/{user_group_id}/edit/advanced',
342 '/user_groups/{user_group_id}/edit/advanced',
343 action='edit_advanced', conditions={'method': ['GET']})
343 action='edit_advanced', conditions={'method': ['GET']})
344
344
345 m.connect('edit_user_group_advanced_sync',
345 m.connect('edit_user_group_advanced_sync',
346 '/user_groups/{user_group_id}/edit/advanced/sync',
346 '/user_groups/{user_group_id}/edit/advanced/sync',
347 action='edit_advanced_set_synchronization', conditions={'method': ['POST']})
347 action='edit_advanced_set_synchronization', conditions={'method': ['POST']})
348
348
349 m.connect('edit_user_group_members',
349 m.connect('edit_user_group_members',
350 '/user_groups/{user_group_id}/edit/members', jsroute=True,
350 '/user_groups/{user_group_id}/edit/members', jsroute=True,
351 action='user_group_members', conditions={'method': ['GET']})
351 action='user_group_members', conditions={'method': ['GET']})
352
352
353 # ADMIN PERMISSIONS ROUTES
353 # ADMIN PERMISSIONS ROUTES
354 with rmap.submapper(path_prefix=ADMIN_PREFIX,
354 with rmap.submapper(path_prefix=ADMIN_PREFIX,
355 controller='admin/permissions') as m:
355 controller='admin/permissions') as m:
356 m.connect('admin_permissions_application', '/permissions/application',
356 m.connect('admin_permissions_application', '/permissions/application',
357 action='permission_application_update', conditions={'method': ['POST']})
357 action='permission_application_update', conditions={'method': ['POST']})
358 m.connect('admin_permissions_application', '/permissions/application',
358 m.connect('admin_permissions_application', '/permissions/application',
359 action='permission_application', conditions={'method': ['GET']})
359 action='permission_application', conditions={'method': ['GET']})
360
360
361 m.connect('admin_permissions_global', '/permissions/global',
361 m.connect('admin_permissions_global', '/permissions/global',
362 action='permission_global_update', conditions={'method': ['POST']})
362 action='permission_global_update', conditions={'method': ['POST']})
363 m.connect('admin_permissions_global', '/permissions/global',
363 m.connect('admin_permissions_global', '/permissions/global',
364 action='permission_global', conditions={'method': ['GET']})
364 action='permission_global', conditions={'method': ['GET']})
365
365
366 m.connect('admin_permissions_object', '/permissions/object',
366 m.connect('admin_permissions_object', '/permissions/object',
367 action='permission_objects_update', conditions={'method': ['POST']})
367 action='permission_objects_update', conditions={'method': ['POST']})
368 m.connect('admin_permissions_object', '/permissions/object',
368 m.connect('admin_permissions_object', '/permissions/object',
369 action='permission_objects', conditions={'method': ['GET']})
369 action='permission_objects', conditions={'method': ['GET']})
370
370
371 m.connect('admin_permissions_ips', '/permissions/ips',
371 m.connect('admin_permissions_ips', '/permissions/ips',
372 action='permission_ips', conditions={'method': ['POST']})
372 action='permission_ips', conditions={'method': ['POST']})
373 m.connect('admin_permissions_ips', '/permissions/ips',
373 m.connect('admin_permissions_ips', '/permissions/ips',
374 action='permission_ips', conditions={'method': ['GET']})
374 action='permission_ips', conditions={'method': ['GET']})
375
375
376 m.connect('admin_permissions_overview', '/permissions/overview',
376 m.connect('admin_permissions_overview', '/permissions/overview',
377 action='permission_perms', conditions={'method': ['GET']})
377 action='permission_perms', conditions={'method': ['GET']})
378
378
379 # ADMIN DEFAULTS REST ROUTES
379 # ADMIN DEFAULTS REST ROUTES
380 with rmap.submapper(path_prefix=ADMIN_PREFIX,
380 with rmap.submapper(path_prefix=ADMIN_PREFIX,
381 controller='admin/defaults') as m:
381 controller='admin/defaults') as m:
382 m.connect('admin_defaults_repositories', '/defaults/repositories',
382 m.connect('admin_defaults_repositories', '/defaults/repositories',
383 action='update_repository_defaults', conditions={'method': ['POST']})
383 action='update_repository_defaults', conditions={'method': ['POST']})
384 m.connect('admin_defaults_repositories', '/defaults/repositories',
384 m.connect('admin_defaults_repositories', '/defaults/repositories',
385 action='index', conditions={'method': ['GET']})
385 action='index', conditions={'method': ['GET']})
386
386
387 # ADMIN DEBUG STYLE ROUTES
387 # ADMIN DEBUG STYLE ROUTES
388 if str2bool(config.get('debug_style')):
388 if str2bool(config.get('debug_style')):
389 with rmap.submapper(path_prefix=ADMIN_PREFIX + '/debug_style',
389 with rmap.submapper(path_prefix=ADMIN_PREFIX + '/debug_style',
390 controller='debug_style') as m:
390 controller='debug_style') as m:
391 m.connect('debug_style_home', '',
391 m.connect('debug_style_home', '',
392 action='index', conditions={'method': ['GET']})
392 action='index', conditions={'method': ['GET']})
393 m.connect('debug_style_template', '/t/{t_path}',
393 m.connect('debug_style_template', '/t/{t_path}',
394 action='template', conditions={'method': ['GET']})
394 action='template', conditions={'method': ['GET']})
395
395
396 # ADMIN SETTINGS ROUTES
396 # ADMIN SETTINGS ROUTES
397 with rmap.submapper(path_prefix=ADMIN_PREFIX,
397 with rmap.submapper(path_prefix=ADMIN_PREFIX,
398 controller='admin/settings') as m:
398 controller='admin/settings') as m:
399
399
400 # default
400 # default
401 m.connect('admin_settings', '/settings',
401 m.connect('admin_settings', '/settings',
402 action='settings_global_update',
402 action='settings_global_update',
403 conditions={'method': ['POST']})
403 conditions={'method': ['POST']})
404 m.connect('admin_settings', '/settings',
404 m.connect('admin_settings', '/settings',
405 action='settings_global', conditions={'method': ['GET']})
405 action='settings_global', conditions={'method': ['GET']})
406
406
407 m.connect('admin_settings_vcs', '/settings/vcs',
407 m.connect('admin_settings_vcs', '/settings/vcs',
408 action='settings_vcs_update',
408 action='settings_vcs_update',
409 conditions={'method': ['POST']})
409 conditions={'method': ['POST']})
410 m.connect('admin_settings_vcs', '/settings/vcs',
410 m.connect('admin_settings_vcs', '/settings/vcs',
411 action='settings_vcs',
411 action='settings_vcs',
412 conditions={'method': ['GET']})
412 conditions={'method': ['GET']})
413 m.connect('admin_settings_vcs', '/settings/vcs',
413 m.connect('admin_settings_vcs', '/settings/vcs',
414 action='delete_svn_pattern',
414 action='delete_svn_pattern',
415 conditions={'method': ['DELETE']})
415 conditions={'method': ['DELETE']})
416
416
417 m.connect('admin_settings_mapping', '/settings/mapping',
417 m.connect('admin_settings_mapping', '/settings/mapping',
418 action='settings_mapping_update',
418 action='settings_mapping_update',
419 conditions={'method': ['POST']})
419 conditions={'method': ['POST']})
420 m.connect('admin_settings_mapping', '/settings/mapping',
420 m.connect('admin_settings_mapping', '/settings/mapping',
421 action='settings_mapping', conditions={'method': ['GET']})
421 action='settings_mapping', conditions={'method': ['GET']})
422
422
423 m.connect('admin_settings_global', '/settings/global',
423 m.connect('admin_settings_global', '/settings/global',
424 action='settings_global_update',
424 action='settings_global_update',
425 conditions={'method': ['POST']})
425 conditions={'method': ['POST']})
426 m.connect('admin_settings_global', '/settings/global',
426 m.connect('admin_settings_global', '/settings/global',
427 action='settings_global', conditions={'method': ['GET']})
427 action='settings_global', conditions={'method': ['GET']})
428
428
429 m.connect('admin_settings_visual', '/settings/visual',
429 m.connect('admin_settings_visual', '/settings/visual',
430 action='settings_visual_update',
430 action='settings_visual_update',
431 conditions={'method': ['POST']})
431 conditions={'method': ['POST']})
432 m.connect('admin_settings_visual', '/settings/visual',
432 m.connect('admin_settings_visual', '/settings/visual',
433 action='settings_visual', conditions={'method': ['GET']})
433 action='settings_visual', conditions={'method': ['GET']})
434
434
435 m.connect('admin_settings_issuetracker',
435 m.connect('admin_settings_issuetracker',
436 '/settings/issue-tracker', action='settings_issuetracker',
436 '/settings/issue-tracker', action='settings_issuetracker',
437 conditions={'method': ['GET']})
437 conditions={'method': ['GET']})
438 m.connect('admin_settings_issuetracker_save',
438 m.connect('admin_settings_issuetracker_save',
439 '/settings/issue-tracker/save',
439 '/settings/issue-tracker/save',
440 action='settings_issuetracker_save',
440 action='settings_issuetracker_save',
441 conditions={'method': ['POST']})
441 conditions={'method': ['POST']})
442 m.connect('admin_issuetracker_test', '/settings/issue-tracker/test',
442 m.connect('admin_issuetracker_test', '/settings/issue-tracker/test',
443 action='settings_issuetracker_test',
443 action='settings_issuetracker_test',
444 conditions={'method': ['POST']})
444 conditions={'method': ['POST']})
445 m.connect('admin_issuetracker_delete',
445 m.connect('admin_issuetracker_delete',
446 '/settings/issue-tracker/delete',
446 '/settings/issue-tracker/delete',
447 action='settings_issuetracker_delete',
447 action='settings_issuetracker_delete',
448 conditions={'method': ['DELETE']})
448 conditions={'method': ['DELETE']})
449
449
450 m.connect('admin_settings_email', '/settings/email',
450 m.connect('admin_settings_email', '/settings/email',
451 action='settings_email_update',
451 action='settings_email_update',
452 conditions={'method': ['POST']})
452 conditions={'method': ['POST']})
453 m.connect('admin_settings_email', '/settings/email',
453 m.connect('admin_settings_email', '/settings/email',
454 action='settings_email', conditions={'method': ['GET']})
454 action='settings_email', conditions={'method': ['GET']})
455
455
456 m.connect('admin_settings_hooks', '/settings/hooks',
456 m.connect('admin_settings_hooks', '/settings/hooks',
457 action='settings_hooks_update',
457 action='settings_hooks_update',
458 conditions={'method': ['POST', 'DELETE']})
458 conditions={'method': ['POST', 'DELETE']})
459 m.connect('admin_settings_hooks', '/settings/hooks',
459 m.connect('admin_settings_hooks', '/settings/hooks',
460 action='settings_hooks', conditions={'method': ['GET']})
460 action='settings_hooks', conditions={'method': ['GET']})
461
461
462 m.connect('admin_settings_search', '/settings/search',
462 m.connect('admin_settings_search', '/settings/search',
463 action='settings_search', conditions={'method': ['GET']})
463 action='settings_search', conditions={'method': ['GET']})
464
464
465 m.connect('admin_settings_supervisor', '/settings/supervisor',
465 m.connect('admin_settings_supervisor', '/settings/supervisor',
466 action='settings_supervisor', conditions={'method': ['GET']})
466 action='settings_supervisor', conditions={'method': ['GET']})
467 m.connect('admin_settings_supervisor_log', '/settings/supervisor/{procid}/log',
467 m.connect('admin_settings_supervisor_log', '/settings/supervisor/{procid}/log',
468 action='settings_supervisor_log', conditions={'method': ['GET']})
468 action='settings_supervisor_log', conditions={'method': ['GET']})
469
469
470 m.connect('admin_settings_labs', '/settings/labs',
470 m.connect('admin_settings_labs', '/settings/labs',
471 action='settings_labs_update',
471 action='settings_labs_update',
472 conditions={'method': ['POST']})
472 conditions={'method': ['POST']})
473 m.connect('admin_settings_labs', '/settings/labs',
473 m.connect('admin_settings_labs', '/settings/labs',
474 action='settings_labs', conditions={'method': ['GET']})
474 action='settings_labs', conditions={'method': ['GET']})
475
475
476 # ADMIN MY ACCOUNT
476 # ADMIN MY ACCOUNT
477 with rmap.submapper(path_prefix=ADMIN_PREFIX,
477 with rmap.submapper(path_prefix=ADMIN_PREFIX,
478 controller='admin/my_account') as m:
478 controller='admin/my_account') as m:
479
479
480 m.connect('my_account_edit', '/my_account/edit',
480 m.connect('my_account_edit', '/my_account/edit',
481 action='my_account_edit', conditions={'method': ['GET']})
481 action='my_account_edit', conditions={'method': ['GET']})
482 m.connect('my_account', '/my_account/update',
482 m.connect('my_account', '/my_account/update',
483 action='my_account_update', conditions={'method': ['POST']})
483 action='my_account_update', conditions={'method': ['POST']})
484
484
485 # NOTE(marcink): this needs to be kept for password force flag to be
485 # NOTE(marcink): this needs to be kept for password force flag to be
486 # handler, remove after migration to pyramid
486 # handler, remove after migration to pyramid
487 m.connect('my_account_password', '/my_account/password',
487 m.connect('my_account_password', '/my_account/password',
488 action='my_account_password', conditions={'method': ['GET']})
488 action='my_account_password', conditions={'method': ['GET']})
489
489
490 m.connect('my_account_repos', '/my_account/repos',
490 m.connect('my_account_repos', '/my_account/repos',
491 action='my_account_repos', conditions={'method': ['GET']})
491 action='my_account_repos', conditions={'method': ['GET']})
492
492
493 m.connect('my_account_watched', '/my_account/watched',
493 m.connect('my_account_watched', '/my_account/watched',
494 action='my_account_watched', conditions={'method': ['GET']})
494 action='my_account_watched', conditions={'method': ['GET']})
495
495
496 m.connect('my_account_pullrequests', '/my_account/pull_requests',
496 m.connect('my_account_pullrequests', '/my_account/pull_requests',
497 action='my_account_pullrequests', conditions={'method': ['GET']})
497 action='my_account_pullrequests', conditions={'method': ['GET']})
498
498
499 m.connect('my_account_perms', '/my_account/perms',
499 m.connect('my_account_perms', '/my_account/perms',
500 action='my_account_perms', conditions={'method': ['GET']})
500 action='my_account_perms', conditions={'method': ['GET']})
501
501
502 m.connect('my_account_emails', '/my_account/emails',
502 m.connect('my_account_emails', '/my_account/emails',
503 action='my_account_emails', conditions={'method': ['GET']})
503 action='my_account_emails', conditions={'method': ['GET']})
504 m.connect('my_account_emails', '/my_account/emails',
504 m.connect('my_account_emails', '/my_account/emails',
505 action='my_account_emails_add', conditions={'method': ['POST']})
505 action='my_account_emails_add', conditions={'method': ['POST']})
506 m.connect('my_account_emails', '/my_account/emails',
506 m.connect('my_account_emails', '/my_account/emails',
507 action='my_account_emails_delete', conditions={'method': ['DELETE']})
507 action='my_account_emails_delete', conditions={'method': ['DELETE']})
508
508
509 m.connect('my_account_notifications', '/my_account/notifications',
509 m.connect('my_account_notifications', '/my_account/notifications',
510 action='my_notifications',
510 action='my_notifications',
511 conditions={'method': ['GET']})
511 conditions={'method': ['GET']})
512 m.connect('my_account_notifications_toggle_visibility',
512 m.connect('my_account_notifications_toggle_visibility',
513 '/my_account/toggle_visibility',
513 '/my_account/toggle_visibility',
514 action='my_notifications_toggle_visibility',
514 action='my_notifications_toggle_visibility',
515 conditions={'method': ['POST']})
515 conditions={'method': ['POST']})
516 m.connect('my_account_notifications_test_channelstream',
516 m.connect('my_account_notifications_test_channelstream',
517 '/my_account/test_channelstream',
517 '/my_account/test_channelstream',
518 action='my_account_notifications_test_channelstream',
518 action='my_account_notifications_test_channelstream',
519 conditions={'method': ['POST']})
519 conditions={'method': ['POST']})
520
520
521 # NOTIFICATION REST ROUTES
521 # NOTIFICATION REST ROUTES
522 with rmap.submapper(path_prefix=ADMIN_PREFIX,
522 with rmap.submapper(path_prefix=ADMIN_PREFIX,
523 controller='admin/notifications') as m:
523 controller='admin/notifications') as m:
524 m.connect('notifications', '/notifications',
524 m.connect('notifications', '/notifications',
525 action='index', conditions={'method': ['GET']})
525 action='index', conditions={'method': ['GET']})
526 m.connect('notifications_mark_all_read', '/notifications/mark_all_read',
526 m.connect('notifications_mark_all_read', '/notifications/mark_all_read',
527 action='mark_all_read', conditions={'method': ['POST']})
527 action='mark_all_read', conditions={'method': ['POST']})
528 m.connect('/notifications/{notification_id}',
528 m.connect('/notifications/{notification_id}',
529 action='update', conditions={'method': ['PUT']})
529 action='update', conditions={'method': ['PUT']})
530 m.connect('/notifications/{notification_id}',
530 m.connect('/notifications/{notification_id}',
531 action='delete', conditions={'method': ['DELETE']})
531 action='delete', conditions={'method': ['DELETE']})
532 m.connect('notification', '/notifications/{notification_id}',
532 m.connect('notification', '/notifications/{notification_id}',
533 action='show', conditions={'method': ['GET']})
533 action='show', conditions={'method': ['GET']})
534
534
535 # ADMIN GIST
535 # ADMIN GIST
536 with rmap.submapper(path_prefix=ADMIN_PREFIX,
536 with rmap.submapper(path_prefix=ADMIN_PREFIX,
537 controller='admin/gists') as m:
537 controller='admin/gists') as m:
538 m.connect('gists', '/gists',
538 m.connect('gists', '/gists',
539 action='create', conditions={'method': ['POST']})
539 action='create', conditions={'method': ['POST']})
540 m.connect('gists', '/gists', jsroute=True,
540 m.connect('gists', '/gists', jsroute=True,
541 action='index', conditions={'method': ['GET']})
541 action='index', conditions={'method': ['GET']})
542 m.connect('new_gist', '/gists/new', jsroute=True,
542 m.connect('new_gist', '/gists/new', jsroute=True,
543 action='new', conditions={'method': ['GET']})
543 action='new', conditions={'method': ['GET']})
544
544
545 m.connect('/gists/{gist_id}',
545 m.connect('/gists/{gist_id}',
546 action='delete', conditions={'method': ['DELETE']})
546 action='delete', conditions={'method': ['DELETE']})
547 m.connect('edit_gist', '/gists/{gist_id}/edit',
547 m.connect('edit_gist', '/gists/{gist_id}/edit',
548 action='edit_form', conditions={'method': ['GET']})
548 action='edit_form', conditions={'method': ['GET']})
549 m.connect('edit_gist', '/gists/{gist_id}/edit',
549 m.connect('edit_gist', '/gists/{gist_id}/edit',
550 action='edit', conditions={'method': ['POST']})
550 action='edit', conditions={'method': ['POST']})
551 m.connect(
551 m.connect(
552 'edit_gist_check_revision', '/gists/{gist_id}/edit/check_revision',
552 'edit_gist_check_revision', '/gists/{gist_id}/edit/check_revision',
553 action='check_revision', conditions={'method': ['GET']})
553 action='check_revision', conditions={'method': ['GET']})
554
554
555 m.connect('gist', '/gists/{gist_id}',
555 m.connect('gist', '/gists/{gist_id}',
556 action='show', conditions={'method': ['GET']})
556 action='show', conditions={'method': ['GET']})
557 m.connect('gist_rev', '/gists/{gist_id}/{revision}',
557 m.connect('gist_rev', '/gists/{gist_id}/{revision}',
558 revision='tip',
558 revision='tip',
559 action='show', conditions={'method': ['GET']})
559 action='show', conditions={'method': ['GET']})
560 m.connect('formatted_gist', '/gists/{gist_id}/{revision}/{format}',
560 m.connect('formatted_gist', '/gists/{gist_id}/{revision}/{format}',
561 revision='tip',
561 revision='tip',
562 action='show', conditions={'method': ['GET']})
562 action='show', conditions={'method': ['GET']})
563 m.connect('formatted_gist_file', '/gists/{gist_id}/{revision}/{format}/{f_path}',
563 m.connect('formatted_gist_file', '/gists/{gist_id}/{revision}/{format}/{f_path}',
564 revision='tip',
564 revision='tip',
565 action='show', conditions={'method': ['GET']},
565 action='show', conditions={'method': ['GET']},
566 requirements=URL_NAME_REQUIREMENTS)
566 requirements=URL_NAME_REQUIREMENTS)
567
567
568 # ADMIN MAIN PAGES
568 # ADMIN MAIN PAGES
569 with rmap.submapper(path_prefix=ADMIN_PREFIX,
569 with rmap.submapper(path_prefix=ADMIN_PREFIX,
570 controller='admin/admin') as m:
570 controller='admin/admin') as m:
571 m.connect('admin_home', '', action='index')
571 m.connect('admin_home', '', action='index')
572 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
572 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
573 action='add_repo')
573 action='add_repo')
574 m.connect(
574 m.connect(
575 'pull_requests_global_0', '/pull_requests/{pull_request_id:[0-9]+}',
575 'pull_requests_global_0', '/pull_requests/{pull_request_id:[0-9]+}',
576 action='pull_requests')
576 action='pull_requests')
577 m.connect(
577 m.connect(
578 'pull_requests_global_1', '/pull-requests/{pull_request_id:[0-9]+}',
578 'pull_requests_global_1', '/pull-requests/{pull_request_id:[0-9]+}',
579 action='pull_requests')
579 action='pull_requests')
580 m.connect(
580 m.connect(
581 'pull_requests_global', '/pull-request/{pull_request_id:[0-9]+}',
581 'pull_requests_global', '/pull-request/{pull_request_id:[0-9]+}',
582 action='pull_requests')
582 action='pull_requests')
583
583
584 # USER JOURNAL
584 # USER JOURNAL
585 rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,),
585 rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,),
586 controller='journal', action='index')
586 controller='journal', action='index')
587 rmap.connect('journal_rss', '%s/journal/rss' % (ADMIN_PREFIX,),
587 rmap.connect('journal_rss', '%s/journal/rss' % (ADMIN_PREFIX,),
588 controller='journal', action='journal_rss')
588 controller='journal', action='journal_rss')
589 rmap.connect('journal_atom', '%s/journal/atom' % (ADMIN_PREFIX,),
589 rmap.connect('journal_atom', '%s/journal/atom' % (ADMIN_PREFIX,),
590 controller='journal', action='journal_atom')
590 controller='journal', action='journal_atom')
591
591
592 rmap.connect('public_journal', '%s/public_journal' % (ADMIN_PREFIX,),
592 rmap.connect('public_journal', '%s/public_journal' % (ADMIN_PREFIX,),
593 controller='journal', action='public_journal')
593 controller='journal', action='public_journal')
594
594
595 rmap.connect('public_journal_rss', '%s/public_journal/rss' % (ADMIN_PREFIX,),
595 rmap.connect('public_journal_rss', '%s/public_journal/rss' % (ADMIN_PREFIX,),
596 controller='journal', action='public_journal_rss')
596 controller='journal', action='public_journal_rss')
597
597
598 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % (ADMIN_PREFIX,),
598 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % (ADMIN_PREFIX,),
599 controller='journal', action='public_journal_rss')
599 controller='journal', action='public_journal_rss')
600
600
601 rmap.connect('public_journal_atom',
601 rmap.connect('public_journal_atom',
602 '%s/public_journal/atom' % (ADMIN_PREFIX,), controller='journal',
602 '%s/public_journal/atom' % (ADMIN_PREFIX,), controller='journal',
603 action='public_journal_atom')
603 action='public_journal_atom')
604
604
605 rmap.connect('public_journal_atom_old',
605 rmap.connect('public_journal_atom_old',
606 '%s/public_journal_atom' % (ADMIN_PREFIX,), controller='journal',
606 '%s/public_journal_atom' % (ADMIN_PREFIX,), controller='journal',
607 action='public_journal_atom')
607 action='public_journal_atom')
608
608
609 rmap.connect('toggle_following', '%s/toggle_following' % (ADMIN_PREFIX,),
609 rmap.connect('toggle_following', '%s/toggle_following' % (ADMIN_PREFIX,),
610 controller='journal', action='toggle_following', jsroute=True,
610 controller='journal', action='toggle_following', jsroute=True,
611 conditions={'method': ['POST']})
611 conditions={'method': ['POST']})
612
612
613 # FEEDS
613 # FEEDS
614 rmap.connect('rss_feed_home', '/{repo_name}/feed/rss',
614 rmap.connect('rss_feed_home', '/{repo_name}/feed/rss',
615 controller='feed', action='rss',
615 controller='feed', action='rss',
616 conditions={'function': check_repo},
616 conditions={'function': check_repo},
617 requirements=URL_NAME_REQUIREMENTS)
617 requirements=URL_NAME_REQUIREMENTS)
618
618
619 rmap.connect('atom_feed_home', '/{repo_name}/feed/atom',
619 rmap.connect('atom_feed_home', '/{repo_name}/feed/atom',
620 controller='feed', action='atom',
620 controller='feed', action='atom',
621 conditions={'function': check_repo},
621 conditions={'function': check_repo},
622 requirements=URL_NAME_REQUIREMENTS)
622 requirements=URL_NAME_REQUIREMENTS)
623
623
624 #==========================================================================
624 #==========================================================================
625 # REPOSITORY ROUTES
625 # REPOSITORY ROUTES
626 #==========================================================================
626 #==========================================================================
627
627
628 rmap.connect('repo_creating_home', '/{repo_name}/repo_creating',
628 rmap.connect('repo_creating_home', '/{repo_name}/repo_creating',
629 controller='admin/repos', action='repo_creating',
629 controller='admin/repos', action='repo_creating',
630 requirements=URL_NAME_REQUIREMENTS)
630 requirements=URL_NAME_REQUIREMENTS)
631 rmap.connect('repo_check_home', '/{repo_name}/crepo_check',
631 rmap.connect('repo_check_home', '/{repo_name}/crepo_check',
632 controller='admin/repos', action='repo_check',
632 controller='admin/repos', action='repo_check',
633 requirements=URL_NAME_REQUIREMENTS)
633 requirements=URL_NAME_REQUIREMENTS)
634
634
635 rmap.connect('repo_stats', '/{repo_name}/repo_stats/{commit_id}',
635 rmap.connect('repo_stats', '/{repo_name}/repo_stats/{commit_id}',
636 controller='summary', action='repo_stats',
636 controller='summary', action='repo_stats',
637 conditions={'function': check_repo},
637 conditions={'function': check_repo},
638 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
638 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
639
639
640 rmap.connect('repo_refs_data', '/{repo_name}/refs-data',
640 rmap.connect('repo_refs_data', '/{repo_name}/refs-data',
641 controller='summary', action='repo_refs_data',
641 controller='summary', action='repo_refs_data',
642 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
642 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
643 rmap.connect('repo_refs_changelog_data', '/{repo_name}/refs-data-changelog',
643 rmap.connect('repo_refs_changelog_data', '/{repo_name}/refs-data-changelog',
644 controller='summary', action='repo_refs_changelog_data',
644 controller='summary', action='repo_refs_changelog_data',
645 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
645 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
646 rmap.connect('repo_default_reviewers_data', '/{repo_name}/default-reviewers',
646 rmap.connect('repo_default_reviewers_data', '/{repo_name}/default-reviewers',
647 controller='summary', action='repo_default_reviewers_data',
647 controller='summary', action='repo_default_reviewers_data',
648 jsroute=True, requirements=URL_NAME_REQUIREMENTS)
648 jsroute=True, requirements=URL_NAME_REQUIREMENTS)
649
649
650 rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}',
650 rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}',
651 controller='changeset', revision='tip',
651 controller='changeset', revision='tip',
652 conditions={'function': check_repo},
652 conditions={'function': check_repo},
653 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
653 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
654 rmap.connect('changeset_children', '/{repo_name}/changeset_children/{revision}',
654 rmap.connect('changeset_children', '/{repo_name}/changeset_children/{revision}',
655 controller='changeset', revision='tip', action='changeset_children',
655 controller='changeset', revision='tip', action='changeset_children',
656 conditions={'function': check_repo},
656 conditions={'function': check_repo},
657 requirements=URL_NAME_REQUIREMENTS)
657 requirements=URL_NAME_REQUIREMENTS)
658 rmap.connect('changeset_parents', '/{repo_name}/changeset_parents/{revision}',
658 rmap.connect('changeset_parents', '/{repo_name}/changeset_parents/{revision}',
659 controller='changeset', revision='tip', action='changeset_parents',
659 controller='changeset', revision='tip', action='changeset_parents',
660 conditions={'function': check_repo},
660 conditions={'function': check_repo},
661 requirements=URL_NAME_REQUIREMENTS)
661 requirements=URL_NAME_REQUIREMENTS)
662
662
663 # repo edit options
663 # repo edit options
664 rmap.connect('edit_repo_perms', '/{repo_name}/settings/permissions',
665 jsroute=True,
666 controller='admin/repos', action='edit_permissions',
667 conditions={'method': ['GET'], 'function': check_repo},
668 requirements=URL_NAME_REQUIREMENTS)
669 rmap.connect('edit_repo_perms_update', '/{repo_name}/settings/permissions',
670 controller='admin/repos', action='edit_permissions_update',
671 conditions={'method': ['PUT'], 'function': check_repo},
672 requirements=URL_NAME_REQUIREMENTS)
673
674 rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields',
664 rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields',
675 controller='admin/repos', action='edit_fields',
665 controller='admin/repos', action='edit_fields',
676 conditions={'method': ['GET'], 'function': check_repo},
666 conditions={'method': ['GET'], 'function': check_repo},
677 requirements=URL_NAME_REQUIREMENTS)
667 requirements=URL_NAME_REQUIREMENTS)
678 rmap.connect('create_repo_fields', '/{repo_name}/settings/fields/new',
668 rmap.connect('create_repo_fields', '/{repo_name}/settings/fields/new',
679 controller='admin/repos', action='create_repo_field',
669 controller='admin/repos', action='create_repo_field',
680 conditions={'method': ['PUT'], 'function': check_repo},
670 conditions={'method': ['PUT'], 'function': check_repo},
681 requirements=URL_NAME_REQUIREMENTS)
671 requirements=URL_NAME_REQUIREMENTS)
682 rmap.connect('delete_repo_fields', '/{repo_name}/settings/fields/{field_id}',
672 rmap.connect('delete_repo_fields', '/{repo_name}/settings/fields/{field_id}',
683 controller='admin/repos', action='delete_repo_field',
673 controller='admin/repos', action='delete_repo_field',
684 conditions={'method': ['DELETE'], 'function': check_repo},
674 conditions={'method': ['DELETE'], 'function': check_repo},
685 requirements=URL_NAME_REQUIREMENTS)
675 requirements=URL_NAME_REQUIREMENTS)
686
676
687 rmap.connect('edit_repo_advanced', '/{repo_name}/settings/advanced',
677 rmap.connect('edit_repo_advanced', '/{repo_name}/settings/advanced',
688 controller='admin/repos', action='edit_advanced',
678 controller='admin/repos', action='edit_advanced',
689 conditions={'method': ['GET'], 'function': check_repo},
679 conditions={'method': ['GET'], 'function': check_repo},
690 requirements=URL_NAME_REQUIREMENTS)
680 requirements=URL_NAME_REQUIREMENTS)
691
681
692 rmap.connect('edit_repo_advanced_locking', '/{repo_name}/settings/advanced/locking',
682 rmap.connect('edit_repo_advanced_locking', '/{repo_name}/settings/advanced/locking',
693 controller='admin/repos', action='edit_advanced_locking',
683 controller='admin/repos', action='edit_advanced_locking',
694 conditions={'method': ['PUT'], 'function': check_repo},
684 conditions={'method': ['PUT'], 'function': check_repo},
695 requirements=URL_NAME_REQUIREMENTS)
685 requirements=URL_NAME_REQUIREMENTS)
696 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
686 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
697 controller='admin/repos', action='toggle_locking',
687 controller='admin/repos', action='toggle_locking',
698 conditions={'method': ['GET'], 'function': check_repo},
688 conditions={'method': ['GET'], 'function': check_repo},
699 requirements=URL_NAME_REQUIREMENTS)
689 requirements=URL_NAME_REQUIREMENTS)
700
690
701 rmap.connect('edit_repo_advanced_journal', '/{repo_name}/settings/advanced/journal',
691 rmap.connect('edit_repo_advanced_journal', '/{repo_name}/settings/advanced/journal',
702 controller='admin/repos', action='edit_advanced_journal',
692 controller='admin/repos', action='edit_advanced_journal',
703 conditions={'method': ['PUT'], 'function': check_repo},
693 conditions={'method': ['PUT'], 'function': check_repo},
704 requirements=URL_NAME_REQUIREMENTS)
694 requirements=URL_NAME_REQUIREMENTS)
705
695
706 rmap.connect('edit_repo_advanced_fork', '/{repo_name}/settings/advanced/fork',
696 rmap.connect('edit_repo_advanced_fork', '/{repo_name}/settings/advanced/fork',
707 controller='admin/repos', action='edit_advanced_fork',
697 controller='admin/repos', action='edit_advanced_fork',
708 conditions={'method': ['PUT'], 'function': check_repo},
698 conditions={'method': ['PUT'], 'function': check_repo},
709 requirements=URL_NAME_REQUIREMENTS)
699 requirements=URL_NAME_REQUIREMENTS)
710
700
711 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
701 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
712 controller='admin/repos', action='edit_remote_form',
702 controller='admin/repos', action='edit_remote_form',
713 conditions={'method': ['GET'], 'function': check_repo},
703 conditions={'method': ['GET'], 'function': check_repo},
714 requirements=URL_NAME_REQUIREMENTS)
704 requirements=URL_NAME_REQUIREMENTS)
715 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
705 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
716 controller='admin/repos', action='edit_remote',
706 controller='admin/repos', action='edit_remote',
717 conditions={'method': ['PUT'], 'function': check_repo},
707 conditions={'method': ['PUT'], 'function': check_repo},
718 requirements=URL_NAME_REQUIREMENTS)
708 requirements=URL_NAME_REQUIREMENTS)
719
709
720 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
710 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
721 controller='admin/repos', action='edit_statistics_form',
711 controller='admin/repos', action='edit_statistics_form',
722 conditions={'method': ['GET'], 'function': check_repo},
712 conditions={'method': ['GET'], 'function': check_repo},
723 requirements=URL_NAME_REQUIREMENTS)
713 requirements=URL_NAME_REQUIREMENTS)
724 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
714 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
725 controller='admin/repos', action='edit_statistics',
715 controller='admin/repos', action='edit_statistics',
726 conditions={'method': ['PUT'], 'function': check_repo},
716 conditions={'method': ['PUT'], 'function': check_repo},
727 requirements=URL_NAME_REQUIREMENTS)
717 requirements=URL_NAME_REQUIREMENTS)
728 rmap.connect('repo_settings_issuetracker',
718 rmap.connect('repo_settings_issuetracker',
729 '/{repo_name}/settings/issue-tracker',
719 '/{repo_name}/settings/issue-tracker',
730 controller='admin/repos', action='repo_issuetracker',
720 controller='admin/repos', action='repo_issuetracker',
731 conditions={'method': ['GET'], 'function': check_repo},
721 conditions={'method': ['GET'], 'function': check_repo},
732 requirements=URL_NAME_REQUIREMENTS)
722 requirements=URL_NAME_REQUIREMENTS)
733 rmap.connect('repo_issuetracker_test',
723 rmap.connect('repo_issuetracker_test',
734 '/{repo_name}/settings/issue-tracker/test',
724 '/{repo_name}/settings/issue-tracker/test',
735 controller='admin/repos', action='repo_issuetracker_test',
725 controller='admin/repos', action='repo_issuetracker_test',
736 conditions={'method': ['POST'], 'function': check_repo},
726 conditions={'method': ['POST'], 'function': check_repo},
737 requirements=URL_NAME_REQUIREMENTS)
727 requirements=URL_NAME_REQUIREMENTS)
738 rmap.connect('repo_issuetracker_delete',
728 rmap.connect('repo_issuetracker_delete',
739 '/{repo_name}/settings/issue-tracker/delete',
729 '/{repo_name}/settings/issue-tracker/delete',
740 controller='admin/repos', action='repo_issuetracker_delete',
730 controller='admin/repos', action='repo_issuetracker_delete',
741 conditions={'method': ['DELETE'], 'function': check_repo},
731 conditions={'method': ['DELETE'], 'function': check_repo},
742 requirements=URL_NAME_REQUIREMENTS)
732 requirements=URL_NAME_REQUIREMENTS)
743 rmap.connect('repo_issuetracker_save',
733 rmap.connect('repo_issuetracker_save',
744 '/{repo_name}/settings/issue-tracker/save',
734 '/{repo_name}/settings/issue-tracker/save',
745 controller='admin/repos', action='repo_issuetracker_save',
735 controller='admin/repos', action='repo_issuetracker_save',
746 conditions={'method': ['POST'], 'function': check_repo},
736 conditions={'method': ['POST'], 'function': check_repo},
747 requirements=URL_NAME_REQUIREMENTS)
737 requirements=URL_NAME_REQUIREMENTS)
748 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
738 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
749 controller='admin/repos', action='repo_settings_vcs_update',
739 controller='admin/repos', action='repo_settings_vcs_update',
750 conditions={'method': ['POST'], 'function': check_repo},
740 conditions={'method': ['POST'], 'function': check_repo},
751 requirements=URL_NAME_REQUIREMENTS)
741 requirements=URL_NAME_REQUIREMENTS)
752 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
742 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
753 controller='admin/repos', action='repo_settings_vcs',
743 controller='admin/repos', action='repo_settings_vcs',
754 conditions={'method': ['GET'], 'function': check_repo},
744 conditions={'method': ['GET'], 'function': check_repo},
755 requirements=URL_NAME_REQUIREMENTS)
745 requirements=URL_NAME_REQUIREMENTS)
756 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
746 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
757 controller='admin/repos', action='repo_delete_svn_pattern',
747 controller='admin/repos', action='repo_delete_svn_pattern',
758 conditions={'method': ['DELETE'], 'function': check_repo},
748 conditions={'method': ['DELETE'], 'function': check_repo},
759 requirements=URL_NAME_REQUIREMENTS)
749 requirements=URL_NAME_REQUIREMENTS)
760 rmap.connect('repo_pullrequest_settings', '/{repo_name}/settings/pullrequest',
750 rmap.connect('repo_pullrequest_settings', '/{repo_name}/settings/pullrequest',
761 controller='admin/repos', action='repo_settings_pullrequest',
751 controller='admin/repos', action='repo_settings_pullrequest',
762 conditions={'method': ['GET', 'POST'], 'function': check_repo},
752 conditions={'method': ['GET', 'POST'], 'function': check_repo},
763 requirements=URL_NAME_REQUIREMENTS)
753 requirements=URL_NAME_REQUIREMENTS)
764
754
765 # still working url for backward compat.
755 # still working url for backward compat.
766 rmap.connect('raw_changeset_home_depraced',
756 rmap.connect('raw_changeset_home_depraced',
767 '/{repo_name}/raw-changeset/{revision}',
757 '/{repo_name}/raw-changeset/{revision}',
768 controller='changeset', action='changeset_raw',
758 controller='changeset', action='changeset_raw',
769 revision='tip', conditions={'function': check_repo},
759 revision='tip', conditions={'function': check_repo},
770 requirements=URL_NAME_REQUIREMENTS)
760 requirements=URL_NAME_REQUIREMENTS)
771
761
772 # new URLs
762 # new URLs
773 rmap.connect('changeset_raw_home',
763 rmap.connect('changeset_raw_home',
774 '/{repo_name}/changeset-diff/{revision}',
764 '/{repo_name}/changeset-diff/{revision}',
775 controller='changeset', action='changeset_raw',
765 controller='changeset', action='changeset_raw',
776 revision='tip', conditions={'function': check_repo},
766 revision='tip', conditions={'function': check_repo},
777 requirements=URL_NAME_REQUIREMENTS)
767 requirements=URL_NAME_REQUIREMENTS)
778
768
779 rmap.connect('changeset_patch_home',
769 rmap.connect('changeset_patch_home',
780 '/{repo_name}/changeset-patch/{revision}',
770 '/{repo_name}/changeset-patch/{revision}',
781 controller='changeset', action='changeset_patch',
771 controller='changeset', action='changeset_patch',
782 revision='tip', conditions={'function': check_repo},
772 revision='tip', conditions={'function': check_repo},
783 requirements=URL_NAME_REQUIREMENTS)
773 requirements=URL_NAME_REQUIREMENTS)
784
774
785 rmap.connect('changeset_download_home',
775 rmap.connect('changeset_download_home',
786 '/{repo_name}/changeset-download/{revision}',
776 '/{repo_name}/changeset-download/{revision}',
787 controller='changeset', action='changeset_download',
777 controller='changeset', action='changeset_download',
788 revision='tip', conditions={'function': check_repo},
778 revision='tip', conditions={'function': check_repo},
789 requirements=URL_NAME_REQUIREMENTS)
779 requirements=URL_NAME_REQUIREMENTS)
790
780
791 rmap.connect('changeset_comment',
781 rmap.connect('changeset_comment',
792 '/{repo_name}/changeset/{revision}/comment', jsroute=True,
782 '/{repo_name}/changeset/{revision}/comment', jsroute=True,
793 controller='changeset', revision='tip', action='comment',
783 controller='changeset', revision='tip', action='comment',
794 conditions={'function': check_repo},
784 conditions={'function': check_repo},
795 requirements=URL_NAME_REQUIREMENTS)
785 requirements=URL_NAME_REQUIREMENTS)
796
786
797 rmap.connect('changeset_comment_preview',
787 rmap.connect('changeset_comment_preview',
798 '/{repo_name}/changeset/comment/preview', jsroute=True,
788 '/{repo_name}/changeset/comment/preview', jsroute=True,
799 controller='changeset', action='preview_comment',
789 controller='changeset', action='preview_comment',
800 conditions={'function': check_repo, 'method': ['POST']},
790 conditions={'function': check_repo, 'method': ['POST']},
801 requirements=URL_NAME_REQUIREMENTS)
791 requirements=URL_NAME_REQUIREMENTS)
802
792
803 rmap.connect('changeset_comment_delete',
793 rmap.connect('changeset_comment_delete',
804 '/{repo_name}/changeset/comment/{comment_id}/delete',
794 '/{repo_name}/changeset/comment/{comment_id}/delete',
805 controller='changeset', action='delete_comment',
795 controller='changeset', action='delete_comment',
806 conditions={'function': check_repo, 'method': ['DELETE']},
796 conditions={'function': check_repo, 'method': ['DELETE']},
807 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
797 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
808
798
809 rmap.connect('changeset_info', '/{repo_name}/changeset_info/{revision}',
799 rmap.connect('changeset_info', '/{repo_name}/changeset_info/{revision}',
810 controller='changeset', action='changeset_info',
800 controller='changeset', action='changeset_info',
811 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
801 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
812
802
813 rmap.connect('compare_home',
803 rmap.connect('compare_home',
814 '/{repo_name}/compare',
804 '/{repo_name}/compare',
815 controller='compare', action='index',
805 controller='compare', action='index',
816 conditions={'function': check_repo},
806 conditions={'function': check_repo},
817 requirements=URL_NAME_REQUIREMENTS)
807 requirements=URL_NAME_REQUIREMENTS)
818
808
819 rmap.connect('compare_url',
809 rmap.connect('compare_url',
820 '/{repo_name}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}',
810 '/{repo_name}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}',
821 controller='compare', action='compare',
811 controller='compare', action='compare',
822 conditions={'function': check_repo},
812 conditions={'function': check_repo},
823 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
813 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
824
814
825 rmap.connect('pullrequest_home',
815 rmap.connect('pullrequest_home',
826 '/{repo_name}/pull-request/new', controller='pullrequests',
816 '/{repo_name}/pull-request/new', controller='pullrequests',
827 action='index', conditions={'function': check_repo,
817 action='index', conditions={'function': check_repo,
828 'method': ['GET']},
818 'method': ['GET']},
829 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
819 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
830
820
831 rmap.connect('pullrequest',
821 rmap.connect('pullrequest',
832 '/{repo_name}/pull-request/new', controller='pullrequests',
822 '/{repo_name}/pull-request/new', controller='pullrequests',
833 action='create', conditions={'function': check_repo,
823 action='create', conditions={'function': check_repo,
834 'method': ['POST']},
824 'method': ['POST']},
835 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
825 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
836
826
837 rmap.connect('pullrequest_repo_refs',
827 rmap.connect('pullrequest_repo_refs',
838 '/{repo_name}/pull-request/refs/{target_repo_name:.*?[^/]}',
828 '/{repo_name}/pull-request/refs/{target_repo_name:.*?[^/]}',
839 controller='pullrequests',
829 controller='pullrequests',
840 action='get_repo_refs',
830 action='get_repo_refs',
841 conditions={'function': check_repo, 'method': ['GET']},
831 conditions={'function': check_repo, 'method': ['GET']},
842 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
832 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
843
833
844 rmap.connect('pullrequest_repo_destinations',
834 rmap.connect('pullrequest_repo_destinations',
845 '/{repo_name}/pull-request/repo-destinations',
835 '/{repo_name}/pull-request/repo-destinations',
846 controller='pullrequests',
836 controller='pullrequests',
847 action='get_repo_destinations',
837 action='get_repo_destinations',
848 conditions={'function': check_repo, 'method': ['GET']},
838 conditions={'function': check_repo, 'method': ['GET']},
849 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
839 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
850
840
851 rmap.connect('pullrequest_show',
841 rmap.connect('pullrequest_show',
852 '/{repo_name}/pull-request/{pull_request_id}',
842 '/{repo_name}/pull-request/{pull_request_id}',
853 controller='pullrequests',
843 controller='pullrequests',
854 action='show', conditions={'function': check_repo,
844 action='show', conditions={'function': check_repo,
855 'method': ['GET']},
845 'method': ['GET']},
856 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
846 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
857
847
858 rmap.connect('pullrequest_update',
848 rmap.connect('pullrequest_update',
859 '/{repo_name}/pull-request/{pull_request_id}',
849 '/{repo_name}/pull-request/{pull_request_id}',
860 controller='pullrequests',
850 controller='pullrequests',
861 action='update', conditions={'function': check_repo,
851 action='update', conditions={'function': check_repo,
862 'method': ['PUT']},
852 'method': ['PUT']},
863 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
853 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
864
854
865 rmap.connect('pullrequest_merge',
855 rmap.connect('pullrequest_merge',
866 '/{repo_name}/pull-request/{pull_request_id}',
856 '/{repo_name}/pull-request/{pull_request_id}',
867 controller='pullrequests',
857 controller='pullrequests',
868 action='merge', conditions={'function': check_repo,
858 action='merge', conditions={'function': check_repo,
869 'method': ['POST']},
859 'method': ['POST']},
870 requirements=URL_NAME_REQUIREMENTS)
860 requirements=URL_NAME_REQUIREMENTS)
871
861
872 rmap.connect('pullrequest_delete',
862 rmap.connect('pullrequest_delete',
873 '/{repo_name}/pull-request/{pull_request_id}',
863 '/{repo_name}/pull-request/{pull_request_id}',
874 controller='pullrequests',
864 controller='pullrequests',
875 action='delete', conditions={'function': check_repo,
865 action='delete', conditions={'function': check_repo,
876 'method': ['DELETE']},
866 'method': ['DELETE']},
877 requirements=URL_NAME_REQUIREMENTS)
867 requirements=URL_NAME_REQUIREMENTS)
878
868
879 rmap.connect('pullrequest_show_all',
869 rmap.connect('pullrequest_show_all',
880 '/{repo_name}/pull-request',
870 '/{repo_name}/pull-request',
881 controller='pullrequests',
871 controller='pullrequests',
882 action='show_all', conditions={'function': check_repo,
872 action='show_all', conditions={'function': check_repo,
883 'method': ['GET']},
873 'method': ['GET']},
884 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
874 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
885
875
886 rmap.connect('pullrequest_comment',
876 rmap.connect('pullrequest_comment',
887 '/{repo_name}/pull-request-comment/{pull_request_id}',
877 '/{repo_name}/pull-request-comment/{pull_request_id}',
888 controller='pullrequests',
878 controller='pullrequests',
889 action='comment', conditions={'function': check_repo,
879 action='comment', conditions={'function': check_repo,
890 'method': ['POST']},
880 'method': ['POST']},
891 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
881 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
892
882
893 rmap.connect('pullrequest_comment_delete',
883 rmap.connect('pullrequest_comment_delete',
894 '/{repo_name}/pull-request-comment/{comment_id}/delete',
884 '/{repo_name}/pull-request-comment/{comment_id}/delete',
895 controller='pullrequests', action='delete_comment',
885 controller='pullrequests', action='delete_comment',
896 conditions={'function': check_repo, 'method': ['DELETE']},
886 conditions={'function': check_repo, 'method': ['DELETE']},
897 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
887 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
898
888
899 rmap.connect('summary_home_explicit', '/{repo_name}/summary',
889 rmap.connect('summary_home_explicit', '/{repo_name}/summary',
900 controller='summary', conditions={'function': check_repo},
890 controller='summary', conditions={'function': check_repo},
901 requirements=URL_NAME_REQUIREMENTS)
891 requirements=URL_NAME_REQUIREMENTS)
902
892
903 rmap.connect('branches_home', '/{repo_name}/branches',
893 rmap.connect('branches_home', '/{repo_name}/branches',
904 controller='branches', conditions={'function': check_repo},
894 controller='branches', conditions={'function': check_repo},
905 requirements=URL_NAME_REQUIREMENTS)
895 requirements=URL_NAME_REQUIREMENTS)
906
896
907 rmap.connect('tags_home', '/{repo_name}/tags',
897 rmap.connect('tags_home', '/{repo_name}/tags',
908 controller='tags', conditions={'function': check_repo},
898 controller='tags', conditions={'function': check_repo},
909 requirements=URL_NAME_REQUIREMENTS)
899 requirements=URL_NAME_REQUIREMENTS)
910
900
911 rmap.connect('bookmarks_home', '/{repo_name}/bookmarks',
901 rmap.connect('bookmarks_home', '/{repo_name}/bookmarks',
912 controller='bookmarks', conditions={'function': check_repo},
902 controller='bookmarks', conditions={'function': check_repo},
913 requirements=URL_NAME_REQUIREMENTS)
903 requirements=URL_NAME_REQUIREMENTS)
914
904
915 rmap.connect('changelog_home', '/{repo_name}/changelog', jsroute=True,
905 rmap.connect('changelog_home', '/{repo_name}/changelog', jsroute=True,
916 controller='changelog', conditions={'function': check_repo},
906 controller='changelog', conditions={'function': check_repo},
917 requirements=URL_NAME_REQUIREMENTS)
907 requirements=URL_NAME_REQUIREMENTS)
918
908
919 rmap.connect('changelog_summary_home', '/{repo_name}/changelog_summary',
909 rmap.connect('changelog_summary_home', '/{repo_name}/changelog_summary',
920 controller='changelog', action='changelog_summary',
910 controller='changelog', action='changelog_summary',
921 conditions={'function': check_repo},
911 conditions={'function': check_repo},
922 requirements=URL_NAME_REQUIREMENTS)
912 requirements=URL_NAME_REQUIREMENTS)
923
913
924 rmap.connect('changelog_file_home',
914 rmap.connect('changelog_file_home',
925 '/{repo_name}/changelog/{revision}/{f_path}',
915 '/{repo_name}/changelog/{revision}/{f_path}',
926 controller='changelog', f_path=None,
916 controller='changelog', f_path=None,
927 conditions={'function': check_repo},
917 conditions={'function': check_repo},
928 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
918 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
929
919
930 rmap.connect('changelog_elements', '/{repo_name}/changelog_details',
920 rmap.connect('changelog_elements', '/{repo_name}/changelog_details',
931 controller='changelog', action='changelog_elements',
921 controller='changelog', action='changelog_elements',
932 conditions={'function': check_repo},
922 conditions={'function': check_repo},
933 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
923 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
934
924
935 rmap.connect('files_home', '/{repo_name}/files/{revision}/{f_path}',
925 rmap.connect('files_home', '/{repo_name}/files/{revision}/{f_path}',
936 controller='files', revision='tip', f_path='',
926 controller='files', revision='tip', f_path='',
937 conditions={'function': check_repo},
927 conditions={'function': check_repo},
938 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
928 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
939
929
940 rmap.connect('files_home_simple_catchrev',
930 rmap.connect('files_home_simple_catchrev',
941 '/{repo_name}/files/{revision}',
931 '/{repo_name}/files/{revision}',
942 controller='files', revision='tip', f_path='',
932 controller='files', revision='tip', f_path='',
943 conditions={'function': check_repo},
933 conditions={'function': check_repo},
944 requirements=URL_NAME_REQUIREMENTS)
934 requirements=URL_NAME_REQUIREMENTS)
945
935
946 rmap.connect('files_home_simple_catchall',
936 rmap.connect('files_home_simple_catchall',
947 '/{repo_name}/files',
937 '/{repo_name}/files',
948 controller='files', revision='tip', f_path='',
938 controller='files', revision='tip', f_path='',
949 conditions={'function': check_repo},
939 conditions={'function': check_repo},
950 requirements=URL_NAME_REQUIREMENTS)
940 requirements=URL_NAME_REQUIREMENTS)
951
941
952 rmap.connect('files_history_home',
942 rmap.connect('files_history_home',
953 '/{repo_name}/history/{revision}/{f_path}',
943 '/{repo_name}/history/{revision}/{f_path}',
954 controller='files', action='history', revision='tip', f_path='',
944 controller='files', action='history', revision='tip', f_path='',
955 conditions={'function': check_repo},
945 conditions={'function': check_repo},
956 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
946 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
957
947
958 rmap.connect('files_authors_home',
948 rmap.connect('files_authors_home',
959 '/{repo_name}/authors/{revision}/{f_path}',
949 '/{repo_name}/authors/{revision}/{f_path}',
960 controller='files', action='authors', revision='tip', f_path='',
950 controller='files', action='authors', revision='tip', f_path='',
961 conditions={'function': check_repo},
951 conditions={'function': check_repo},
962 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
952 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
963
953
964 rmap.connect('files_diff_home', '/{repo_name}/diff/{f_path}',
954 rmap.connect('files_diff_home', '/{repo_name}/diff/{f_path}',
965 controller='files', action='diff', f_path='',
955 controller='files', action='diff', f_path='',
966 conditions={'function': check_repo},
956 conditions={'function': check_repo},
967 requirements=URL_NAME_REQUIREMENTS)
957 requirements=URL_NAME_REQUIREMENTS)
968
958
969 rmap.connect('files_diff_2way_home',
959 rmap.connect('files_diff_2way_home',
970 '/{repo_name}/diff-2way/{f_path}',
960 '/{repo_name}/diff-2way/{f_path}',
971 controller='files', action='diff_2way', f_path='',
961 controller='files', action='diff_2way', f_path='',
972 conditions={'function': check_repo},
962 conditions={'function': check_repo},
973 requirements=URL_NAME_REQUIREMENTS)
963 requirements=URL_NAME_REQUIREMENTS)
974
964
975 rmap.connect('files_rawfile_home',
965 rmap.connect('files_rawfile_home',
976 '/{repo_name}/rawfile/{revision}/{f_path}',
966 '/{repo_name}/rawfile/{revision}/{f_path}',
977 controller='files', action='rawfile', revision='tip',
967 controller='files', action='rawfile', revision='tip',
978 f_path='', conditions={'function': check_repo},
968 f_path='', conditions={'function': check_repo},
979 requirements=URL_NAME_REQUIREMENTS)
969 requirements=URL_NAME_REQUIREMENTS)
980
970
981 rmap.connect('files_raw_home',
971 rmap.connect('files_raw_home',
982 '/{repo_name}/raw/{revision}/{f_path}',
972 '/{repo_name}/raw/{revision}/{f_path}',
983 controller='files', action='raw', revision='tip', f_path='',
973 controller='files', action='raw', revision='tip', f_path='',
984 conditions={'function': check_repo},
974 conditions={'function': check_repo},
985 requirements=URL_NAME_REQUIREMENTS)
975 requirements=URL_NAME_REQUIREMENTS)
986
976
987 rmap.connect('files_render_home',
977 rmap.connect('files_render_home',
988 '/{repo_name}/render/{revision}/{f_path}',
978 '/{repo_name}/render/{revision}/{f_path}',
989 controller='files', action='index', revision='tip', f_path='',
979 controller='files', action='index', revision='tip', f_path='',
990 rendered=True, conditions={'function': check_repo},
980 rendered=True, conditions={'function': check_repo},
991 requirements=URL_NAME_REQUIREMENTS)
981 requirements=URL_NAME_REQUIREMENTS)
992
982
993 rmap.connect('files_annotate_home',
983 rmap.connect('files_annotate_home',
994 '/{repo_name}/annotate/{revision}/{f_path}',
984 '/{repo_name}/annotate/{revision}/{f_path}',
995 controller='files', action='index', revision='tip',
985 controller='files', action='index', revision='tip',
996 f_path='', annotate=True, conditions={'function': check_repo},
986 f_path='', annotate=True, conditions={'function': check_repo},
997 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
987 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
998
988
999 rmap.connect('files_annotate_previous',
989 rmap.connect('files_annotate_previous',
1000 '/{repo_name}/annotate-previous/{revision}/{f_path}',
990 '/{repo_name}/annotate-previous/{revision}/{f_path}',
1001 controller='files', action='annotate_previous', revision='tip',
991 controller='files', action='annotate_previous', revision='tip',
1002 f_path='', annotate=True, conditions={'function': check_repo},
992 f_path='', annotate=True, conditions={'function': check_repo},
1003 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
993 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1004
994
1005 rmap.connect('files_edit',
995 rmap.connect('files_edit',
1006 '/{repo_name}/edit/{revision}/{f_path}',
996 '/{repo_name}/edit/{revision}/{f_path}',
1007 controller='files', action='edit', revision='tip',
997 controller='files', action='edit', revision='tip',
1008 f_path='',
998 f_path='',
1009 conditions={'function': check_repo, 'method': ['POST']},
999 conditions={'function': check_repo, 'method': ['POST']},
1010 requirements=URL_NAME_REQUIREMENTS)
1000 requirements=URL_NAME_REQUIREMENTS)
1011
1001
1012 rmap.connect('files_edit_home',
1002 rmap.connect('files_edit_home',
1013 '/{repo_name}/edit/{revision}/{f_path}',
1003 '/{repo_name}/edit/{revision}/{f_path}',
1014 controller='files', action='edit_home', revision='tip',
1004 controller='files', action='edit_home', revision='tip',
1015 f_path='', conditions={'function': check_repo},
1005 f_path='', conditions={'function': check_repo},
1016 requirements=URL_NAME_REQUIREMENTS)
1006 requirements=URL_NAME_REQUIREMENTS)
1017
1007
1018 rmap.connect('files_add',
1008 rmap.connect('files_add',
1019 '/{repo_name}/add/{revision}/{f_path}',
1009 '/{repo_name}/add/{revision}/{f_path}',
1020 controller='files', action='add', revision='tip',
1010 controller='files', action='add', revision='tip',
1021 f_path='',
1011 f_path='',
1022 conditions={'function': check_repo, 'method': ['POST']},
1012 conditions={'function': check_repo, 'method': ['POST']},
1023 requirements=URL_NAME_REQUIREMENTS)
1013 requirements=URL_NAME_REQUIREMENTS)
1024
1014
1025 rmap.connect('files_add_home',
1015 rmap.connect('files_add_home',
1026 '/{repo_name}/add/{revision}/{f_path}',
1016 '/{repo_name}/add/{revision}/{f_path}',
1027 controller='files', action='add_home', revision='tip',
1017 controller='files', action='add_home', revision='tip',
1028 f_path='', conditions={'function': check_repo},
1018 f_path='', conditions={'function': check_repo},
1029 requirements=URL_NAME_REQUIREMENTS)
1019 requirements=URL_NAME_REQUIREMENTS)
1030
1020
1031 rmap.connect('files_delete',
1021 rmap.connect('files_delete',
1032 '/{repo_name}/delete/{revision}/{f_path}',
1022 '/{repo_name}/delete/{revision}/{f_path}',
1033 controller='files', action='delete', revision='tip',
1023 controller='files', action='delete', revision='tip',
1034 f_path='',
1024 f_path='',
1035 conditions={'function': check_repo, 'method': ['POST']},
1025 conditions={'function': check_repo, 'method': ['POST']},
1036 requirements=URL_NAME_REQUIREMENTS)
1026 requirements=URL_NAME_REQUIREMENTS)
1037
1027
1038 rmap.connect('files_delete_home',
1028 rmap.connect('files_delete_home',
1039 '/{repo_name}/delete/{revision}/{f_path}',
1029 '/{repo_name}/delete/{revision}/{f_path}',
1040 controller='files', action='delete_home', revision='tip',
1030 controller='files', action='delete_home', revision='tip',
1041 f_path='', conditions={'function': check_repo},
1031 f_path='', conditions={'function': check_repo},
1042 requirements=URL_NAME_REQUIREMENTS)
1032 requirements=URL_NAME_REQUIREMENTS)
1043
1033
1044 rmap.connect('files_archive_home', '/{repo_name}/archive/{fname}',
1034 rmap.connect('files_archive_home', '/{repo_name}/archive/{fname}',
1045 controller='files', action='archivefile',
1035 controller='files', action='archivefile',
1046 conditions={'function': check_repo},
1036 conditions={'function': check_repo},
1047 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1037 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1048
1038
1049 rmap.connect('files_nodelist_home',
1039 rmap.connect('files_nodelist_home',
1050 '/{repo_name}/nodelist/{revision}/{f_path}',
1040 '/{repo_name}/nodelist/{revision}/{f_path}',
1051 controller='files', action='nodelist',
1041 controller='files', action='nodelist',
1052 conditions={'function': check_repo},
1042 conditions={'function': check_repo},
1053 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1043 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1054
1044
1055 rmap.connect('files_nodetree_full',
1045 rmap.connect('files_nodetree_full',
1056 '/{repo_name}/nodetree_full/{commit_id}/{f_path}',
1046 '/{repo_name}/nodetree_full/{commit_id}/{f_path}',
1057 controller='files', action='nodetree_full',
1047 controller='files', action='nodetree_full',
1058 conditions={'function': check_repo},
1048 conditions={'function': check_repo},
1059 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1049 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1060
1050
1061 rmap.connect('repo_fork_create_home', '/{repo_name}/fork',
1051 rmap.connect('repo_fork_create_home', '/{repo_name}/fork',
1062 controller='forks', action='fork_create',
1052 controller='forks', action='fork_create',
1063 conditions={'function': check_repo, 'method': ['POST']},
1053 conditions={'function': check_repo, 'method': ['POST']},
1064 requirements=URL_NAME_REQUIREMENTS)
1054 requirements=URL_NAME_REQUIREMENTS)
1065
1055
1066 rmap.connect('repo_fork_home', '/{repo_name}/fork',
1056 rmap.connect('repo_fork_home', '/{repo_name}/fork',
1067 controller='forks', action='fork',
1057 controller='forks', action='fork',
1068 conditions={'function': check_repo},
1058 conditions={'function': check_repo},
1069 requirements=URL_NAME_REQUIREMENTS)
1059 requirements=URL_NAME_REQUIREMENTS)
1070
1060
1071 rmap.connect('repo_forks_home', '/{repo_name}/forks',
1061 rmap.connect('repo_forks_home', '/{repo_name}/forks',
1072 controller='forks', action='forks',
1062 controller='forks', action='forks',
1073 conditions={'function': check_repo},
1063 conditions={'function': check_repo},
1074 requirements=URL_NAME_REQUIREMENTS)
1064 requirements=URL_NAME_REQUIREMENTS)
1075
1065
1076 # must be here for proper group/repo catching pattern
1066 # must be here for proper group/repo catching pattern
1077 _connect_with_slash(
1067 _connect_with_slash(
1078 rmap, 'repo_group_home', '/{group_name}',
1068 rmap, 'repo_group_home', '/{group_name}',
1079 controller='home', action='index_repo_group',
1069 controller='home', action='index_repo_group',
1080 conditions={'function': check_group},
1070 conditions={'function': check_group},
1081 requirements=URL_NAME_REQUIREMENTS)
1071 requirements=URL_NAME_REQUIREMENTS)
1082
1072
1083 # catch all, at the end
1073 # catch all, at the end
1084 _connect_with_slash(
1074 _connect_with_slash(
1085 rmap, 'summary_home', '/{repo_name}', jsroute=True,
1075 rmap, 'summary_home', '/{repo_name}', jsroute=True,
1086 controller='summary', action='index',
1076 controller='summary', action='index',
1087 conditions={'function': check_repo},
1077 conditions={'function': check_repo},
1088 requirements=URL_NAME_REQUIREMENTS)
1078 requirements=URL_NAME_REQUIREMENTS)
1089
1079
1090 return rmap
1080 return rmap
1091
1081
1092
1082
1093 def _connect_with_slash(mapper, name, path, *args, **kwargs):
1083 def _connect_with_slash(mapper, name, path, *args, **kwargs):
1094 """
1084 """
1095 Connect a route with an optional trailing slash in `path`.
1085 Connect a route with an optional trailing slash in `path`.
1096 """
1086 """
1097 mapper.connect(name + '_slash', path + '/', *args, **kwargs)
1087 mapper.connect(name + '_slash', path + '/', *args, **kwargs)
1098 mapper.connect(name, path, *args, **kwargs)
1088 mapper.connect(name, path, *args, **kwargs)
@@ -1,781 +1,754 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2013-2017 RhodeCode GmbH
3 # Copyright (C) 2013-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 """
22 """
23 Repositories controller for RhodeCode
23 Repositories controller for RhodeCode
24 """
24 """
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28
28
29 import formencode
29 import formencode
30 from formencode import htmlfill
30 from formencode import htmlfill
31 from pylons import request, tmpl_context as c, url
31 from pylons import request, tmpl_context as c, url
32 from pylons.controllers.util import redirect
32 from pylons.controllers.util import redirect
33 from pylons.i18n.translation import _
33 from pylons.i18n.translation import _
34 from webob.exc import HTTPForbidden, HTTPNotFound, HTTPBadRequest
34 from webob.exc import HTTPForbidden, HTTPNotFound, HTTPBadRequest
35
35
36 import rhodecode
36 import rhodecode
37 from rhodecode.lib import auth, helpers as h
37 from rhodecode.lib import auth, helpers as h
38 from rhodecode.lib.auth import (
38 from rhodecode.lib.auth import (
39 LoginRequired, HasPermissionAllDecorator,
39 LoginRequired, HasPermissionAllDecorator,
40 HasRepoPermissionAllDecorator, NotAnonymous, HasPermissionAny,
40 HasRepoPermissionAllDecorator, NotAnonymous, HasPermissionAny,
41 HasRepoGroupPermissionAny, HasRepoPermissionAnyDecorator)
41 HasRepoGroupPermissionAny, HasRepoPermissionAnyDecorator)
42 from rhodecode.lib.base import BaseRepoController, render
42 from rhodecode.lib.base import BaseRepoController, render
43 from rhodecode.lib.ext_json import json
43 from rhodecode.lib.ext_json import json
44 from rhodecode.lib.exceptions import AttachedForksError
44 from rhodecode.lib.exceptions import AttachedForksError
45 from rhodecode.lib.utils import action_logger, repo_name_slug, jsonify
45 from rhodecode.lib.utils import action_logger, repo_name_slug, jsonify
46 from rhodecode.lib.utils2 import safe_int, str2bool
46 from rhodecode.lib.utils2 import safe_int, str2bool
47 from rhodecode.lib.vcs import RepositoryError
47 from rhodecode.lib.vcs import RepositoryError
48 from rhodecode.model.db import (
48 from rhodecode.model.db import (
49 User, Repository, UserFollowing, RepoGroup, RepositoryField)
49 User, Repository, UserFollowing, RepoGroup, RepositoryField)
50 from rhodecode.model.forms import (
50 from rhodecode.model.forms import (
51 RepoForm, RepoFieldForm, RepoPermsForm, RepoVcsSettingsForm,
51 RepoForm, RepoFieldForm, RepoPermsForm, RepoVcsSettingsForm,
52 IssueTrackerPatternsForm)
52 IssueTrackerPatternsForm)
53 from rhodecode.model.meta import Session
53 from rhodecode.model.meta import Session
54 from rhodecode.model.repo import RepoModel
54 from rhodecode.model.repo import RepoModel
55 from rhodecode.model.scm import ScmModel, RepoGroupList, RepoList
55 from rhodecode.model.scm import ScmModel, RepoGroupList, RepoList
56 from rhodecode.model.settings import (
56 from rhodecode.model.settings import (
57 SettingsModel, IssueTrackerSettingsModel, VcsSettingsModel,
57 SettingsModel, IssueTrackerSettingsModel, VcsSettingsModel,
58 SettingNotFound)
58 SettingNotFound)
59
59
60 log = logging.getLogger(__name__)
60 log = logging.getLogger(__name__)
61
61
62
62
63 class ReposController(BaseRepoController):
63 class ReposController(BaseRepoController):
64 """
64 """
65 REST Controller styled on the Atom Publishing Protocol"""
65 REST Controller styled on the Atom Publishing Protocol"""
66 # To properly map this controller, ensure your config/routing.py
66 # To properly map this controller, ensure your config/routing.py
67 # file has a resource setup:
67 # file has a resource setup:
68 # map.resource('repo', 'repos')
68 # map.resource('repo', 'repos')
69
69
70 @LoginRequired()
70 @LoginRequired()
71 def __before__(self):
71 def __before__(self):
72 super(ReposController, self).__before__()
72 super(ReposController, self).__before__()
73
73
74 def _load_repo(self, repo_name):
74 def _load_repo(self, repo_name):
75 repo_obj = Repository.get_by_repo_name(repo_name)
75 repo_obj = Repository.get_by_repo_name(repo_name)
76
76
77 if repo_obj is None:
77 if repo_obj is None:
78 h.not_mapped_error(repo_name)
78 h.not_mapped_error(repo_name)
79 return redirect(url('repos'))
79 return redirect(url('repos'))
80
80
81 return repo_obj
81 return repo_obj
82
82
83 def __load_defaults(self, repo=None):
83 def __load_defaults(self, repo=None):
84 acl_groups = RepoGroupList(RepoGroup.query().all(),
84 acl_groups = RepoGroupList(RepoGroup.query().all(),
85 perm_set=['group.write', 'group.admin'])
85 perm_set=['group.write', 'group.admin'])
86 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
86 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
87 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
87 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
88
88
89 # in case someone no longer have a group.write access to a repository
89 # in case someone no longer have a group.write access to a repository
90 # pre fill the list with this entry, we don't care if this is the same
90 # pre fill the list with this entry, we don't care if this is the same
91 # but it will allow saving repo data properly.
91 # but it will allow saving repo data properly.
92
92
93 repo_group = None
93 repo_group = None
94 if repo:
94 if repo:
95 repo_group = repo.group
95 repo_group = repo.group
96 if repo_group and unicode(repo_group.group_id) not in c.repo_groups_choices:
96 if repo_group and unicode(repo_group.group_id) not in c.repo_groups_choices:
97 c.repo_groups_choices.append(unicode(repo_group.group_id))
97 c.repo_groups_choices.append(unicode(repo_group.group_id))
98 c.repo_groups.append(RepoGroup._generate_choice(repo_group))
98 c.repo_groups.append(RepoGroup._generate_choice(repo_group))
99
99
100 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
100 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
101 c.landing_revs_choices = choices
101 c.landing_revs_choices = choices
102
102
103 def __load_data(self, repo_name=None):
103 def __load_data(self, repo_name=None):
104 """
104 """
105 Load defaults settings for edit, and update
105 Load defaults settings for edit, and update
106
106
107 :param repo_name:
107 :param repo_name:
108 """
108 """
109 c.repo_info = self._load_repo(repo_name)
109 c.repo_info = self._load_repo(repo_name)
110 self.__load_defaults(c.repo_info)
110 self.__load_defaults(c.repo_info)
111
111
112 # override defaults for exact repo info here git/hg etc
112 # override defaults for exact repo info here git/hg etc
113 if not c.repository_requirements_missing:
113 if not c.repository_requirements_missing:
114 choices, c.landing_revs = ScmModel().get_repo_landing_revs(
114 choices, c.landing_revs = ScmModel().get_repo_landing_revs(
115 c.repo_info)
115 c.repo_info)
116 c.landing_revs_choices = choices
116 c.landing_revs_choices = choices
117 defaults = RepoModel()._get_defaults(repo_name)
117 defaults = RepoModel()._get_defaults(repo_name)
118
118
119 return defaults
119 return defaults
120
120
121 def _log_creation_exception(self, e, repo_name):
121 def _log_creation_exception(self, e, repo_name):
122 reason = None
122 reason = None
123 if len(e.args) == 2:
123 if len(e.args) == 2:
124 reason = e.args[1]
124 reason = e.args[1]
125
125
126 if reason == 'INVALID_CERTIFICATE':
126 if reason == 'INVALID_CERTIFICATE':
127 log.exception(
127 log.exception(
128 'Exception creating a repository: invalid certificate')
128 'Exception creating a repository: invalid certificate')
129 msg = (_('Error creating repository %s: invalid certificate')
129 msg = (_('Error creating repository %s: invalid certificate')
130 % repo_name)
130 % repo_name)
131 else:
131 else:
132 log.exception("Exception creating a repository")
132 log.exception("Exception creating a repository")
133 msg = (_('Error creating repository %s')
133 msg = (_('Error creating repository %s')
134 % repo_name)
134 % repo_name)
135
135
136 return msg
136 return msg
137
137
138 @NotAnonymous()
138 @NotAnonymous()
139 def index(self, format='html'):
139 def index(self, format='html'):
140 """GET /repos: All items in the collection"""
140 """GET /repos: All items in the collection"""
141 # url('repos')
141 # url('repos')
142
142
143 repo_list = Repository.get_all_repos()
143 repo_list = Repository.get_all_repos()
144 c.repo_list = RepoList(repo_list, perm_set=['repository.admin'])
144 c.repo_list = RepoList(repo_list, perm_set=['repository.admin'])
145 repos_data = RepoModel().get_repos_as_dict(
145 repos_data = RepoModel().get_repos_as_dict(
146 repo_list=c.repo_list, admin=True, super_user_actions=True)
146 repo_list=c.repo_list, admin=True, super_user_actions=True)
147 # json used to render the grid
147 # json used to render the grid
148 c.data = json.dumps(repos_data)
148 c.data = json.dumps(repos_data)
149
149
150 return render('admin/repos/repos.mako')
150 return render('admin/repos/repos.mako')
151
151
152 # perms check inside
152 # perms check inside
153 @NotAnonymous()
153 @NotAnonymous()
154 @auth.CSRFRequired()
154 @auth.CSRFRequired()
155 def create(self):
155 def create(self):
156 """
156 """
157 POST /repos: Create a new item"""
157 POST /repos: Create a new item"""
158 # url('repos')
158 # url('repos')
159
159
160 self.__load_defaults()
160 self.__load_defaults()
161 form_result = {}
161 form_result = {}
162 task_id = None
162 task_id = None
163 c.personal_repo_group = c.rhodecode_user.personal_repo_group
163 c.personal_repo_group = c.rhodecode_user.personal_repo_group
164 try:
164 try:
165 # CanWriteToGroup validators checks permissions of this POST
165 # CanWriteToGroup validators checks permissions of this POST
166 form_result = RepoForm(repo_groups=c.repo_groups_choices,
166 form_result = RepoForm(repo_groups=c.repo_groups_choices,
167 landing_revs=c.landing_revs_choices)()\
167 landing_revs=c.landing_revs_choices)()\
168 .to_python(dict(request.POST))
168 .to_python(dict(request.POST))
169
169
170 # create is done sometimes async on celery, db transaction
170 # create is done sometimes async on celery, db transaction
171 # management is handled there.
171 # management is handled there.
172 task = RepoModel().create(form_result, c.rhodecode_user.user_id)
172 task = RepoModel().create(form_result, c.rhodecode_user.user_id)
173 from celery.result import BaseAsyncResult
173 from celery.result import BaseAsyncResult
174 if isinstance(task, BaseAsyncResult):
174 if isinstance(task, BaseAsyncResult):
175 task_id = task.task_id
175 task_id = task.task_id
176 except formencode.Invalid as errors:
176 except formencode.Invalid as errors:
177 return htmlfill.render(
177 return htmlfill.render(
178 render('admin/repos/repo_add.mako'),
178 render('admin/repos/repo_add.mako'),
179 defaults=errors.value,
179 defaults=errors.value,
180 errors=errors.error_dict or {},
180 errors=errors.error_dict or {},
181 prefix_error=False,
181 prefix_error=False,
182 encoding="UTF-8",
182 encoding="UTF-8",
183 force_defaults=False)
183 force_defaults=False)
184
184
185 except Exception as e:
185 except Exception as e:
186 msg = self._log_creation_exception(e, form_result.get('repo_name'))
186 msg = self._log_creation_exception(e, form_result.get('repo_name'))
187 h.flash(msg, category='error')
187 h.flash(msg, category='error')
188 return redirect(url('home'))
188 return redirect(url('home'))
189
189
190 return redirect(h.url('repo_creating_home',
190 return redirect(h.url('repo_creating_home',
191 repo_name=form_result['repo_name_full'],
191 repo_name=form_result['repo_name_full'],
192 task_id=task_id))
192 task_id=task_id))
193
193
194 # perms check inside
194 # perms check inside
195 @NotAnonymous()
195 @NotAnonymous()
196 def create_repository(self):
196 def create_repository(self):
197 """GET /_admin/create_repository: Form to create a new item"""
197 """GET /_admin/create_repository: Form to create a new item"""
198 new_repo = request.GET.get('repo', '')
198 new_repo = request.GET.get('repo', '')
199 parent_group = safe_int(request.GET.get('parent_group'))
199 parent_group = safe_int(request.GET.get('parent_group'))
200 _gr = RepoGroup.get(parent_group)
200 _gr = RepoGroup.get(parent_group)
201
201
202 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
202 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
203 # you're not super admin nor have global create permissions,
203 # you're not super admin nor have global create permissions,
204 # but maybe you have at least write permission to a parent group ?
204 # but maybe you have at least write permission to a parent group ?
205
205
206 gr_name = _gr.group_name if _gr else None
206 gr_name = _gr.group_name if _gr else None
207 # create repositories with write permission on group is set to true
207 # create repositories with write permission on group is set to true
208 create_on_write = HasPermissionAny('hg.create.write_on_repogroup.true')()
208 create_on_write = HasPermissionAny('hg.create.write_on_repogroup.true')()
209 group_admin = HasRepoGroupPermissionAny('group.admin')(group_name=gr_name)
209 group_admin = HasRepoGroupPermissionAny('group.admin')(group_name=gr_name)
210 group_write = HasRepoGroupPermissionAny('group.write')(group_name=gr_name)
210 group_write = HasRepoGroupPermissionAny('group.write')(group_name=gr_name)
211 if not (group_admin or (group_write and create_on_write)):
211 if not (group_admin or (group_write and create_on_write)):
212 raise HTTPForbidden
212 raise HTTPForbidden
213
213
214 acl_groups = RepoGroupList(RepoGroup.query().all(),
214 acl_groups = RepoGroupList(RepoGroup.query().all(),
215 perm_set=['group.write', 'group.admin'])
215 perm_set=['group.write', 'group.admin'])
216 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
216 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
217 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
217 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
218 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
218 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
219 c.personal_repo_group = c.rhodecode_user.personal_repo_group
219 c.personal_repo_group = c.rhodecode_user.personal_repo_group
220 c.new_repo = repo_name_slug(new_repo)
220 c.new_repo = repo_name_slug(new_repo)
221
221
222 # apply the defaults from defaults page
222 # apply the defaults from defaults page
223 defaults = SettingsModel().get_default_repo_settings(strip_prefix=True)
223 defaults = SettingsModel().get_default_repo_settings(strip_prefix=True)
224 # set checkbox to autochecked
224 # set checkbox to autochecked
225 defaults['repo_copy_permissions'] = True
225 defaults['repo_copy_permissions'] = True
226
226
227 parent_group_choice = '-1'
227 parent_group_choice = '-1'
228 if not c.rhodecode_user.is_admin and c.rhodecode_user.personal_repo_group:
228 if not c.rhodecode_user.is_admin and c.rhodecode_user.personal_repo_group:
229 parent_group_choice = c.rhodecode_user.personal_repo_group
229 parent_group_choice = c.rhodecode_user.personal_repo_group
230
230
231 if parent_group and _gr:
231 if parent_group and _gr:
232 if parent_group in [x[0] for x in c.repo_groups]:
232 if parent_group in [x[0] for x in c.repo_groups]:
233 parent_group_choice = unicode(parent_group)
233 parent_group_choice = unicode(parent_group)
234
234
235 defaults.update({'repo_group': parent_group_choice})
235 defaults.update({'repo_group': parent_group_choice})
236
236
237 return htmlfill.render(
237 return htmlfill.render(
238 render('admin/repos/repo_add.mako'),
238 render('admin/repos/repo_add.mako'),
239 defaults=defaults,
239 defaults=defaults,
240 errors={},
240 errors={},
241 prefix_error=False,
241 prefix_error=False,
242 encoding="UTF-8",
242 encoding="UTF-8",
243 force_defaults=False
243 force_defaults=False
244 )
244 )
245
245
246 @NotAnonymous()
246 @NotAnonymous()
247 def repo_creating(self, repo_name):
247 def repo_creating(self, repo_name):
248 c.repo = repo_name
248 c.repo = repo_name
249 c.task_id = request.GET.get('task_id')
249 c.task_id = request.GET.get('task_id')
250 if not c.repo:
250 if not c.repo:
251 raise HTTPNotFound()
251 raise HTTPNotFound()
252 return render('admin/repos/repo_creating.mako')
252 return render('admin/repos/repo_creating.mako')
253
253
254 @NotAnonymous()
254 @NotAnonymous()
255 @jsonify
255 @jsonify
256 def repo_check(self, repo_name):
256 def repo_check(self, repo_name):
257 c.repo = repo_name
257 c.repo = repo_name
258 task_id = request.GET.get('task_id')
258 task_id = request.GET.get('task_id')
259
259
260 if task_id and task_id not in ['None']:
260 if task_id and task_id not in ['None']:
261 import rhodecode
261 import rhodecode
262 from celery.result import AsyncResult
262 from celery.result import AsyncResult
263 if rhodecode.CELERY_ENABLED:
263 if rhodecode.CELERY_ENABLED:
264 task = AsyncResult(task_id)
264 task = AsyncResult(task_id)
265 if task.failed():
265 if task.failed():
266 msg = self._log_creation_exception(task.result, c.repo)
266 msg = self._log_creation_exception(task.result, c.repo)
267 h.flash(msg, category='error')
267 h.flash(msg, category='error')
268 return redirect(url('home'), code=501)
268 return redirect(url('home'), code=501)
269
269
270 repo = Repository.get_by_repo_name(repo_name)
270 repo = Repository.get_by_repo_name(repo_name)
271 if repo and repo.repo_state == Repository.STATE_CREATED:
271 if repo and repo.repo_state == Repository.STATE_CREATED:
272 if repo.clone_uri:
272 if repo.clone_uri:
273 clone_uri = repo.clone_uri_hidden
273 clone_uri = repo.clone_uri_hidden
274 h.flash(_('Created repository %s from %s')
274 h.flash(_('Created repository %s from %s')
275 % (repo.repo_name, clone_uri), category='success')
275 % (repo.repo_name, clone_uri), category='success')
276 else:
276 else:
277 repo_url = h.link_to(repo.repo_name,
277 repo_url = h.link_to(repo.repo_name,
278 h.url('summary_home',
278 h.url('summary_home',
279 repo_name=repo.repo_name))
279 repo_name=repo.repo_name))
280 fork = repo.fork
280 fork = repo.fork
281 if fork:
281 if fork:
282 fork_name = fork.repo_name
282 fork_name = fork.repo_name
283 h.flash(h.literal(_('Forked repository %s as %s')
283 h.flash(h.literal(_('Forked repository %s as %s')
284 % (fork_name, repo_url)), category='success')
284 % (fork_name, repo_url)), category='success')
285 else:
285 else:
286 h.flash(h.literal(_('Created repository %s') % repo_url),
286 h.flash(h.literal(_('Created repository %s') % repo_url),
287 category='success')
287 category='success')
288 return {'result': True}
288 return {'result': True}
289 return {'result': False}
289 return {'result': False}
290
290
291 @HasRepoPermissionAllDecorator('repository.admin')
291 @HasRepoPermissionAllDecorator('repository.admin')
292 @auth.CSRFRequired()
292 @auth.CSRFRequired()
293 def delete(self, repo_name):
293 def delete(self, repo_name):
294 """
294 """
295 DELETE /repos/repo_name: Delete an existing item"""
295 DELETE /repos/repo_name: Delete an existing item"""
296 # Forms posted to this method should contain a hidden field:
296 # Forms posted to this method should contain a hidden field:
297 # <input type="hidden" name="_method" value="DELETE" />
297 # <input type="hidden" name="_method" value="DELETE" />
298 # Or using helpers:
298 # Or using helpers:
299 # h.form(url('repo', repo_name=ID),
299 # h.form(url('repo', repo_name=ID),
300 # method='delete')
300 # method='delete')
301 # url('repo', repo_name=ID)
301 # url('repo', repo_name=ID)
302
302
303 repo_model = RepoModel()
303 repo_model = RepoModel()
304 repo = repo_model.get_by_repo_name(repo_name)
304 repo = repo_model.get_by_repo_name(repo_name)
305 if not repo:
305 if not repo:
306 h.not_mapped_error(repo_name)
306 h.not_mapped_error(repo_name)
307 return redirect(url('repos'))
307 return redirect(url('repos'))
308 try:
308 try:
309 _forks = repo.forks.count()
309 _forks = repo.forks.count()
310 handle_forks = None
310 handle_forks = None
311 if _forks and request.POST.get('forks'):
311 if _forks and request.POST.get('forks'):
312 do = request.POST['forks']
312 do = request.POST['forks']
313 if do == 'detach_forks':
313 if do == 'detach_forks':
314 handle_forks = 'detach'
314 handle_forks = 'detach'
315 h.flash(_('Detached %s forks') % _forks, category='success')
315 h.flash(_('Detached %s forks') % _forks, category='success')
316 elif do == 'delete_forks':
316 elif do == 'delete_forks':
317 handle_forks = 'delete'
317 handle_forks = 'delete'
318 h.flash(_('Deleted %s forks') % _forks, category='success')
318 h.flash(_('Deleted %s forks') % _forks, category='success')
319 repo_model.delete(repo, forks=handle_forks)
319 repo_model.delete(repo, forks=handle_forks)
320 action_logger(c.rhodecode_user, 'admin_deleted_repo',
320 action_logger(c.rhodecode_user, 'admin_deleted_repo',
321 repo_name, self.ip_addr, self.sa)
321 repo_name, self.ip_addr, self.sa)
322 ScmModel().mark_for_invalidation(repo_name)
322 ScmModel().mark_for_invalidation(repo_name)
323 h.flash(_('Deleted repository %s') % repo_name, category='success')
323 h.flash(_('Deleted repository %s') % repo_name, category='success')
324 Session().commit()
324 Session().commit()
325 except AttachedForksError:
325 except AttachedForksError:
326 h.flash(_('Cannot delete %s it still contains attached forks')
326 h.flash(_('Cannot delete %s it still contains attached forks')
327 % repo_name, category='warning')
327 % repo_name, category='warning')
328
328
329 except Exception:
329 except Exception:
330 log.exception("Exception during deletion of repository")
330 log.exception("Exception during deletion of repository")
331 h.flash(_('An error occurred during deletion of %s') % repo_name,
331 h.flash(_('An error occurred during deletion of %s') % repo_name,
332 category='error')
332 category='error')
333
333
334 return redirect(url('repos'))
334 return redirect(url('repos'))
335
335
336 @HasPermissionAllDecorator('hg.admin')
336 @HasPermissionAllDecorator('hg.admin')
337 def show(self, repo_name, format='html'):
337 def show(self, repo_name, format='html'):
338 """GET /repos/repo_name: Show a specific item"""
338 """GET /repos/repo_name: Show a specific item"""
339 # url('repo', repo_name=ID)
339 # url('repo', repo_name=ID)
340
340
341 @HasRepoPermissionAllDecorator('repository.admin')
341 @HasRepoPermissionAllDecorator('repository.admin')
342 def edit_permissions(self, repo_name):
343 """GET /repo_name/settings: Form to edit an existing item"""
344 c.repo_info = self._load_repo(repo_name)
345 c.active = 'permissions'
346 defaults = RepoModel()._get_defaults(repo_name)
347
348 return htmlfill.render(
349 render('admin/repos/repo_edit.mako'),
350 defaults=defaults,
351 encoding="UTF-8",
352 force_defaults=False)
353
354 @HasRepoPermissionAllDecorator('repository.admin')
355 @auth.CSRFRequired()
356 def edit_permissions_update(self, repo_name):
357 form = RepoPermsForm()().to_python(request.POST)
358 RepoModel().update_permissions(repo_name,
359 form['perm_additions'], form['perm_updates'], form['perm_deletions'])
360
361 #TODO: implement this
362 #action_logger(c.rhodecode_user, 'admin_changed_repo_permissions',
363 # repo_name, self.ip_addr, self.sa)
364 Session().commit()
365 h.flash(_('Repository permissions updated'), category='success')
366 return redirect(url('edit_repo_perms', repo_name=repo_name))
367
368 @HasRepoPermissionAllDecorator('repository.admin')
369 def edit_fields(self, repo_name):
342 def edit_fields(self, repo_name):
370 """GET /repo_name/settings: Form to edit an existing item"""
343 """GET /repo_name/settings: Form to edit an existing item"""
371 c.repo_info = self._load_repo(repo_name)
344 c.repo_info = self._load_repo(repo_name)
372 c.repo_fields = RepositoryField.query()\
345 c.repo_fields = RepositoryField.query()\
373 .filter(RepositoryField.repository == c.repo_info).all()
346 .filter(RepositoryField.repository == c.repo_info).all()
374 c.active = 'fields'
347 c.active = 'fields'
375 if request.POST:
348 if request.POST:
376
349
377 return redirect(url('repo_edit_fields'))
350 return redirect(url('repo_edit_fields'))
378 return render('admin/repos/repo_edit.mako')
351 return render('admin/repos/repo_edit.mako')
379
352
380 @HasRepoPermissionAllDecorator('repository.admin')
353 @HasRepoPermissionAllDecorator('repository.admin')
381 @auth.CSRFRequired()
354 @auth.CSRFRequired()
382 def create_repo_field(self, repo_name):
355 def create_repo_field(self, repo_name):
383 try:
356 try:
384 form_result = RepoFieldForm()().to_python(dict(request.POST))
357 form_result = RepoFieldForm()().to_python(dict(request.POST))
385 RepoModel().add_repo_field(
358 RepoModel().add_repo_field(
386 repo_name, form_result['new_field_key'],
359 repo_name, form_result['new_field_key'],
387 field_type=form_result['new_field_type'],
360 field_type=form_result['new_field_type'],
388 field_value=form_result['new_field_value'],
361 field_value=form_result['new_field_value'],
389 field_label=form_result['new_field_label'],
362 field_label=form_result['new_field_label'],
390 field_desc=form_result['new_field_desc'])
363 field_desc=form_result['new_field_desc'])
391
364
392 Session().commit()
365 Session().commit()
393 except Exception as e:
366 except Exception as e:
394 log.exception("Exception creating field")
367 log.exception("Exception creating field")
395 msg = _('An error occurred during creation of field')
368 msg = _('An error occurred during creation of field')
396 if isinstance(e, formencode.Invalid):
369 if isinstance(e, formencode.Invalid):
397 msg += ". " + e.msg
370 msg += ". " + e.msg
398 h.flash(msg, category='error')
371 h.flash(msg, category='error')
399 return redirect(url('edit_repo_fields', repo_name=repo_name))
372 return redirect(url('edit_repo_fields', repo_name=repo_name))
400
373
401 @HasRepoPermissionAllDecorator('repository.admin')
374 @HasRepoPermissionAllDecorator('repository.admin')
402 @auth.CSRFRequired()
375 @auth.CSRFRequired()
403 def delete_repo_field(self, repo_name, field_id):
376 def delete_repo_field(self, repo_name, field_id):
404 field = RepositoryField.get_or_404(field_id)
377 field = RepositoryField.get_or_404(field_id)
405 try:
378 try:
406 RepoModel().delete_repo_field(repo_name, field.field_key)
379 RepoModel().delete_repo_field(repo_name, field.field_key)
407 Session().commit()
380 Session().commit()
408 except Exception as e:
381 except Exception as e:
409 log.exception("Exception during removal of field")
382 log.exception("Exception during removal of field")
410 msg = _('An error occurred during removal of field')
383 msg = _('An error occurred during removal of field')
411 h.flash(msg, category='error')
384 h.flash(msg, category='error')
412 return redirect(url('edit_repo_fields', repo_name=repo_name))
385 return redirect(url('edit_repo_fields', repo_name=repo_name))
413
386
414 @HasRepoPermissionAllDecorator('repository.admin')
387 @HasRepoPermissionAllDecorator('repository.admin')
415 def edit_advanced(self, repo_name):
388 def edit_advanced(self, repo_name):
416 """GET /repo_name/settings: Form to edit an existing item"""
389 """GET /repo_name/settings: Form to edit an existing item"""
417 c.repo_info = self._load_repo(repo_name)
390 c.repo_info = self._load_repo(repo_name)
418 c.default_user_id = User.get_default_user().user_id
391 c.default_user_id = User.get_default_user().user_id
419 c.in_public_journal = UserFollowing.query()\
392 c.in_public_journal = UserFollowing.query()\
420 .filter(UserFollowing.user_id == c.default_user_id)\
393 .filter(UserFollowing.user_id == c.default_user_id)\
421 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
394 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
422
395
423 c.active = 'advanced'
396 c.active = 'advanced'
424 c.has_origin_repo_read_perm = False
397 c.has_origin_repo_read_perm = False
425 if c.repo_info.fork:
398 if c.repo_info.fork:
426 c.has_origin_repo_read_perm = h.HasRepoPermissionAny(
399 c.has_origin_repo_read_perm = h.HasRepoPermissionAny(
427 'repository.write', 'repository.read', 'repository.admin')(
400 'repository.write', 'repository.read', 'repository.admin')(
428 c.repo_info.fork.repo_name, 'repo set as fork page')
401 c.repo_info.fork.repo_name, 'repo set as fork page')
429
402
430 if request.POST:
403 if request.POST:
431 return redirect(url('repo_edit_advanced'))
404 return redirect(url('repo_edit_advanced'))
432 return render('admin/repos/repo_edit.mako')
405 return render('admin/repos/repo_edit.mako')
433
406
434 @HasRepoPermissionAllDecorator('repository.admin')
407 @HasRepoPermissionAllDecorator('repository.admin')
435 @auth.CSRFRequired()
408 @auth.CSRFRequired()
436 def edit_advanced_journal(self, repo_name):
409 def edit_advanced_journal(self, repo_name):
437 """
410 """
438 Set's this repository to be visible in public journal,
411 Set's this repository to be visible in public journal,
439 in other words assing default user to follow this repo
412 in other words assing default user to follow this repo
440
413
441 :param repo_name:
414 :param repo_name:
442 """
415 """
443
416
444 try:
417 try:
445 repo_id = Repository.get_by_repo_name(repo_name).repo_id
418 repo_id = Repository.get_by_repo_name(repo_name).repo_id
446 user_id = User.get_default_user().user_id
419 user_id = User.get_default_user().user_id
447 self.scm_model.toggle_following_repo(repo_id, user_id)
420 self.scm_model.toggle_following_repo(repo_id, user_id)
448 h.flash(_('Updated repository visibility in public journal'),
421 h.flash(_('Updated repository visibility in public journal'),
449 category='success')
422 category='success')
450 Session().commit()
423 Session().commit()
451 except Exception:
424 except Exception:
452 h.flash(_('An error occurred during setting this'
425 h.flash(_('An error occurred during setting this'
453 ' repository in public journal'),
426 ' repository in public journal'),
454 category='error')
427 category='error')
455
428
456 return redirect(url('edit_repo_advanced', repo_name=repo_name))
429 return redirect(url('edit_repo_advanced', repo_name=repo_name))
457
430
458 @HasRepoPermissionAllDecorator('repository.admin')
431 @HasRepoPermissionAllDecorator('repository.admin')
459 @auth.CSRFRequired()
432 @auth.CSRFRequired()
460 def edit_advanced_fork(self, repo_name):
433 def edit_advanced_fork(self, repo_name):
461 """
434 """
462 Mark given repository as a fork of another
435 Mark given repository as a fork of another
463
436
464 :param repo_name:
437 :param repo_name:
465 """
438 """
466
439
467 new_fork_id = request.POST.get('id_fork_of')
440 new_fork_id = request.POST.get('id_fork_of')
468 try:
441 try:
469
442
470 if new_fork_id and not new_fork_id.isdigit():
443 if new_fork_id and not new_fork_id.isdigit():
471 log.error('Given fork id %s is not an INT', new_fork_id)
444 log.error('Given fork id %s is not an INT', new_fork_id)
472
445
473 fork_id = safe_int(new_fork_id)
446 fork_id = safe_int(new_fork_id)
474 repo = ScmModel().mark_as_fork(repo_name, fork_id,
447 repo = ScmModel().mark_as_fork(repo_name, fork_id,
475 c.rhodecode_user.username)
448 c.rhodecode_user.username)
476 fork = repo.fork.repo_name if repo.fork else _('Nothing')
449 fork = repo.fork.repo_name if repo.fork else _('Nothing')
477 Session().commit()
450 Session().commit()
478 h.flash(_('Marked repo %s as fork of %s') % (repo_name, fork),
451 h.flash(_('Marked repo %s as fork of %s') % (repo_name, fork),
479 category='success')
452 category='success')
480 except RepositoryError as e:
453 except RepositoryError as e:
481 log.exception("Repository Error occurred")
454 log.exception("Repository Error occurred")
482 h.flash(str(e), category='error')
455 h.flash(str(e), category='error')
483 except Exception as e:
456 except Exception as e:
484 log.exception("Exception while editing fork")
457 log.exception("Exception while editing fork")
485 h.flash(_('An error occurred during this operation'),
458 h.flash(_('An error occurred during this operation'),
486 category='error')
459 category='error')
487
460
488 return redirect(url('edit_repo_advanced', repo_name=repo_name))
461 return redirect(url('edit_repo_advanced', repo_name=repo_name))
489
462
490 @HasRepoPermissionAllDecorator('repository.admin')
463 @HasRepoPermissionAllDecorator('repository.admin')
491 @auth.CSRFRequired()
464 @auth.CSRFRequired()
492 def edit_advanced_locking(self, repo_name):
465 def edit_advanced_locking(self, repo_name):
493 """
466 """
494 Unlock repository when it is locked !
467 Unlock repository when it is locked !
495
468
496 :param repo_name:
469 :param repo_name:
497 """
470 """
498 try:
471 try:
499 repo = Repository.get_by_repo_name(repo_name)
472 repo = Repository.get_by_repo_name(repo_name)
500 if request.POST.get('set_lock'):
473 if request.POST.get('set_lock'):
501 Repository.lock(repo, c.rhodecode_user.user_id,
474 Repository.lock(repo, c.rhodecode_user.user_id,
502 lock_reason=Repository.LOCK_WEB)
475 lock_reason=Repository.LOCK_WEB)
503 h.flash(_('Locked repository'), category='success')
476 h.flash(_('Locked repository'), category='success')
504 elif request.POST.get('set_unlock'):
477 elif request.POST.get('set_unlock'):
505 Repository.unlock(repo)
478 Repository.unlock(repo)
506 h.flash(_('Unlocked repository'), category='success')
479 h.flash(_('Unlocked repository'), category='success')
507 except Exception as e:
480 except Exception as e:
508 log.exception("Exception during unlocking")
481 log.exception("Exception during unlocking")
509 h.flash(_('An error occurred during unlocking'),
482 h.flash(_('An error occurred during unlocking'),
510 category='error')
483 category='error')
511 return redirect(url('edit_repo_advanced', repo_name=repo_name))
484 return redirect(url('edit_repo_advanced', repo_name=repo_name))
512
485
513 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
486 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
514 @auth.CSRFRequired()
487 @auth.CSRFRequired()
515 def toggle_locking(self, repo_name):
488 def toggle_locking(self, repo_name):
516 """
489 """
517 Toggle locking of repository by simple GET call to url
490 Toggle locking of repository by simple GET call to url
518
491
519 :param repo_name:
492 :param repo_name:
520 """
493 """
521
494
522 try:
495 try:
523 repo = Repository.get_by_repo_name(repo_name)
496 repo = Repository.get_by_repo_name(repo_name)
524
497
525 if repo.enable_locking:
498 if repo.enable_locking:
526 if repo.locked[0]:
499 if repo.locked[0]:
527 Repository.unlock(repo)
500 Repository.unlock(repo)
528 action = _('Unlocked')
501 action = _('Unlocked')
529 else:
502 else:
530 Repository.lock(repo, c.rhodecode_user.user_id,
503 Repository.lock(repo, c.rhodecode_user.user_id,
531 lock_reason=Repository.LOCK_WEB)
504 lock_reason=Repository.LOCK_WEB)
532 action = _('Locked')
505 action = _('Locked')
533
506
534 h.flash(_('Repository has been %s') % action,
507 h.flash(_('Repository has been %s') % action,
535 category='success')
508 category='success')
536 except Exception:
509 except Exception:
537 log.exception("Exception during unlocking")
510 log.exception("Exception during unlocking")
538 h.flash(_('An error occurred during unlocking'),
511 h.flash(_('An error occurred during unlocking'),
539 category='error')
512 category='error')
540 return redirect(url('summary_home', repo_name=repo_name))
513 return redirect(url('summary_home', repo_name=repo_name))
541
514
542 @HasRepoPermissionAllDecorator('repository.admin')
515 @HasRepoPermissionAllDecorator('repository.admin')
543 @auth.CSRFRequired()
516 @auth.CSRFRequired()
544 def edit_remote(self, repo_name):
517 def edit_remote(self, repo_name):
545 """PUT /{repo_name}/settings/remote: edit the repo remote."""
518 """PUT /{repo_name}/settings/remote: edit the repo remote."""
546 try:
519 try:
547 ScmModel().pull_changes(repo_name, c.rhodecode_user.username)
520 ScmModel().pull_changes(repo_name, c.rhodecode_user.username)
548 h.flash(_('Pulled from remote location'), category='success')
521 h.flash(_('Pulled from remote location'), category='success')
549 except Exception:
522 except Exception:
550 log.exception("Exception during pull from remote")
523 log.exception("Exception during pull from remote")
551 h.flash(_('An error occurred during pull from remote location'),
524 h.flash(_('An error occurred during pull from remote location'),
552 category='error')
525 category='error')
553 return redirect(url('edit_repo_remote', repo_name=c.repo_name))
526 return redirect(url('edit_repo_remote', repo_name=c.repo_name))
554
527
555 @HasRepoPermissionAllDecorator('repository.admin')
528 @HasRepoPermissionAllDecorator('repository.admin')
556 def edit_remote_form(self, repo_name):
529 def edit_remote_form(self, repo_name):
557 """GET /repo_name/settings: Form to edit an existing item"""
530 """GET /repo_name/settings: Form to edit an existing item"""
558 c.repo_info = self._load_repo(repo_name)
531 c.repo_info = self._load_repo(repo_name)
559 c.active = 'remote'
532 c.active = 'remote'
560
533
561 return render('admin/repos/repo_edit.mako')
534 return render('admin/repos/repo_edit.mako')
562
535
563 @HasRepoPermissionAllDecorator('repository.admin')
536 @HasRepoPermissionAllDecorator('repository.admin')
564 @auth.CSRFRequired()
537 @auth.CSRFRequired()
565 def edit_statistics(self, repo_name):
538 def edit_statistics(self, repo_name):
566 """PUT /{repo_name}/settings/statistics: reset the repo statistics."""
539 """PUT /{repo_name}/settings/statistics: reset the repo statistics."""
567 try:
540 try:
568 RepoModel().delete_stats(repo_name)
541 RepoModel().delete_stats(repo_name)
569 Session().commit()
542 Session().commit()
570 except Exception as e:
543 except Exception as e:
571 log.error(traceback.format_exc())
544 log.error(traceback.format_exc())
572 h.flash(_('An error occurred during deletion of repository stats'),
545 h.flash(_('An error occurred during deletion of repository stats'),
573 category='error')
546 category='error')
574 return redirect(url('edit_repo_statistics', repo_name=c.repo_name))
547 return redirect(url('edit_repo_statistics', repo_name=c.repo_name))
575
548
576 @HasRepoPermissionAllDecorator('repository.admin')
549 @HasRepoPermissionAllDecorator('repository.admin')
577 def edit_statistics_form(self, repo_name):
550 def edit_statistics_form(self, repo_name):
578 """GET /repo_name/settings: Form to edit an existing item"""
551 """GET /repo_name/settings: Form to edit an existing item"""
579 c.repo_info = self._load_repo(repo_name)
552 c.repo_info = self._load_repo(repo_name)
580 repo = c.repo_info.scm_instance()
553 repo = c.repo_info.scm_instance()
581
554
582 if c.repo_info.stats:
555 if c.repo_info.stats:
583 # this is on what revision we ended up so we add +1 for count
556 # this is on what revision we ended up so we add +1 for count
584 last_rev = c.repo_info.stats.stat_on_revision + 1
557 last_rev = c.repo_info.stats.stat_on_revision + 1
585 else:
558 else:
586 last_rev = 0
559 last_rev = 0
587 c.stats_revision = last_rev
560 c.stats_revision = last_rev
588
561
589 c.repo_last_rev = repo.count()
562 c.repo_last_rev = repo.count()
590
563
591 if last_rev == 0 or c.repo_last_rev == 0:
564 if last_rev == 0 or c.repo_last_rev == 0:
592 c.stats_percentage = 0
565 c.stats_percentage = 0
593 else:
566 else:
594 c.stats_percentage = '%.2f' % ((float((last_rev)) / c.repo_last_rev) * 100)
567 c.stats_percentage = '%.2f' % ((float((last_rev)) / c.repo_last_rev) * 100)
595
568
596 c.active = 'statistics'
569 c.active = 'statistics'
597
570
598 return render('admin/repos/repo_edit.mako')
571 return render('admin/repos/repo_edit.mako')
599
572
600 @HasRepoPermissionAllDecorator('repository.admin')
573 @HasRepoPermissionAllDecorator('repository.admin')
601 @auth.CSRFRequired()
574 @auth.CSRFRequired()
602 def repo_issuetracker_test(self, repo_name):
575 def repo_issuetracker_test(self, repo_name):
603 if request.is_xhr:
576 if request.is_xhr:
604 return h.urlify_commit_message(
577 return h.urlify_commit_message(
605 request.POST.get('test_text', ''),
578 request.POST.get('test_text', ''),
606 repo_name)
579 repo_name)
607 else:
580 else:
608 raise HTTPBadRequest()
581 raise HTTPBadRequest()
609
582
610 @HasRepoPermissionAllDecorator('repository.admin')
583 @HasRepoPermissionAllDecorator('repository.admin')
611 @auth.CSRFRequired()
584 @auth.CSRFRequired()
612 def repo_issuetracker_delete(self, repo_name):
585 def repo_issuetracker_delete(self, repo_name):
613 uid = request.POST.get('uid')
586 uid = request.POST.get('uid')
614 repo_settings = IssueTrackerSettingsModel(repo=repo_name)
587 repo_settings = IssueTrackerSettingsModel(repo=repo_name)
615 try:
588 try:
616 repo_settings.delete_entries(uid)
589 repo_settings.delete_entries(uid)
617 except Exception:
590 except Exception:
618 h.flash(_('Error occurred during deleting issue tracker entry'),
591 h.flash(_('Error occurred during deleting issue tracker entry'),
619 category='error')
592 category='error')
620 else:
593 else:
621 h.flash(_('Removed issue tracker entry'), category='success')
594 h.flash(_('Removed issue tracker entry'), category='success')
622 return redirect(url('repo_settings_issuetracker',
595 return redirect(url('repo_settings_issuetracker',
623 repo_name=repo_name))
596 repo_name=repo_name))
624
597
625 def _update_patterns(self, form, repo_settings):
598 def _update_patterns(self, form, repo_settings):
626 for uid in form['delete_patterns']:
599 for uid in form['delete_patterns']:
627 repo_settings.delete_entries(uid)
600 repo_settings.delete_entries(uid)
628
601
629 for pattern in form['patterns']:
602 for pattern in form['patterns']:
630 for setting, value, type_ in pattern:
603 for setting, value, type_ in pattern:
631 sett = repo_settings.create_or_update_setting(
604 sett = repo_settings.create_or_update_setting(
632 setting, value, type_)
605 setting, value, type_)
633 Session().add(sett)
606 Session().add(sett)
634
607
635 Session().commit()
608 Session().commit()
636
609
637 @HasRepoPermissionAllDecorator('repository.admin')
610 @HasRepoPermissionAllDecorator('repository.admin')
638 @auth.CSRFRequired()
611 @auth.CSRFRequired()
639 def repo_issuetracker_save(self, repo_name):
612 def repo_issuetracker_save(self, repo_name):
640 # Save inheritance
613 # Save inheritance
641 repo_settings = IssueTrackerSettingsModel(repo=repo_name)
614 repo_settings = IssueTrackerSettingsModel(repo=repo_name)
642 inherited = (request.POST.get('inherit_global_issuetracker')
615 inherited = (request.POST.get('inherit_global_issuetracker')
643 == "inherited")
616 == "inherited")
644 repo_settings.inherit_global_settings = inherited
617 repo_settings.inherit_global_settings = inherited
645 Session().commit()
618 Session().commit()
646
619
647 form = IssueTrackerPatternsForm()().to_python(request.POST)
620 form = IssueTrackerPatternsForm()().to_python(request.POST)
648 if form:
621 if form:
649 self._update_patterns(form, repo_settings)
622 self._update_patterns(form, repo_settings)
650
623
651 h.flash(_('Updated issue tracker entries'), category='success')
624 h.flash(_('Updated issue tracker entries'), category='success')
652 return redirect(url('repo_settings_issuetracker',
625 return redirect(url('repo_settings_issuetracker',
653 repo_name=repo_name))
626 repo_name=repo_name))
654
627
655 @HasRepoPermissionAllDecorator('repository.admin')
628 @HasRepoPermissionAllDecorator('repository.admin')
656 def repo_issuetracker(self, repo_name):
629 def repo_issuetracker(self, repo_name):
657 """GET /admin/settings/issue-tracker: All items in the collection"""
630 """GET /admin/settings/issue-tracker: All items in the collection"""
658 c.active = 'issuetracker'
631 c.active = 'issuetracker'
659 c.data = 'data'
632 c.data = 'data'
660 c.repo_info = self._load_repo(repo_name)
633 c.repo_info = self._load_repo(repo_name)
661
634
662 repo = Repository.get_by_repo_name(repo_name)
635 repo = Repository.get_by_repo_name(repo_name)
663 c.settings_model = IssueTrackerSettingsModel(repo=repo)
636 c.settings_model = IssueTrackerSettingsModel(repo=repo)
664 c.global_patterns = c.settings_model.get_global_settings()
637 c.global_patterns = c.settings_model.get_global_settings()
665 c.repo_patterns = c.settings_model.get_repo_settings()
638 c.repo_patterns = c.settings_model.get_repo_settings()
666
639
667 return render('admin/repos/repo_edit.mako')
640 return render('admin/repos/repo_edit.mako')
668
641
669 @HasRepoPermissionAllDecorator('repository.admin')
642 @HasRepoPermissionAllDecorator('repository.admin')
670 def repo_settings_vcs(self, repo_name):
643 def repo_settings_vcs(self, repo_name):
671 """GET /{repo_name}/settings/vcs/: All items in the collection"""
644 """GET /{repo_name}/settings/vcs/: All items in the collection"""
672
645
673 model = VcsSettingsModel(repo=repo_name)
646 model = VcsSettingsModel(repo=repo_name)
674
647
675 c.active = 'vcs'
648 c.active = 'vcs'
676 c.global_svn_branch_patterns = model.get_global_svn_branch_patterns()
649 c.global_svn_branch_patterns = model.get_global_svn_branch_patterns()
677 c.global_svn_tag_patterns = model.get_global_svn_tag_patterns()
650 c.global_svn_tag_patterns = model.get_global_svn_tag_patterns()
678 c.svn_branch_patterns = model.get_repo_svn_branch_patterns()
651 c.svn_branch_patterns = model.get_repo_svn_branch_patterns()
679 c.svn_tag_patterns = model.get_repo_svn_tag_patterns()
652 c.svn_tag_patterns = model.get_repo_svn_tag_patterns()
680 c.repo_info = self._load_repo(repo_name)
653 c.repo_info = self._load_repo(repo_name)
681 defaults = self._vcs_form_defaults(repo_name)
654 defaults = self._vcs_form_defaults(repo_name)
682 c.inherit_global_settings = defaults['inherit_global_settings']
655 c.inherit_global_settings = defaults['inherit_global_settings']
683 c.labs_active = str2bool(
656 c.labs_active = str2bool(
684 rhodecode.CONFIG.get('labs_settings_active', 'true'))
657 rhodecode.CONFIG.get('labs_settings_active', 'true'))
685
658
686 return htmlfill.render(
659 return htmlfill.render(
687 render('admin/repos/repo_edit.mako'),
660 render('admin/repos/repo_edit.mako'),
688 defaults=defaults,
661 defaults=defaults,
689 encoding="UTF-8",
662 encoding="UTF-8",
690 force_defaults=False)
663 force_defaults=False)
691
664
692 @HasRepoPermissionAllDecorator('repository.admin')
665 @HasRepoPermissionAllDecorator('repository.admin')
693 @auth.CSRFRequired()
666 @auth.CSRFRequired()
694 def repo_settings_vcs_update(self, repo_name):
667 def repo_settings_vcs_update(self, repo_name):
695 """POST /{repo_name}/settings/vcs/: All items in the collection"""
668 """POST /{repo_name}/settings/vcs/: All items in the collection"""
696 c.active = 'vcs'
669 c.active = 'vcs'
697
670
698 model = VcsSettingsModel(repo=repo_name)
671 model = VcsSettingsModel(repo=repo_name)
699 c.global_svn_branch_patterns = model.get_global_svn_branch_patterns()
672 c.global_svn_branch_patterns = model.get_global_svn_branch_patterns()
700 c.global_svn_tag_patterns = model.get_global_svn_tag_patterns()
673 c.global_svn_tag_patterns = model.get_global_svn_tag_patterns()
701 c.svn_branch_patterns = model.get_repo_svn_branch_patterns()
674 c.svn_branch_patterns = model.get_repo_svn_branch_patterns()
702 c.svn_tag_patterns = model.get_repo_svn_tag_patterns()
675 c.svn_tag_patterns = model.get_repo_svn_tag_patterns()
703 c.repo_info = self._load_repo(repo_name)
676 c.repo_info = self._load_repo(repo_name)
704 defaults = self._vcs_form_defaults(repo_name)
677 defaults = self._vcs_form_defaults(repo_name)
705 c.inherit_global_settings = defaults['inherit_global_settings']
678 c.inherit_global_settings = defaults['inherit_global_settings']
706
679
707 application_form = RepoVcsSettingsForm(repo_name)()
680 application_form = RepoVcsSettingsForm(repo_name)()
708 try:
681 try:
709 form_result = application_form.to_python(dict(request.POST))
682 form_result = application_form.to_python(dict(request.POST))
710 except formencode.Invalid as errors:
683 except formencode.Invalid as errors:
711 h.flash(
684 h.flash(
712 _("Some form inputs contain invalid data."),
685 _("Some form inputs contain invalid data."),
713 category='error')
686 category='error')
714 return htmlfill.render(
687 return htmlfill.render(
715 render('admin/repos/repo_edit.mako'),
688 render('admin/repos/repo_edit.mako'),
716 defaults=errors.value,
689 defaults=errors.value,
717 errors=errors.error_dict or {},
690 errors=errors.error_dict or {},
718 prefix_error=False,
691 prefix_error=False,
719 encoding="UTF-8",
692 encoding="UTF-8",
720 force_defaults=False
693 force_defaults=False
721 )
694 )
722
695
723 try:
696 try:
724 inherit_global_settings = form_result['inherit_global_settings']
697 inherit_global_settings = form_result['inherit_global_settings']
725 model.create_or_update_repo_settings(
698 model.create_or_update_repo_settings(
726 form_result, inherit_global_settings=inherit_global_settings)
699 form_result, inherit_global_settings=inherit_global_settings)
727 except Exception:
700 except Exception:
728 log.exception("Exception while updating settings")
701 log.exception("Exception while updating settings")
729 h.flash(
702 h.flash(
730 _('Error occurred during updating repository VCS settings'),
703 _('Error occurred during updating repository VCS settings'),
731 category='error')
704 category='error')
732 else:
705 else:
733 Session().commit()
706 Session().commit()
734 h.flash(_('Updated VCS settings'), category='success')
707 h.flash(_('Updated VCS settings'), category='success')
735 return redirect(url('repo_vcs_settings', repo_name=repo_name))
708 return redirect(url('repo_vcs_settings', repo_name=repo_name))
736
709
737 return htmlfill.render(
710 return htmlfill.render(
738 render('admin/repos/repo_edit.mako'),
711 render('admin/repos/repo_edit.mako'),
739 defaults=self._vcs_form_defaults(repo_name),
712 defaults=self._vcs_form_defaults(repo_name),
740 encoding="UTF-8",
713 encoding="UTF-8",
741 force_defaults=False)
714 force_defaults=False)
742
715
743 @HasRepoPermissionAllDecorator('repository.admin')
716 @HasRepoPermissionAllDecorator('repository.admin')
744 @auth.CSRFRequired()
717 @auth.CSRFRequired()
745 @jsonify
718 @jsonify
746 def repo_delete_svn_pattern(self, repo_name):
719 def repo_delete_svn_pattern(self, repo_name):
747 if not request.is_xhr:
720 if not request.is_xhr:
748 return False
721 return False
749
722
750 delete_pattern_id = request.POST.get('delete_svn_pattern')
723 delete_pattern_id = request.POST.get('delete_svn_pattern')
751 model = VcsSettingsModel(repo=repo_name)
724 model = VcsSettingsModel(repo=repo_name)
752 try:
725 try:
753 model.delete_repo_svn_pattern(delete_pattern_id)
726 model.delete_repo_svn_pattern(delete_pattern_id)
754 except SettingNotFound:
727 except SettingNotFound:
755 raise HTTPBadRequest()
728 raise HTTPBadRequest()
756
729
757 Session().commit()
730 Session().commit()
758 return True
731 return True
759
732
760 def _vcs_form_defaults(self, repo_name):
733 def _vcs_form_defaults(self, repo_name):
761 model = VcsSettingsModel(repo=repo_name)
734 model = VcsSettingsModel(repo=repo_name)
762 global_defaults = model.get_global_settings()
735 global_defaults = model.get_global_settings()
763
736
764 repo_defaults = {}
737 repo_defaults = {}
765 repo_defaults.update(global_defaults)
738 repo_defaults.update(global_defaults)
766 repo_defaults.update(model.get_repo_settings())
739 repo_defaults.update(model.get_repo_settings())
767
740
768 global_defaults = {
741 global_defaults = {
769 '{}_inherited'.format(k): global_defaults[k]
742 '{}_inherited'.format(k): global_defaults[k]
770 for k in global_defaults}
743 for k in global_defaults}
771
744
772 defaults = {
745 defaults = {
773 'inherit_global_settings': model.inherit_global_settings
746 'inherit_global_settings': model.inherit_global_settings
774 }
747 }
775 defaults.update(global_defaults)
748 defaults.update(global_defaults)
776 defaults.update(repo_defaults)
749 defaults.update(repo_defaults)
777 defaults.update({
750 defaults.update({
778 'new_svn_branch': '',
751 'new_svn_branch': '',
779 'new_svn_tag': '',
752 'new_svn_tag': '',
780 })
753 })
781 return defaults
754 return defaults
@@ -1,147 +1,148 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2017-2017 RhodeCode GmbH
3 # Copyright (C) 2017-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22 import datetime
22 import datetime
23
23
24 from rhodecode.model import meta
24 from rhodecode.model import meta
25 from rhodecode.model.db import User, UserLog
25 from rhodecode.model.db import User, UserLog
26
26
27
27
28 log = logging.getLogger(__name__)
28 log = logging.getLogger(__name__)
29
29
30
30
31 ACTIONS = {
31 ACTIONS = {
32 'user.login.success': {},
32 'user.login.success': {},
33 'user.login.failure': {},
33 'user.login.failure': {},
34 'user.logout': {},
34 'user.logout': {},
35 'user.password.reset_request': {},
35 'user.password.reset_request': {},
36
36
37 'repo.add': {},
37 'repo.add': {},
38 'repo.edit': {},
38 'repo.edit': {},
39 'repo.edit.permissions': {},
39 'repo.commit.strip': {}
40 'repo.commit.strip': {}
40 }
41 }
41
42
42
43
43 class UserWrap(object):
44 class UserWrap(object):
44 """
45 """
45 Fake object used to imitate AuthUser
46 Fake object used to imitate AuthUser
46 """
47 """
47
48
48 def __init__(self, user_id=None, username=None, ip_addr=None):
49 def __init__(self, user_id=None, username=None, ip_addr=None):
49 self.user_id = user_id
50 self.user_id = user_id
50 self.username = username
51 self.username = username
51 self.ip_addr = ip_addr
52 self.ip_addr = ip_addr
52
53
53
54
54 def _store_log(action_name, action_data, user_id, username, user_data,
55 def _store_log(action_name, action_data, user_id, username, user_data,
55 ip_address, repository_id, repository_name):
56 ip_address, repository_id, repository_name):
56 user_log = UserLog()
57 user_log = UserLog()
57 user_log.version = UserLog.VERSION_2
58 user_log.version = UserLog.VERSION_2
58
59
59 user_log.action = action_name
60 user_log.action = action_name
60 user_log.action_data = action_data
61 user_log.action_data = action_data
61
62
62 user_log.user_ip = ip_address
63 user_log.user_ip = ip_address
63
64
64 user_log.user_id = user_id
65 user_log.user_id = user_id
65 user_log.username = username
66 user_log.username = username
66 user_log.user_data = user_data
67 user_log.user_data = user_data
67
68
68 user_log.repository_id = repository_id
69 user_log.repository_id = repository_id
69 user_log.repository_name = repository_name
70 user_log.repository_name = repository_name
70
71
71 user_log.action_date = datetime.datetime.now()
72 user_log.action_date = datetime.datetime.now()
72
73
73 log.info('AUDIT: Logging action: `%s` by user:id:%s[%s] ip:%s',
74 log.info('AUDIT: Logging action: `%s` by user:id:%s[%s] ip:%s',
74 action_name, user_id, username, ip_address)
75 action_name, user_id, username, ip_address)
75
76
76 return user_log
77 return user_log
77
78
78
79
79 def store(
80 def store(
80 action, user, action_data=None, user_data=None, ip_addr=None,
81 action, user, action_data=None, user_data=None, ip_addr=None,
81 repo=None, sa_session=None, commit=False):
82 repo=None, sa_session=None, commit=False):
82 """
83 """
83 Audit logger for various actions made by users, typically this results in a call such::
84 Audit logger for various actions made by users, typically this results in a call such::
84
85
85 from rhodecode.lib import audit_logger
86 from rhodecode.lib import audit_logger
86
87
87 audit_logger.store(action='repo.edit', user=self._rhodecode_user)
88 audit_logger.store(action='repo.edit', user=self._rhodecode_user)
88 audit_logger.store(action='repo.delete', user=audit_logger.UserWrap(username='itried-to-login', ip_addr='8.8.8.8'))
89 audit_logger.store(action='repo.delete', user=audit_logger.UserWrap(username='itried-to-login', ip_addr='8.8.8.8'))
89
90
90 # without an user ?
91 # without an user ?
91 audit_user = audit_logger.UserWrap(
92 audit_user = audit_logger.UserWrap(
92 username=self.request.params.get('username'),
93 username=self.request.params.get('username'),
93 ip_addr=self.request.remote_addr)
94 ip_addr=self.request.remote_addr)
94 audit_logger.store(action='user.login.failure', user=audit_user)
95 audit_logger.store(action='user.login.failure', user=audit_user)
95 """
96 """
96 from rhodecode.lib.utils2 import safe_unicode
97 from rhodecode.lib.utils2 import safe_unicode
97 from rhodecode.lib.auth import AuthUser
98 from rhodecode.lib.auth import AuthUser
98
99
99 if action not in ACTIONS:
100 if action not in ACTIONS:
100 raise ValueError('Action `{}` not in valid actions'.format(action))
101 raise ValueError('Action `{}` not in valid actions'.format(action))
101
102
102 if not sa_session:
103 if not sa_session:
103 sa_session = meta.Session()
104 sa_session = meta.Session()
104
105
105 try:
106 try:
106 username = getattr(user, 'username', None)
107 username = getattr(user, 'username', None)
107 if not username:
108 if not username:
108 pass
109 pass
109
110
110 user_id = getattr(user, 'user_id', None)
111 user_id = getattr(user, 'user_id', None)
111 if not user_id:
112 if not user_id:
112 # maybe we have username ? Try to figure user_id from username
113 # maybe we have username ? Try to figure user_id from username
113 if username:
114 if username:
114 user_id = getattr(
115 user_id = getattr(
115 User.get_by_username(username), 'user_id', None)
116 User.get_by_username(username), 'user_id', None)
116
117
117 ip_addr = ip_addr or getattr(user, 'ip_addr', None)
118 ip_addr = ip_addr or getattr(user, 'ip_addr', None)
118 if not ip_addr:
119 if not ip_addr:
119 pass
120 pass
120
121
121 if not user_data:
122 if not user_data:
122 # try to get this from the auth user
123 # try to get this from the auth user
123 if isinstance(user, AuthUser):
124 if isinstance(user, AuthUser):
124 user_data = {
125 user_data = {
125 'username': user.username,
126 'username': user.username,
126 'email': user.email,
127 'email': user.email,
127 }
128 }
128
129
129 repository_id = getattr(repo, 'repo_id', None)
130 repository_id = getattr(repo, 'repo_id', None)
130 repository_name = getattr(repo, 'repo_name', None)
131 repository_name = getattr(repo, 'repo_name', None)
131
132
132 user_log = _store_log(
133 user_log = _store_log(
133 action_name=safe_unicode(action),
134 action_name=safe_unicode(action),
134 action_data=action_data or {},
135 action_data=action_data or {},
135 user_id=user_id,
136 user_id=user_id,
136 username=username,
137 username=username,
137 user_data=user_data or {},
138 user_data=user_data or {},
138 ip_address=safe_unicode(ip_addr),
139 ip_address=safe_unicode(ip_addr),
139 repository_id=repository_id,
140 repository_id=repository_id,
140 repository_name=repository_name
141 repository_name=repository_name
141 )
142 )
142 sa_session.add(user_log)
143 sa_session.add(user_log)
143 if commit:
144 if commit:
144 sa_session.commit()
145 sa_session.commit()
145
146
146 except Exception:
147 except Exception:
147 log.exception('AUDIT: failed to store audit log')
148 log.exception('AUDIT: failed to store audit log')
@@ -1,993 +1,999 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 Repository model for rhodecode
22 Repository model for rhodecode
23 """
23 """
24
24
25 import logging
25 import logging
26 import os
26 import os
27 import re
27 import re
28 import shutil
28 import shutil
29 import time
29 import time
30 import traceback
30 import traceback
31 from datetime import datetime, timedelta
31 from datetime import datetime, timedelta
32
32
33 from zope.cachedescriptors.property import Lazy as LazyProperty
33 from zope.cachedescriptors.property import Lazy as LazyProperty
34
34
35 from rhodecode import events
35 from rhodecode import events
36 from rhodecode.lib import helpers as h
36 from rhodecode.lib import helpers as h
37 from rhodecode.lib.auth import HasUserGroupPermissionAny
37 from rhodecode.lib.auth import HasUserGroupPermissionAny
38 from rhodecode.lib.caching_query import FromCache
38 from rhodecode.lib.caching_query import FromCache
39 from rhodecode.lib.exceptions import AttachedForksError
39 from rhodecode.lib.exceptions import AttachedForksError
40 from rhodecode.lib.hooks_base import log_delete_repository
40 from rhodecode.lib.hooks_base import log_delete_repository
41 from rhodecode.lib.utils import make_db_config
41 from rhodecode.lib.utils import make_db_config
42 from rhodecode.lib.utils2 import (
42 from rhodecode.lib.utils2 import (
43 safe_str, safe_unicode, remove_prefix, obfuscate_url_pw,
43 safe_str, safe_unicode, remove_prefix, obfuscate_url_pw,
44 get_current_rhodecode_user, safe_int, datetime_to_time, action_logger_generic)
44 get_current_rhodecode_user, safe_int, datetime_to_time, action_logger_generic)
45 from rhodecode.lib.vcs.backends import get_backend
45 from rhodecode.lib.vcs.backends import get_backend
46 from rhodecode.model import BaseModel
46 from rhodecode.model import BaseModel
47 from rhodecode.model.db import (
47 from rhodecode.model.db import (
48 Repository, UserRepoToPerm, UserGroupRepoToPerm, UserRepoGroupToPerm,
48 Repository, UserRepoToPerm, UserGroupRepoToPerm, UserRepoGroupToPerm,
49 UserGroupRepoGroupToPerm, User, Permission, Statistics, UserGroup,
49 UserGroupRepoGroupToPerm, User, Permission, Statistics, UserGroup,
50 RepoGroup, RepositoryField)
50 RepoGroup, RepositoryField)
51
51
52 from rhodecode.model.settings import VcsSettingsModel
52 from rhodecode.model.settings import VcsSettingsModel
53
53
54
54
55 log = logging.getLogger(__name__)
55 log = logging.getLogger(__name__)
56
56
57
57
58 class RepoModel(BaseModel):
58 class RepoModel(BaseModel):
59
59
60 cls = Repository
60 cls = Repository
61
61
62 def _get_user_group(self, users_group):
62 def _get_user_group(self, users_group):
63 return self._get_instance(UserGroup, users_group,
63 return self._get_instance(UserGroup, users_group,
64 callback=UserGroup.get_by_group_name)
64 callback=UserGroup.get_by_group_name)
65
65
66 def _get_repo_group(self, repo_group):
66 def _get_repo_group(self, repo_group):
67 return self._get_instance(RepoGroup, repo_group,
67 return self._get_instance(RepoGroup, repo_group,
68 callback=RepoGroup.get_by_group_name)
68 callback=RepoGroup.get_by_group_name)
69
69
70 def _create_default_perms(self, repository, private):
70 def _create_default_perms(self, repository, private):
71 # create default permission
71 # create default permission
72 default = 'repository.read'
72 default = 'repository.read'
73 def_user = User.get_default_user()
73 def_user = User.get_default_user()
74 for p in def_user.user_perms:
74 for p in def_user.user_perms:
75 if p.permission.permission_name.startswith('repository.'):
75 if p.permission.permission_name.startswith('repository.'):
76 default = p.permission.permission_name
76 default = p.permission.permission_name
77 break
77 break
78
78
79 default_perm = 'repository.none' if private else default
79 default_perm = 'repository.none' if private else default
80
80
81 repo_to_perm = UserRepoToPerm()
81 repo_to_perm = UserRepoToPerm()
82 repo_to_perm.permission = Permission.get_by_key(default_perm)
82 repo_to_perm.permission = Permission.get_by_key(default_perm)
83
83
84 repo_to_perm.repository = repository
84 repo_to_perm.repository = repository
85 repo_to_perm.user_id = def_user.user_id
85 repo_to_perm.user_id = def_user.user_id
86
86
87 return repo_to_perm
87 return repo_to_perm
88
88
89 @LazyProperty
89 @LazyProperty
90 def repos_path(self):
90 def repos_path(self):
91 """
91 """
92 Gets the repositories root path from database
92 Gets the repositories root path from database
93 """
93 """
94 settings_model = VcsSettingsModel(sa=self.sa)
94 settings_model = VcsSettingsModel(sa=self.sa)
95 return settings_model.get_repos_location()
95 return settings_model.get_repos_location()
96
96
97 def get(self, repo_id, cache=False):
97 def get(self, repo_id, cache=False):
98 repo = self.sa.query(Repository) \
98 repo = self.sa.query(Repository) \
99 .filter(Repository.repo_id == repo_id)
99 .filter(Repository.repo_id == repo_id)
100
100
101 if cache:
101 if cache:
102 repo = repo.options(FromCache("sql_cache_short",
102 repo = repo.options(FromCache("sql_cache_short",
103 "get_repo_%s" % repo_id))
103 "get_repo_%s" % repo_id))
104 return repo.scalar()
104 return repo.scalar()
105
105
106 def get_repo(self, repository):
106 def get_repo(self, repository):
107 return self._get_repo(repository)
107 return self._get_repo(repository)
108
108
109 def get_by_repo_name(self, repo_name, cache=False):
109 def get_by_repo_name(self, repo_name, cache=False):
110 repo = self.sa.query(Repository) \
110 repo = self.sa.query(Repository) \
111 .filter(Repository.repo_name == repo_name)
111 .filter(Repository.repo_name == repo_name)
112
112
113 if cache:
113 if cache:
114 repo = repo.options(FromCache("sql_cache_short",
114 repo = repo.options(FromCache("sql_cache_short",
115 "get_repo_%s" % repo_name))
115 "get_repo_%s" % repo_name))
116 return repo.scalar()
116 return repo.scalar()
117
117
118 def _extract_id_from_repo_name(self, repo_name):
118 def _extract_id_from_repo_name(self, repo_name):
119 if repo_name.startswith('/'):
119 if repo_name.startswith('/'):
120 repo_name = repo_name.lstrip('/')
120 repo_name = repo_name.lstrip('/')
121 by_id_match = re.match(r'^_(\d{1,})', repo_name)
121 by_id_match = re.match(r'^_(\d{1,})', repo_name)
122 if by_id_match:
122 if by_id_match:
123 return by_id_match.groups()[0]
123 return by_id_match.groups()[0]
124
124
125 def get_repo_by_id(self, repo_name):
125 def get_repo_by_id(self, repo_name):
126 """
126 """
127 Extracts repo_name by id from special urls.
127 Extracts repo_name by id from special urls.
128 Example url is _11/repo_name
128 Example url is _11/repo_name
129
129
130 :param repo_name:
130 :param repo_name:
131 :return: repo object if matched else None
131 :return: repo object if matched else None
132 """
132 """
133 try:
133 try:
134 _repo_id = self._extract_id_from_repo_name(repo_name)
134 _repo_id = self._extract_id_from_repo_name(repo_name)
135 if _repo_id:
135 if _repo_id:
136 return self.get(_repo_id)
136 return self.get(_repo_id)
137 except Exception:
137 except Exception:
138 log.exception('Failed to extract repo_name from URL')
138 log.exception('Failed to extract repo_name from URL')
139
139
140 return None
140 return None
141
141
142 def get_repos_for_root(self, root, traverse=False):
142 def get_repos_for_root(self, root, traverse=False):
143 if traverse:
143 if traverse:
144 like_expression = u'{}%'.format(safe_unicode(root))
144 like_expression = u'{}%'.format(safe_unicode(root))
145 repos = Repository.query().filter(
145 repos = Repository.query().filter(
146 Repository.repo_name.like(like_expression)).all()
146 Repository.repo_name.like(like_expression)).all()
147 else:
147 else:
148 if root and not isinstance(root, RepoGroup):
148 if root and not isinstance(root, RepoGroup):
149 raise ValueError(
149 raise ValueError(
150 'Root must be an instance '
150 'Root must be an instance '
151 'of RepoGroup, got:{} instead'.format(type(root)))
151 'of RepoGroup, got:{} instead'.format(type(root)))
152 repos = Repository.query().filter(Repository.group == root).all()
152 repos = Repository.query().filter(Repository.group == root).all()
153 return repos
153 return repos
154
154
155 def get_url(self, repo):
155 def get_url(self, repo):
156 return h.url('summary_home', repo_name=safe_str(repo.repo_name),
156 return h.url('summary_home', repo_name=safe_str(repo.repo_name),
157 qualified=True)
157 qualified=True)
158
158
159 @classmethod
159 @classmethod
160 def update_repoinfo(cls, repositories=None):
160 def update_repoinfo(cls, repositories=None):
161 if not repositories:
161 if not repositories:
162 repositories = Repository.getAll()
162 repositories = Repository.getAll()
163 for repo in repositories:
163 for repo in repositories:
164 repo.update_commit_cache()
164 repo.update_commit_cache()
165
165
166 def get_repos_as_dict(self, repo_list=None, admin=False,
166 def get_repos_as_dict(self, repo_list=None, admin=False,
167 super_user_actions=False):
167 super_user_actions=False):
168
168
169 from rhodecode.lib.utils import PartialRenderer
169 from rhodecode.lib.utils import PartialRenderer
170 _render = PartialRenderer('data_table/_dt_elements.mako')
170 _render = PartialRenderer('data_table/_dt_elements.mako')
171 c = _render.c
171 c = _render.c
172
172
173 def quick_menu(repo_name):
173 def quick_menu(repo_name):
174 return _render('quick_menu', repo_name)
174 return _render('quick_menu', repo_name)
175
175
176 def repo_lnk(name, rtype, rstate, private, fork_of):
176 def repo_lnk(name, rtype, rstate, private, fork_of):
177 return _render('repo_name', name, rtype, rstate, private, fork_of,
177 return _render('repo_name', name, rtype, rstate, private, fork_of,
178 short_name=not admin, admin=False)
178 short_name=not admin, admin=False)
179
179
180 def last_change(last_change):
180 def last_change(last_change):
181 if admin and isinstance(last_change, datetime) and not last_change.tzinfo:
181 if admin and isinstance(last_change, datetime) and not last_change.tzinfo:
182 last_change = last_change + timedelta(seconds=
182 last_change = last_change + timedelta(seconds=
183 (datetime.now() - datetime.utcnow()).seconds)
183 (datetime.now() - datetime.utcnow()).seconds)
184 return _render("last_change", last_change)
184 return _render("last_change", last_change)
185
185
186 def rss_lnk(repo_name):
186 def rss_lnk(repo_name):
187 return _render("rss", repo_name)
187 return _render("rss", repo_name)
188
188
189 def atom_lnk(repo_name):
189 def atom_lnk(repo_name):
190 return _render("atom", repo_name)
190 return _render("atom", repo_name)
191
191
192 def last_rev(repo_name, cs_cache):
192 def last_rev(repo_name, cs_cache):
193 return _render('revision', repo_name, cs_cache.get('revision'),
193 return _render('revision', repo_name, cs_cache.get('revision'),
194 cs_cache.get('raw_id'), cs_cache.get('author'),
194 cs_cache.get('raw_id'), cs_cache.get('author'),
195 cs_cache.get('message'))
195 cs_cache.get('message'))
196
196
197 def desc(desc):
197 def desc(desc):
198 if c.visual.stylify_metatags:
198 if c.visual.stylify_metatags:
199 desc = h.urlify_text(h.escaped_stylize(desc))
199 desc = h.urlify_text(h.escaped_stylize(desc))
200 else:
200 else:
201 desc = h.urlify_text(h.html_escape(desc))
201 desc = h.urlify_text(h.html_escape(desc))
202
202
203 return _render('repo_desc', desc)
203 return _render('repo_desc', desc)
204
204
205 def state(repo_state):
205 def state(repo_state):
206 return _render("repo_state", repo_state)
206 return _render("repo_state", repo_state)
207
207
208 def repo_actions(repo_name):
208 def repo_actions(repo_name):
209 return _render('repo_actions', repo_name, super_user_actions)
209 return _render('repo_actions', repo_name, super_user_actions)
210
210
211 def user_profile(username):
211 def user_profile(username):
212 return _render('user_profile', username)
212 return _render('user_profile', username)
213
213
214 repos_data = []
214 repos_data = []
215 for repo in repo_list:
215 for repo in repo_list:
216 cs_cache = repo.changeset_cache
216 cs_cache = repo.changeset_cache
217 row = {
217 row = {
218 "menu": quick_menu(repo.repo_name),
218 "menu": quick_menu(repo.repo_name),
219
219
220 "name": repo_lnk(repo.repo_name, repo.repo_type,
220 "name": repo_lnk(repo.repo_name, repo.repo_type,
221 repo.repo_state, repo.private, repo.fork),
221 repo.repo_state, repo.private, repo.fork),
222 "name_raw": repo.repo_name.lower(),
222 "name_raw": repo.repo_name.lower(),
223
223
224 "last_change": last_change(repo.last_db_change),
224 "last_change": last_change(repo.last_db_change),
225 "last_change_raw": datetime_to_time(repo.last_db_change),
225 "last_change_raw": datetime_to_time(repo.last_db_change),
226
226
227 "last_changeset": last_rev(repo.repo_name, cs_cache),
227 "last_changeset": last_rev(repo.repo_name, cs_cache),
228 "last_changeset_raw": cs_cache.get('revision'),
228 "last_changeset_raw": cs_cache.get('revision'),
229
229
230 "desc": desc(repo.description),
230 "desc": desc(repo.description),
231 "owner": user_profile(repo.user.username),
231 "owner": user_profile(repo.user.username),
232
232
233 "state": state(repo.repo_state),
233 "state": state(repo.repo_state),
234 "rss": rss_lnk(repo.repo_name),
234 "rss": rss_lnk(repo.repo_name),
235
235
236 "atom": atom_lnk(repo.repo_name),
236 "atom": atom_lnk(repo.repo_name),
237 }
237 }
238 if admin:
238 if admin:
239 row.update({
239 row.update({
240 "action": repo_actions(repo.repo_name),
240 "action": repo_actions(repo.repo_name),
241 })
241 })
242 repos_data.append(row)
242 repos_data.append(row)
243
243
244 return repos_data
244 return repos_data
245
245
246 def _get_defaults(self, repo_name):
246 def _get_defaults(self, repo_name):
247 """
247 """
248 Gets information about repository, and returns a dict for
248 Gets information about repository, and returns a dict for
249 usage in forms
249 usage in forms
250
250
251 :param repo_name:
251 :param repo_name:
252 """
252 """
253
253
254 repo_info = Repository.get_by_repo_name(repo_name)
254 repo_info = Repository.get_by_repo_name(repo_name)
255
255
256 if repo_info is None:
256 if repo_info is None:
257 return None
257 return None
258
258
259 defaults = repo_info.get_dict()
259 defaults = repo_info.get_dict()
260 defaults['repo_name'] = repo_info.just_name
260 defaults['repo_name'] = repo_info.just_name
261
261
262 groups = repo_info.groups_with_parents
262 groups = repo_info.groups_with_parents
263 parent_group = groups[-1] if groups else None
263 parent_group = groups[-1] if groups else None
264
264
265 # we use -1 as this is how in HTML, we mark an empty group
265 # we use -1 as this is how in HTML, we mark an empty group
266 defaults['repo_group'] = getattr(parent_group, 'group_id', -1)
266 defaults['repo_group'] = getattr(parent_group, 'group_id', -1)
267
267
268 keys_to_process = (
268 keys_to_process = (
269 {'k': 'repo_type', 'strip': False},
269 {'k': 'repo_type', 'strip': False},
270 {'k': 'repo_enable_downloads', 'strip': True},
270 {'k': 'repo_enable_downloads', 'strip': True},
271 {'k': 'repo_description', 'strip': True},
271 {'k': 'repo_description', 'strip': True},
272 {'k': 'repo_enable_locking', 'strip': True},
272 {'k': 'repo_enable_locking', 'strip': True},
273 {'k': 'repo_landing_rev', 'strip': True},
273 {'k': 'repo_landing_rev', 'strip': True},
274 {'k': 'clone_uri', 'strip': False},
274 {'k': 'clone_uri', 'strip': False},
275 {'k': 'repo_private', 'strip': True},
275 {'k': 'repo_private', 'strip': True},
276 {'k': 'repo_enable_statistics', 'strip': True}
276 {'k': 'repo_enable_statistics', 'strip': True}
277 )
277 )
278
278
279 for item in keys_to_process:
279 for item in keys_to_process:
280 attr = item['k']
280 attr = item['k']
281 if item['strip']:
281 if item['strip']:
282 attr = remove_prefix(item['k'], 'repo_')
282 attr = remove_prefix(item['k'], 'repo_')
283
283
284 val = defaults[attr]
284 val = defaults[attr]
285 if item['k'] == 'repo_landing_rev':
285 if item['k'] == 'repo_landing_rev':
286 val = ':'.join(defaults[attr])
286 val = ':'.join(defaults[attr])
287 defaults[item['k']] = val
287 defaults[item['k']] = val
288 if item['k'] == 'clone_uri':
288 if item['k'] == 'clone_uri':
289 defaults['clone_uri_hidden'] = repo_info.clone_uri_hidden
289 defaults['clone_uri_hidden'] = repo_info.clone_uri_hidden
290
290
291 # fill owner
291 # fill owner
292 if repo_info.user:
292 if repo_info.user:
293 defaults.update({'user': repo_info.user.username})
293 defaults.update({'user': repo_info.user.username})
294 else:
294 else:
295 replacement_user = User.get_first_super_admin().username
295 replacement_user = User.get_first_super_admin().username
296 defaults.update({'user': replacement_user})
296 defaults.update({'user': replacement_user})
297
297
298 # fill repository users
299 for p in repo_info.repo_to_perm:
300 defaults.update({'u_perm_%s' % p.user.user_id:
301 p.permission.permission_name})
302
303 # fill repository groups
304 for p in repo_info.users_group_to_perm:
305 defaults.update({'g_perm_%s' % p.users_group.users_group_id:
306 p.permission.permission_name})
307
308 return defaults
298 return defaults
309
299
310 def update(self, repo, **kwargs):
300 def update(self, repo, **kwargs):
311 try:
301 try:
312 cur_repo = self._get_repo(repo)
302 cur_repo = self._get_repo(repo)
313 source_repo_name = cur_repo.repo_name
303 source_repo_name = cur_repo.repo_name
314 if 'user' in kwargs:
304 if 'user' in kwargs:
315 cur_repo.user = User.get_by_username(kwargs['user'])
305 cur_repo.user = User.get_by_username(kwargs['user'])
316
306
317 if 'repo_group' in kwargs:
307 if 'repo_group' in kwargs:
318 cur_repo.group = RepoGroup.get(kwargs['repo_group'])
308 cur_repo.group = RepoGroup.get(kwargs['repo_group'])
319 log.debug('Updating repo %s with params:%s', cur_repo, kwargs)
309 log.debug('Updating repo %s with params:%s', cur_repo, kwargs)
320
310
321 update_keys = [
311 update_keys = [
322 (1, 'repo_description'),
312 (1, 'repo_description'),
323 (1, 'repo_landing_rev'),
313 (1, 'repo_landing_rev'),
324 (1, 'repo_private'),
314 (1, 'repo_private'),
325 (1, 'repo_enable_downloads'),
315 (1, 'repo_enable_downloads'),
326 (1, 'repo_enable_locking'),
316 (1, 'repo_enable_locking'),
327 (1, 'repo_enable_statistics'),
317 (1, 'repo_enable_statistics'),
328 (0, 'clone_uri'),
318 (0, 'clone_uri'),
329 (0, 'fork_id')
319 (0, 'fork_id')
330 ]
320 ]
331 for strip, k in update_keys:
321 for strip, k in update_keys:
332 if k in kwargs:
322 if k in kwargs:
333 val = kwargs[k]
323 val = kwargs[k]
334 if strip:
324 if strip:
335 k = remove_prefix(k, 'repo_')
325 k = remove_prefix(k, 'repo_')
336
326
337 setattr(cur_repo, k, val)
327 setattr(cur_repo, k, val)
338
328
339 new_name = cur_repo.get_new_name(kwargs['repo_name'])
329 new_name = cur_repo.get_new_name(kwargs['repo_name'])
340 cur_repo.repo_name = new_name
330 cur_repo.repo_name = new_name
341
331
342 # if private flag is set, reset default permission to NONE
332 # if private flag is set, reset default permission to NONE
343 if kwargs.get('repo_private'):
333 if kwargs.get('repo_private'):
344 EMPTY_PERM = 'repository.none'
334 EMPTY_PERM = 'repository.none'
345 RepoModel().grant_user_permission(
335 RepoModel().grant_user_permission(
346 repo=cur_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM
336 repo=cur_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM
347 )
337 )
348
338
349 # handle extra fields
339 # handle extra fields
350 for field in filter(lambda k: k.startswith(RepositoryField.PREFIX),
340 for field in filter(lambda k: k.startswith(RepositoryField.PREFIX),
351 kwargs):
341 kwargs):
352 k = RepositoryField.un_prefix_key(field)
342 k = RepositoryField.un_prefix_key(field)
353 ex_field = RepositoryField.get_by_key_name(
343 ex_field = RepositoryField.get_by_key_name(
354 key=k, repo=cur_repo)
344 key=k, repo=cur_repo)
355 if ex_field:
345 if ex_field:
356 ex_field.field_value = kwargs[field]
346 ex_field.field_value = kwargs[field]
357 self.sa.add(ex_field)
347 self.sa.add(ex_field)
358 self.sa.add(cur_repo)
348 self.sa.add(cur_repo)
359
349
360 if source_repo_name != new_name:
350 if source_repo_name != new_name:
361 # rename repository
351 # rename repository
362 self._rename_filesystem_repo(
352 self._rename_filesystem_repo(
363 old=source_repo_name, new=new_name)
353 old=source_repo_name, new=new_name)
364
354
365 return cur_repo
355 return cur_repo
366 except Exception:
356 except Exception:
367 log.error(traceback.format_exc())
357 log.error(traceback.format_exc())
368 raise
358 raise
369
359
370 def _create_repo(self, repo_name, repo_type, description, owner,
360 def _create_repo(self, repo_name, repo_type, description, owner,
371 private=False, clone_uri=None, repo_group=None,
361 private=False, clone_uri=None, repo_group=None,
372 landing_rev='rev:tip', fork_of=None,
362 landing_rev='rev:tip', fork_of=None,
373 copy_fork_permissions=False, enable_statistics=False,
363 copy_fork_permissions=False, enable_statistics=False,
374 enable_locking=False, enable_downloads=False,
364 enable_locking=False, enable_downloads=False,
375 copy_group_permissions=False,
365 copy_group_permissions=False,
376 state=Repository.STATE_PENDING):
366 state=Repository.STATE_PENDING):
377 """
367 """
378 Create repository inside database with PENDING state, this should be
368 Create repository inside database with PENDING state, this should be
379 only executed by create() repo. With exception of importing existing
369 only executed by create() repo. With exception of importing existing
380 repos
370 repos
381 """
371 """
382 from rhodecode.model.scm import ScmModel
372 from rhodecode.model.scm import ScmModel
383
373
384 owner = self._get_user(owner)
374 owner = self._get_user(owner)
385 fork_of = self._get_repo(fork_of)
375 fork_of = self._get_repo(fork_of)
386 repo_group = self._get_repo_group(safe_int(repo_group))
376 repo_group = self._get_repo_group(safe_int(repo_group))
387
377
388 try:
378 try:
389 repo_name = safe_unicode(repo_name)
379 repo_name = safe_unicode(repo_name)
390 description = safe_unicode(description)
380 description = safe_unicode(description)
391 # repo name is just a name of repository
381 # repo name is just a name of repository
392 # while repo_name_full is a full qualified name that is combined
382 # while repo_name_full is a full qualified name that is combined
393 # with name and path of group
383 # with name and path of group
394 repo_name_full = repo_name
384 repo_name_full = repo_name
395 repo_name = repo_name.split(Repository.NAME_SEP)[-1]
385 repo_name = repo_name.split(Repository.NAME_SEP)[-1]
396
386
397 new_repo = Repository()
387 new_repo = Repository()
398 new_repo.repo_state = state
388 new_repo.repo_state = state
399 new_repo.enable_statistics = False
389 new_repo.enable_statistics = False
400 new_repo.repo_name = repo_name_full
390 new_repo.repo_name = repo_name_full
401 new_repo.repo_type = repo_type
391 new_repo.repo_type = repo_type
402 new_repo.user = owner
392 new_repo.user = owner
403 new_repo.group = repo_group
393 new_repo.group = repo_group
404 new_repo.description = description or repo_name
394 new_repo.description = description or repo_name
405 new_repo.private = private
395 new_repo.private = private
406 new_repo.clone_uri = clone_uri
396 new_repo.clone_uri = clone_uri
407 new_repo.landing_rev = landing_rev
397 new_repo.landing_rev = landing_rev
408
398
409 new_repo.enable_statistics = enable_statistics
399 new_repo.enable_statistics = enable_statistics
410 new_repo.enable_locking = enable_locking
400 new_repo.enable_locking = enable_locking
411 new_repo.enable_downloads = enable_downloads
401 new_repo.enable_downloads = enable_downloads
412
402
413 if repo_group:
403 if repo_group:
414 new_repo.enable_locking = repo_group.enable_locking
404 new_repo.enable_locking = repo_group.enable_locking
415
405
416 if fork_of:
406 if fork_of:
417 parent_repo = fork_of
407 parent_repo = fork_of
418 new_repo.fork = parent_repo
408 new_repo.fork = parent_repo
419
409
420 events.trigger(events.RepoPreCreateEvent(new_repo))
410 events.trigger(events.RepoPreCreateEvent(new_repo))
421
411
422 self.sa.add(new_repo)
412 self.sa.add(new_repo)
423
413
424 EMPTY_PERM = 'repository.none'
414 EMPTY_PERM = 'repository.none'
425 if fork_of and copy_fork_permissions:
415 if fork_of and copy_fork_permissions:
426 repo = fork_of
416 repo = fork_of
427 user_perms = UserRepoToPerm.query() \
417 user_perms = UserRepoToPerm.query() \
428 .filter(UserRepoToPerm.repository == repo).all()
418 .filter(UserRepoToPerm.repository == repo).all()
429 group_perms = UserGroupRepoToPerm.query() \
419 group_perms = UserGroupRepoToPerm.query() \
430 .filter(UserGroupRepoToPerm.repository == repo).all()
420 .filter(UserGroupRepoToPerm.repository == repo).all()
431
421
432 for perm in user_perms:
422 for perm in user_perms:
433 UserRepoToPerm.create(
423 UserRepoToPerm.create(
434 perm.user, new_repo, perm.permission)
424 perm.user, new_repo, perm.permission)
435
425
436 for perm in group_perms:
426 for perm in group_perms:
437 UserGroupRepoToPerm.create(
427 UserGroupRepoToPerm.create(
438 perm.users_group, new_repo, perm.permission)
428 perm.users_group, new_repo, perm.permission)
439 # in case we copy permissions and also set this repo to private
429 # in case we copy permissions and also set this repo to private
440 # override the default user permission to make it a private
430 # override the default user permission to make it a private
441 # repo
431 # repo
442 if private:
432 if private:
443 RepoModel(self.sa).grant_user_permission(
433 RepoModel(self.sa).grant_user_permission(
444 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
434 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
445
435
446 elif repo_group and copy_group_permissions:
436 elif repo_group and copy_group_permissions:
447 user_perms = UserRepoGroupToPerm.query() \
437 user_perms = UserRepoGroupToPerm.query() \
448 .filter(UserRepoGroupToPerm.group == repo_group).all()
438 .filter(UserRepoGroupToPerm.group == repo_group).all()
449
439
450 group_perms = UserGroupRepoGroupToPerm.query() \
440 group_perms = UserGroupRepoGroupToPerm.query() \
451 .filter(UserGroupRepoGroupToPerm.group == repo_group).all()
441 .filter(UserGroupRepoGroupToPerm.group == repo_group).all()
452
442
453 for perm in user_perms:
443 for perm in user_perms:
454 perm_name = perm.permission.permission_name.replace(
444 perm_name = perm.permission.permission_name.replace(
455 'group.', 'repository.')
445 'group.', 'repository.')
456 perm_obj = Permission.get_by_key(perm_name)
446 perm_obj = Permission.get_by_key(perm_name)
457 UserRepoToPerm.create(perm.user, new_repo, perm_obj)
447 UserRepoToPerm.create(perm.user, new_repo, perm_obj)
458
448
459 for perm in group_perms:
449 for perm in group_perms:
460 perm_name = perm.permission.permission_name.replace(
450 perm_name = perm.permission.permission_name.replace(
461 'group.', 'repository.')
451 'group.', 'repository.')
462 perm_obj = Permission.get_by_key(perm_name)
452 perm_obj = Permission.get_by_key(perm_name)
463 UserGroupRepoToPerm.create(
453 UserGroupRepoToPerm.create(
464 perm.users_group, new_repo, perm_obj)
454 perm.users_group, new_repo, perm_obj)
465
455
466 if private:
456 if private:
467 RepoModel(self.sa).grant_user_permission(
457 RepoModel(self.sa).grant_user_permission(
468 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
458 repo=new_repo, user=User.DEFAULT_USER, perm=EMPTY_PERM)
469
459
470 else:
460 else:
471 perm_obj = self._create_default_perms(new_repo, private)
461 perm_obj = self._create_default_perms(new_repo, private)
472 self.sa.add(perm_obj)
462 self.sa.add(perm_obj)
473
463
474 # now automatically start following this repository as owner
464 # now automatically start following this repository as owner
475 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
465 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
476 owner.user_id)
466 owner.user_id)
477
467
478 # we need to flush here, in order to check if database won't
468 # we need to flush here, in order to check if database won't
479 # throw any exceptions, create filesystem dirs at the very end
469 # throw any exceptions, create filesystem dirs at the very end
480 self.sa.flush()
470 self.sa.flush()
481 events.trigger(events.RepoCreateEvent(new_repo))
471 events.trigger(events.RepoCreateEvent(new_repo))
482 return new_repo
472 return new_repo
483
473
484 except Exception:
474 except Exception:
485 log.error(traceback.format_exc())
475 log.error(traceback.format_exc())
486 raise
476 raise
487
477
488 def create(self, form_data, cur_user):
478 def create(self, form_data, cur_user):
489 """
479 """
490 Create repository using celery tasks
480 Create repository using celery tasks
491
481
492 :param form_data:
482 :param form_data:
493 :param cur_user:
483 :param cur_user:
494 """
484 """
495 from rhodecode.lib.celerylib import tasks, run_task
485 from rhodecode.lib.celerylib import tasks, run_task
496 return run_task(tasks.create_repo, form_data, cur_user)
486 return run_task(tasks.create_repo, form_data, cur_user)
497
487
498 def update_permissions(self, repo, perm_additions=None, perm_updates=None,
488 def update_permissions(self, repo, perm_additions=None, perm_updates=None,
499 perm_deletions=None, check_perms=True,
489 perm_deletions=None, check_perms=True,
500 cur_user=None):
490 cur_user=None):
501 if not perm_additions:
491 if not perm_additions:
502 perm_additions = []
492 perm_additions = []
503 if not perm_updates:
493 if not perm_updates:
504 perm_updates = []
494 perm_updates = []
505 if not perm_deletions:
495 if not perm_deletions:
506 perm_deletions = []
496 perm_deletions = []
507
497
508 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
498 req_perms = ('usergroup.read', 'usergroup.write', 'usergroup.admin')
509
499
500 changes = {
501 'added': [],
502 'updated': [],
503 'deleted': []
504 }
510 # update permissions
505 # update permissions
511 for member_id, perm, member_type in perm_updates:
506 for member_id, perm, member_type in perm_updates:
512 member_id = int(member_id)
507 member_id = int(member_id)
513 if member_type == 'user':
508 if member_type == 'user':
509 member_name = User.get(member_id).username
514 # this updates also current one if found
510 # this updates also current one if found
515 self.grant_user_permission(
511 self.grant_user_permission(
516 repo=repo, user=member_id, perm=perm)
512 repo=repo, user=member_id, perm=perm)
517 else: # set for user group
513 else: # set for user group
518 # check if we have permissions to alter this usergroup
514 # check if we have permissions to alter this usergroup
519 member_name = UserGroup.get(member_id).users_group_name
515 member_name = UserGroup.get(member_id).users_group_name
520 if not check_perms or HasUserGroupPermissionAny(
516 if not check_perms or HasUserGroupPermissionAny(
521 *req_perms)(member_name, user=cur_user):
517 *req_perms)(member_name, user=cur_user):
522 self.grant_user_group_permission(
518 self.grant_user_group_permission(
523 repo=repo, group_name=member_id, perm=perm)
519 repo=repo, group_name=member_id, perm=perm)
524
520
521 changes['updated'].append({'type': member_type, 'id': member_id,
522 'name': member_name, 'new_perm': perm})
523
525 # set new permissions
524 # set new permissions
526 for member_id, perm, member_type in perm_additions:
525 for member_id, perm, member_type in perm_additions:
527 member_id = int(member_id)
526 member_id = int(member_id)
528 if member_type == 'user':
527 if member_type == 'user':
528 member_name = User.get(member_id).username
529 self.grant_user_permission(
529 self.grant_user_permission(
530 repo=repo, user=member_id, perm=perm)
530 repo=repo, user=member_id, perm=perm)
531 else: # set for user group
531 else: # set for user group
532 # check if we have permissions to alter this usergroup
532 # check if we have permissions to alter this usergroup
533 member_name = UserGroup.get(member_id).users_group_name
533 member_name = UserGroup.get(member_id).users_group_name
534 if not check_perms or HasUserGroupPermissionAny(
534 if not check_perms or HasUserGroupPermissionAny(
535 *req_perms)(member_name, user=cur_user):
535 *req_perms)(member_name, user=cur_user):
536 self.grant_user_group_permission(
536 self.grant_user_group_permission(
537 repo=repo, group_name=member_id, perm=perm)
537 repo=repo, group_name=member_id, perm=perm)
538
538 changes['added'].append({'type': member_type, 'id': member_id,
539 'name': member_name, 'new_perm': perm})
539 # delete permissions
540 # delete permissions
540 for member_id, perm, member_type in perm_deletions:
541 for member_id, perm, member_type in perm_deletions:
541 member_id = int(member_id)
542 member_id = int(member_id)
542 if member_type == 'user':
543 if member_type == 'user':
544 member_name = User.get(member_id).username
543 self.revoke_user_permission(repo=repo, user=member_id)
545 self.revoke_user_permission(repo=repo, user=member_id)
544 else: # set for user group
546 else: # set for user group
545 # check if we have permissions to alter this usergroup
547 # check if we have permissions to alter this usergroup
546 member_name = UserGroup.get(member_id).users_group_name
548 member_name = UserGroup.get(member_id).users_group_name
547 if not check_perms or HasUserGroupPermissionAny(
549 if not check_perms or HasUserGroupPermissionAny(
548 *req_perms)(member_name, user=cur_user):
550 *req_perms)(member_name, user=cur_user):
549 self.revoke_user_group_permission(
551 self.revoke_user_group_permission(
550 repo=repo, group_name=member_id)
552 repo=repo, group_name=member_id)
551
553
554 changes['deleted'].append({'type': member_type, 'id': member_id,
555 'name': member_name, 'new_perm': perm})
556 return changes
557
552 def create_fork(self, form_data, cur_user):
558 def create_fork(self, form_data, cur_user):
553 """
559 """
554 Simple wrapper into executing celery task for fork creation
560 Simple wrapper into executing celery task for fork creation
555
561
556 :param form_data:
562 :param form_data:
557 :param cur_user:
563 :param cur_user:
558 """
564 """
559 from rhodecode.lib.celerylib import tasks, run_task
565 from rhodecode.lib.celerylib import tasks, run_task
560 return run_task(tasks.create_repo_fork, form_data, cur_user)
566 return run_task(tasks.create_repo_fork, form_data, cur_user)
561
567
562 def delete(self, repo, forks=None, fs_remove=True, cur_user=None):
568 def delete(self, repo, forks=None, fs_remove=True, cur_user=None):
563 """
569 """
564 Delete given repository, forks parameter defines what do do with
570 Delete given repository, forks parameter defines what do do with
565 attached forks. Throws AttachedForksError if deleted repo has attached
571 attached forks. Throws AttachedForksError if deleted repo has attached
566 forks
572 forks
567
573
568 :param repo:
574 :param repo:
569 :param forks: str 'delete' or 'detach'
575 :param forks: str 'delete' or 'detach'
570 :param fs_remove: remove(archive) repo from filesystem
576 :param fs_remove: remove(archive) repo from filesystem
571 """
577 """
572 if not cur_user:
578 if not cur_user:
573 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
579 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
574 repo = self._get_repo(repo)
580 repo = self._get_repo(repo)
575 if repo:
581 if repo:
576 if forks == 'detach':
582 if forks == 'detach':
577 for r in repo.forks:
583 for r in repo.forks:
578 r.fork = None
584 r.fork = None
579 self.sa.add(r)
585 self.sa.add(r)
580 elif forks == 'delete':
586 elif forks == 'delete':
581 for r in repo.forks:
587 for r in repo.forks:
582 self.delete(r, forks='delete')
588 self.delete(r, forks='delete')
583 elif [f for f in repo.forks]:
589 elif [f for f in repo.forks]:
584 raise AttachedForksError()
590 raise AttachedForksError()
585
591
586 old_repo_dict = repo.get_dict()
592 old_repo_dict = repo.get_dict()
587 events.trigger(events.RepoPreDeleteEvent(repo))
593 events.trigger(events.RepoPreDeleteEvent(repo))
588 try:
594 try:
589 self.sa.delete(repo)
595 self.sa.delete(repo)
590 if fs_remove:
596 if fs_remove:
591 self._delete_filesystem_repo(repo)
597 self._delete_filesystem_repo(repo)
592 else:
598 else:
593 log.debug('skipping removal from filesystem')
599 log.debug('skipping removal from filesystem')
594 old_repo_dict.update({
600 old_repo_dict.update({
595 'deleted_by': cur_user,
601 'deleted_by': cur_user,
596 'deleted_on': time.time(),
602 'deleted_on': time.time(),
597 })
603 })
598 log_delete_repository(**old_repo_dict)
604 log_delete_repository(**old_repo_dict)
599 events.trigger(events.RepoDeleteEvent(repo))
605 events.trigger(events.RepoDeleteEvent(repo))
600 except Exception:
606 except Exception:
601 log.error(traceback.format_exc())
607 log.error(traceback.format_exc())
602 raise
608 raise
603
609
604 def grant_user_permission(self, repo, user, perm):
610 def grant_user_permission(self, repo, user, perm):
605 """
611 """
606 Grant permission for user on given repository, or update existing one
612 Grant permission for user on given repository, or update existing one
607 if found
613 if found
608
614
609 :param repo: Instance of Repository, repository_id, or repository name
615 :param repo: Instance of Repository, repository_id, or repository name
610 :param user: Instance of User, user_id or username
616 :param user: Instance of User, user_id or username
611 :param perm: Instance of Permission, or permission_name
617 :param perm: Instance of Permission, or permission_name
612 """
618 """
613 user = self._get_user(user)
619 user = self._get_user(user)
614 repo = self._get_repo(repo)
620 repo = self._get_repo(repo)
615 permission = self._get_perm(perm)
621 permission = self._get_perm(perm)
616
622
617 # check if we have that permission already
623 # check if we have that permission already
618 obj = self.sa.query(UserRepoToPerm) \
624 obj = self.sa.query(UserRepoToPerm) \
619 .filter(UserRepoToPerm.user == user) \
625 .filter(UserRepoToPerm.user == user) \
620 .filter(UserRepoToPerm.repository == repo) \
626 .filter(UserRepoToPerm.repository == repo) \
621 .scalar()
627 .scalar()
622 if obj is None:
628 if obj is None:
623 # create new !
629 # create new !
624 obj = UserRepoToPerm()
630 obj = UserRepoToPerm()
625 obj.repository = repo
631 obj.repository = repo
626 obj.user = user
632 obj.user = user
627 obj.permission = permission
633 obj.permission = permission
628 self.sa.add(obj)
634 self.sa.add(obj)
629 log.debug('Granted perm %s to %s on %s', perm, user, repo)
635 log.debug('Granted perm %s to %s on %s', perm, user, repo)
630 action_logger_generic(
636 action_logger_generic(
631 'granted permission: {} to user: {} on repo: {}'.format(
637 'granted permission: {} to user: {} on repo: {}'.format(
632 perm, user, repo), namespace='security.repo')
638 perm, user, repo), namespace='security.repo')
633 return obj
639 return obj
634
640
635 def revoke_user_permission(self, repo, user):
641 def revoke_user_permission(self, repo, user):
636 """
642 """
637 Revoke permission for user on given repository
643 Revoke permission for user on given repository
638
644
639 :param repo: Instance of Repository, repository_id, or repository name
645 :param repo: Instance of Repository, repository_id, or repository name
640 :param user: Instance of User, user_id or username
646 :param user: Instance of User, user_id or username
641 """
647 """
642
648
643 user = self._get_user(user)
649 user = self._get_user(user)
644 repo = self._get_repo(repo)
650 repo = self._get_repo(repo)
645
651
646 obj = self.sa.query(UserRepoToPerm) \
652 obj = self.sa.query(UserRepoToPerm) \
647 .filter(UserRepoToPerm.repository == repo) \
653 .filter(UserRepoToPerm.repository == repo) \
648 .filter(UserRepoToPerm.user == user) \
654 .filter(UserRepoToPerm.user == user) \
649 .scalar()
655 .scalar()
650 if obj:
656 if obj:
651 self.sa.delete(obj)
657 self.sa.delete(obj)
652 log.debug('Revoked perm on %s on %s', repo, user)
658 log.debug('Revoked perm on %s on %s', repo, user)
653 action_logger_generic(
659 action_logger_generic(
654 'revoked permission from user: {} on repo: {}'.format(
660 'revoked permission from user: {} on repo: {}'.format(
655 user, repo), namespace='security.repo')
661 user, repo), namespace='security.repo')
656
662
657 def grant_user_group_permission(self, repo, group_name, perm):
663 def grant_user_group_permission(self, repo, group_name, perm):
658 """
664 """
659 Grant permission for user group on given repository, or update
665 Grant permission for user group on given repository, or update
660 existing one if found
666 existing one if found
661
667
662 :param repo: Instance of Repository, repository_id, or repository name
668 :param repo: Instance of Repository, repository_id, or repository name
663 :param group_name: Instance of UserGroup, users_group_id,
669 :param group_name: Instance of UserGroup, users_group_id,
664 or user group name
670 or user group name
665 :param perm: Instance of Permission, or permission_name
671 :param perm: Instance of Permission, or permission_name
666 """
672 """
667 repo = self._get_repo(repo)
673 repo = self._get_repo(repo)
668 group_name = self._get_user_group(group_name)
674 group_name = self._get_user_group(group_name)
669 permission = self._get_perm(perm)
675 permission = self._get_perm(perm)
670
676
671 # check if we have that permission already
677 # check if we have that permission already
672 obj = self.sa.query(UserGroupRepoToPerm) \
678 obj = self.sa.query(UserGroupRepoToPerm) \
673 .filter(UserGroupRepoToPerm.users_group == group_name) \
679 .filter(UserGroupRepoToPerm.users_group == group_name) \
674 .filter(UserGroupRepoToPerm.repository == repo) \
680 .filter(UserGroupRepoToPerm.repository == repo) \
675 .scalar()
681 .scalar()
676
682
677 if obj is None:
683 if obj is None:
678 # create new
684 # create new
679 obj = UserGroupRepoToPerm()
685 obj = UserGroupRepoToPerm()
680
686
681 obj.repository = repo
687 obj.repository = repo
682 obj.users_group = group_name
688 obj.users_group = group_name
683 obj.permission = permission
689 obj.permission = permission
684 self.sa.add(obj)
690 self.sa.add(obj)
685 log.debug('Granted perm %s to %s on %s', perm, group_name, repo)
691 log.debug('Granted perm %s to %s on %s', perm, group_name, repo)
686 action_logger_generic(
692 action_logger_generic(
687 'granted permission: {} to usergroup: {} on repo: {}'.format(
693 'granted permission: {} to usergroup: {} on repo: {}'.format(
688 perm, group_name, repo), namespace='security.repo')
694 perm, group_name, repo), namespace='security.repo')
689
695
690 return obj
696 return obj
691
697
692 def revoke_user_group_permission(self, repo, group_name):
698 def revoke_user_group_permission(self, repo, group_name):
693 """
699 """
694 Revoke permission for user group on given repository
700 Revoke permission for user group on given repository
695
701
696 :param repo: Instance of Repository, repository_id, or repository name
702 :param repo: Instance of Repository, repository_id, or repository name
697 :param group_name: Instance of UserGroup, users_group_id,
703 :param group_name: Instance of UserGroup, users_group_id,
698 or user group name
704 or user group name
699 """
705 """
700 repo = self._get_repo(repo)
706 repo = self._get_repo(repo)
701 group_name = self._get_user_group(group_name)
707 group_name = self._get_user_group(group_name)
702
708
703 obj = self.sa.query(UserGroupRepoToPerm) \
709 obj = self.sa.query(UserGroupRepoToPerm) \
704 .filter(UserGroupRepoToPerm.repository == repo) \
710 .filter(UserGroupRepoToPerm.repository == repo) \
705 .filter(UserGroupRepoToPerm.users_group == group_name) \
711 .filter(UserGroupRepoToPerm.users_group == group_name) \
706 .scalar()
712 .scalar()
707 if obj:
713 if obj:
708 self.sa.delete(obj)
714 self.sa.delete(obj)
709 log.debug('Revoked perm to %s on %s', repo, group_name)
715 log.debug('Revoked perm to %s on %s', repo, group_name)
710 action_logger_generic(
716 action_logger_generic(
711 'revoked permission from usergroup: {} on repo: {}'.format(
717 'revoked permission from usergroup: {} on repo: {}'.format(
712 group_name, repo), namespace='security.repo')
718 group_name, repo), namespace='security.repo')
713
719
714 def delete_stats(self, repo_name):
720 def delete_stats(self, repo_name):
715 """
721 """
716 removes stats for given repo
722 removes stats for given repo
717
723
718 :param repo_name:
724 :param repo_name:
719 """
725 """
720 repo = self._get_repo(repo_name)
726 repo = self._get_repo(repo_name)
721 try:
727 try:
722 obj = self.sa.query(Statistics) \
728 obj = self.sa.query(Statistics) \
723 .filter(Statistics.repository == repo).scalar()
729 .filter(Statistics.repository == repo).scalar()
724 if obj:
730 if obj:
725 self.sa.delete(obj)
731 self.sa.delete(obj)
726 except Exception:
732 except Exception:
727 log.error(traceback.format_exc())
733 log.error(traceback.format_exc())
728 raise
734 raise
729
735
730 def add_repo_field(self, repo_name, field_key, field_label, field_value='',
736 def add_repo_field(self, repo_name, field_key, field_label, field_value='',
731 field_type='str', field_desc=''):
737 field_type='str', field_desc=''):
732
738
733 repo = self._get_repo(repo_name)
739 repo = self._get_repo(repo_name)
734
740
735 new_field = RepositoryField()
741 new_field = RepositoryField()
736 new_field.repository = repo
742 new_field.repository = repo
737 new_field.field_key = field_key
743 new_field.field_key = field_key
738 new_field.field_type = field_type # python type
744 new_field.field_type = field_type # python type
739 new_field.field_value = field_value
745 new_field.field_value = field_value
740 new_field.field_desc = field_desc
746 new_field.field_desc = field_desc
741 new_field.field_label = field_label
747 new_field.field_label = field_label
742 self.sa.add(new_field)
748 self.sa.add(new_field)
743 return new_field
749 return new_field
744
750
745 def delete_repo_field(self, repo_name, field_key):
751 def delete_repo_field(self, repo_name, field_key):
746 repo = self._get_repo(repo_name)
752 repo = self._get_repo(repo_name)
747 field = RepositoryField.get_by_key_name(field_key, repo)
753 field = RepositoryField.get_by_key_name(field_key, repo)
748 if field:
754 if field:
749 self.sa.delete(field)
755 self.sa.delete(field)
750
756
751 def _create_filesystem_repo(self, repo_name, repo_type, repo_group,
757 def _create_filesystem_repo(self, repo_name, repo_type, repo_group,
752 clone_uri=None, repo_store_location=None,
758 clone_uri=None, repo_store_location=None,
753 use_global_config=False):
759 use_global_config=False):
754 """
760 """
755 makes repository on filesystem. It's group aware means it'll create
761 makes repository on filesystem. It's group aware means it'll create
756 a repository within a group, and alter the paths accordingly of
762 a repository within a group, and alter the paths accordingly of
757 group location
763 group location
758
764
759 :param repo_name:
765 :param repo_name:
760 :param alias:
766 :param alias:
761 :param parent:
767 :param parent:
762 :param clone_uri:
768 :param clone_uri:
763 :param repo_store_location:
769 :param repo_store_location:
764 """
770 """
765 from rhodecode.lib.utils import is_valid_repo, is_valid_repo_group
771 from rhodecode.lib.utils import is_valid_repo, is_valid_repo_group
766 from rhodecode.model.scm import ScmModel
772 from rhodecode.model.scm import ScmModel
767
773
768 if Repository.NAME_SEP in repo_name:
774 if Repository.NAME_SEP in repo_name:
769 raise ValueError(
775 raise ValueError(
770 'repo_name must not contain groups got `%s`' % repo_name)
776 'repo_name must not contain groups got `%s`' % repo_name)
771
777
772 if isinstance(repo_group, RepoGroup):
778 if isinstance(repo_group, RepoGroup):
773 new_parent_path = os.sep.join(repo_group.full_path_splitted)
779 new_parent_path = os.sep.join(repo_group.full_path_splitted)
774 else:
780 else:
775 new_parent_path = repo_group or ''
781 new_parent_path = repo_group or ''
776
782
777 if repo_store_location:
783 if repo_store_location:
778 _paths = [repo_store_location]
784 _paths = [repo_store_location]
779 else:
785 else:
780 _paths = [self.repos_path, new_parent_path, repo_name]
786 _paths = [self.repos_path, new_parent_path, repo_name]
781 # we need to make it str for mercurial
787 # we need to make it str for mercurial
782 repo_path = os.path.join(*map(lambda x: safe_str(x), _paths))
788 repo_path = os.path.join(*map(lambda x: safe_str(x), _paths))
783
789
784 # check if this path is not a repository
790 # check if this path is not a repository
785 if is_valid_repo(repo_path, self.repos_path):
791 if is_valid_repo(repo_path, self.repos_path):
786 raise Exception('This path %s is a valid repository' % repo_path)
792 raise Exception('This path %s is a valid repository' % repo_path)
787
793
788 # check if this path is a group
794 # check if this path is a group
789 if is_valid_repo_group(repo_path, self.repos_path):
795 if is_valid_repo_group(repo_path, self.repos_path):
790 raise Exception('This path %s is a valid group' % repo_path)
796 raise Exception('This path %s is a valid group' % repo_path)
791
797
792 log.info('creating repo %s in %s from url: `%s`',
798 log.info('creating repo %s in %s from url: `%s`',
793 repo_name, safe_unicode(repo_path),
799 repo_name, safe_unicode(repo_path),
794 obfuscate_url_pw(clone_uri))
800 obfuscate_url_pw(clone_uri))
795
801
796 backend = get_backend(repo_type)
802 backend = get_backend(repo_type)
797
803
798 config_repo = None if use_global_config else repo_name
804 config_repo = None if use_global_config else repo_name
799 if config_repo and new_parent_path:
805 if config_repo and new_parent_path:
800 config_repo = Repository.NAME_SEP.join(
806 config_repo = Repository.NAME_SEP.join(
801 (new_parent_path, config_repo))
807 (new_parent_path, config_repo))
802 config = make_db_config(clear_session=False, repo=config_repo)
808 config = make_db_config(clear_session=False, repo=config_repo)
803 config.set('extensions', 'largefiles', '')
809 config.set('extensions', 'largefiles', '')
804
810
805 # patch and reset hooks section of UI config to not run any
811 # patch and reset hooks section of UI config to not run any
806 # hooks on creating remote repo
812 # hooks on creating remote repo
807 config.clear_section('hooks')
813 config.clear_section('hooks')
808
814
809 # TODO: johbo: Unify this, hardcoded "bare=True" does not look nice
815 # TODO: johbo: Unify this, hardcoded "bare=True" does not look nice
810 if repo_type == 'git':
816 if repo_type == 'git':
811 repo = backend(
817 repo = backend(
812 repo_path, config=config, create=True, src_url=clone_uri,
818 repo_path, config=config, create=True, src_url=clone_uri,
813 bare=True)
819 bare=True)
814 else:
820 else:
815 repo = backend(
821 repo = backend(
816 repo_path, config=config, create=True, src_url=clone_uri)
822 repo_path, config=config, create=True, src_url=clone_uri)
817
823
818 ScmModel().install_hooks(repo, repo_type=repo_type)
824 ScmModel().install_hooks(repo, repo_type=repo_type)
819
825
820 log.debug('Created repo %s with %s backend',
826 log.debug('Created repo %s with %s backend',
821 safe_unicode(repo_name), safe_unicode(repo_type))
827 safe_unicode(repo_name), safe_unicode(repo_type))
822 return repo
828 return repo
823
829
824 def _rename_filesystem_repo(self, old, new):
830 def _rename_filesystem_repo(self, old, new):
825 """
831 """
826 renames repository on filesystem
832 renames repository on filesystem
827
833
828 :param old: old name
834 :param old: old name
829 :param new: new name
835 :param new: new name
830 """
836 """
831 log.info('renaming repo from %s to %s', old, new)
837 log.info('renaming repo from %s to %s', old, new)
832
838
833 old_path = os.path.join(self.repos_path, old)
839 old_path = os.path.join(self.repos_path, old)
834 new_path = os.path.join(self.repos_path, new)
840 new_path = os.path.join(self.repos_path, new)
835 if os.path.isdir(new_path):
841 if os.path.isdir(new_path):
836 raise Exception(
842 raise Exception(
837 'Was trying to rename to already existing dir %s' % new_path
843 'Was trying to rename to already existing dir %s' % new_path
838 )
844 )
839 shutil.move(old_path, new_path)
845 shutil.move(old_path, new_path)
840
846
841 def _delete_filesystem_repo(self, repo):
847 def _delete_filesystem_repo(self, repo):
842 """
848 """
843 removes repo from filesystem, the removal is acctually made by
849 removes repo from filesystem, the removal is acctually made by
844 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
850 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
845 repository is no longer valid for rhodecode, can be undeleted later on
851 repository is no longer valid for rhodecode, can be undeleted later on
846 by reverting the renames on this repository
852 by reverting the renames on this repository
847
853
848 :param repo: repo object
854 :param repo: repo object
849 """
855 """
850 rm_path = os.path.join(self.repos_path, repo.repo_name)
856 rm_path = os.path.join(self.repos_path, repo.repo_name)
851 repo_group = repo.group
857 repo_group = repo.group
852 log.info("Removing repository %s", rm_path)
858 log.info("Removing repository %s", rm_path)
853 # disable hg/git internal that it doesn't get detected as repo
859 # disable hg/git internal that it doesn't get detected as repo
854 alias = repo.repo_type
860 alias = repo.repo_type
855
861
856 config = make_db_config(clear_session=False)
862 config = make_db_config(clear_session=False)
857 config.set('extensions', 'largefiles', '')
863 config.set('extensions', 'largefiles', '')
858 bare = getattr(repo.scm_instance(config=config), 'bare', False)
864 bare = getattr(repo.scm_instance(config=config), 'bare', False)
859
865
860 # skip this for bare git repos
866 # skip this for bare git repos
861 if not bare:
867 if not bare:
862 # disable VCS repo
868 # disable VCS repo
863 vcs_path = os.path.join(rm_path, '.%s' % alias)
869 vcs_path = os.path.join(rm_path, '.%s' % alias)
864 if os.path.exists(vcs_path):
870 if os.path.exists(vcs_path):
865 shutil.move(vcs_path, os.path.join(rm_path, 'rm__.%s' % alias))
871 shutil.move(vcs_path, os.path.join(rm_path, 'rm__.%s' % alias))
866
872
867 _now = datetime.now()
873 _now = datetime.now()
868 _ms = str(_now.microsecond).rjust(6, '0')
874 _ms = str(_now.microsecond).rjust(6, '0')
869 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
875 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
870 repo.just_name)
876 repo.just_name)
871 if repo_group:
877 if repo_group:
872 # if repository is in group, prefix the removal path with the group
878 # if repository is in group, prefix the removal path with the group
873 args = repo_group.full_path_splitted + [_d]
879 args = repo_group.full_path_splitted + [_d]
874 _d = os.path.join(*args)
880 _d = os.path.join(*args)
875
881
876 if os.path.isdir(rm_path):
882 if os.path.isdir(rm_path):
877 shutil.move(rm_path, os.path.join(self.repos_path, _d))
883 shutil.move(rm_path, os.path.join(self.repos_path, _d))
878
884
879
885
880 class ReadmeFinder:
886 class ReadmeFinder:
881 """
887 """
882 Utility which knows how to find a readme for a specific commit.
888 Utility which knows how to find a readme for a specific commit.
883
889
884 The main idea is that this is a configurable algorithm. When creating an
890 The main idea is that this is a configurable algorithm. When creating an
885 instance you can define parameters, currently only the `default_renderer`.
891 instance you can define parameters, currently only the `default_renderer`.
886 Based on this configuration the method :meth:`search` behaves slightly
892 Based on this configuration the method :meth:`search` behaves slightly
887 different.
893 different.
888 """
894 """
889
895
890 readme_re = re.compile(r'^readme(\.[^\.]+)?$', re.IGNORECASE)
896 readme_re = re.compile(r'^readme(\.[^\.]+)?$', re.IGNORECASE)
891 path_re = re.compile(r'^docs?', re.IGNORECASE)
897 path_re = re.compile(r'^docs?', re.IGNORECASE)
892
898
893 default_priorities = {
899 default_priorities = {
894 None: 0,
900 None: 0,
895 '.text': 2,
901 '.text': 2,
896 '.txt': 3,
902 '.txt': 3,
897 '.rst': 1,
903 '.rst': 1,
898 '.rest': 2,
904 '.rest': 2,
899 '.md': 1,
905 '.md': 1,
900 '.mkdn': 2,
906 '.mkdn': 2,
901 '.mdown': 3,
907 '.mdown': 3,
902 '.markdown': 4,
908 '.markdown': 4,
903 }
909 }
904
910
905 path_priority = {
911 path_priority = {
906 'doc': 0,
912 'doc': 0,
907 'docs': 1,
913 'docs': 1,
908 }
914 }
909
915
910 FALLBACK_PRIORITY = 99
916 FALLBACK_PRIORITY = 99
911
917
912 RENDERER_TO_EXTENSION = {
918 RENDERER_TO_EXTENSION = {
913 'rst': ['.rst', '.rest'],
919 'rst': ['.rst', '.rest'],
914 'markdown': ['.md', 'mkdn', '.mdown', '.markdown'],
920 'markdown': ['.md', 'mkdn', '.mdown', '.markdown'],
915 }
921 }
916
922
917 def __init__(self, default_renderer=None):
923 def __init__(self, default_renderer=None):
918 self._default_renderer = default_renderer
924 self._default_renderer = default_renderer
919 self._renderer_extensions = self.RENDERER_TO_EXTENSION.get(
925 self._renderer_extensions = self.RENDERER_TO_EXTENSION.get(
920 default_renderer, [])
926 default_renderer, [])
921
927
922 def search(self, commit, path='/'):
928 def search(self, commit, path='/'):
923 """
929 """
924 Find a readme in the given `commit`.
930 Find a readme in the given `commit`.
925 """
931 """
926 nodes = commit.get_nodes(path)
932 nodes = commit.get_nodes(path)
927 matches = self._match_readmes(nodes)
933 matches = self._match_readmes(nodes)
928 matches = self._sort_according_to_priority(matches)
934 matches = self._sort_according_to_priority(matches)
929 if matches:
935 if matches:
930 return matches[0].node
936 return matches[0].node
931
937
932 paths = self._match_paths(nodes)
938 paths = self._match_paths(nodes)
933 paths = self._sort_paths_according_to_priority(paths)
939 paths = self._sort_paths_according_to_priority(paths)
934 for path in paths:
940 for path in paths:
935 match = self.search(commit, path=path)
941 match = self.search(commit, path=path)
936 if match:
942 if match:
937 return match
943 return match
938
944
939 return None
945 return None
940
946
941 def _match_readmes(self, nodes):
947 def _match_readmes(self, nodes):
942 for node in nodes:
948 for node in nodes:
943 if not node.is_file():
949 if not node.is_file():
944 continue
950 continue
945 path = node.path.rsplit('/', 1)[-1]
951 path = node.path.rsplit('/', 1)[-1]
946 match = self.readme_re.match(path)
952 match = self.readme_re.match(path)
947 if match:
953 if match:
948 extension = match.group(1)
954 extension = match.group(1)
949 yield ReadmeMatch(node, match, self._priority(extension))
955 yield ReadmeMatch(node, match, self._priority(extension))
950
956
951 def _match_paths(self, nodes):
957 def _match_paths(self, nodes):
952 for node in nodes:
958 for node in nodes:
953 if not node.is_dir():
959 if not node.is_dir():
954 continue
960 continue
955 match = self.path_re.match(node.path)
961 match = self.path_re.match(node.path)
956 if match:
962 if match:
957 yield node.path
963 yield node.path
958
964
959 def _priority(self, extension):
965 def _priority(self, extension):
960 renderer_priority = (
966 renderer_priority = (
961 0 if extension in self._renderer_extensions else 1)
967 0 if extension in self._renderer_extensions else 1)
962 extension_priority = self.default_priorities.get(
968 extension_priority = self.default_priorities.get(
963 extension, self.FALLBACK_PRIORITY)
969 extension, self.FALLBACK_PRIORITY)
964 return (renderer_priority, extension_priority)
970 return (renderer_priority, extension_priority)
965
971
966 def _sort_according_to_priority(self, matches):
972 def _sort_according_to_priority(self, matches):
967
973
968 def priority_and_path(match):
974 def priority_and_path(match):
969 return (match.priority, match.path)
975 return (match.priority, match.path)
970
976
971 return sorted(matches, key=priority_and_path)
977 return sorted(matches, key=priority_and_path)
972
978
973 def _sort_paths_according_to_priority(self, paths):
979 def _sort_paths_according_to_priority(self, paths):
974
980
975 def priority_and_path(path):
981 def priority_and_path(path):
976 return (self.path_priority.get(path, self.FALLBACK_PRIORITY), path)
982 return (self.path_priority.get(path, self.FALLBACK_PRIORITY), path)
977
983
978 return sorted(paths, key=priority_and_path)
984 return sorted(paths, key=priority_and_path)
979
985
980
986
981 class ReadmeMatch:
987 class ReadmeMatch:
982
988
983 def __init__(self, node, match, priority):
989 def __init__(self, node, match, priority):
984 self.node = node
990 self.node = node
985 self._match = match
991 self._match = match
986 self.priority = priority
992 self.priority = priority
987
993
988 @property
994 @property
989 def path(self):
995 def path(self):
990 return self.node.path
996 return self.node.path
991
997
992 def __repr__(self):
998 def __repr__(self):
993 return '<ReadmeMatch {} priority={}'.format(self.path, self.priority)
999 return '<ReadmeMatch {} priority={}'.format(self.path, self.priority)
@@ -1,1109 +1,1122 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 Set of generic validators
22 Set of generic validators
23 """
23 """
24
24
25 import logging
25 import logging
26 import os
26 import os
27 import re
27 import re
28 from collections import defaultdict
28 from collections import defaultdict
29
29
30 import formencode
30 import formencode
31 import ipaddress
31 import ipaddress
32 from formencode.validators import (
32 from formencode.validators import (
33 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set,
33 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set,
34 NotEmpty, IPAddress, CIDR, String, FancyValidator
34 NotEmpty, IPAddress, CIDR, String, FancyValidator
35 )
35 )
36 from pylons.i18n.translation import _
36 from pylons.i18n.translation import _
37 from sqlalchemy.sql.expression import true
37 from sqlalchemy.sql.expression import true
38 from sqlalchemy.util import OrderedSet
38 from sqlalchemy.util import OrderedSet
39 from webhelpers.pylonslib.secure_form import authentication_token
39 from webhelpers.pylonslib.secure_form import authentication_token
40
40
41 from rhodecode.authentication import (
41 from rhodecode.authentication import (
42 legacy_plugin_prefix, _import_legacy_plugin)
42 legacy_plugin_prefix, _import_legacy_plugin)
43 from rhodecode.authentication.base import loadplugin
43 from rhodecode.authentication.base import loadplugin
44 from rhodecode.config.routing import ADMIN_PREFIX
44 from rhodecode.config.routing import ADMIN_PREFIX
45 from rhodecode.lib.auth import HasRepoGroupPermissionAny, HasPermissionAny
45 from rhodecode.lib.auth import HasRepoGroupPermissionAny, HasPermissionAny
46 from rhodecode.lib.utils import repo_name_slug, make_db_config
46 from rhodecode.lib.utils import repo_name_slug, make_db_config
47 from rhodecode.lib.utils2 import safe_int, str2bool, aslist, md5
47 from rhodecode.lib.utils2 import safe_int, str2bool, aslist, md5
48 from rhodecode.lib.vcs.backends.git.repository import GitRepository
48 from rhodecode.lib.vcs.backends.git.repository import GitRepository
49 from rhodecode.lib.vcs.backends.hg.repository import MercurialRepository
49 from rhodecode.lib.vcs.backends.hg.repository import MercurialRepository
50 from rhodecode.lib.vcs.backends.svn.repository import SubversionRepository
50 from rhodecode.lib.vcs.backends.svn.repository import SubversionRepository
51 from rhodecode.model.db import (
51 from rhodecode.model.db import (
52 RepoGroup, Repository, UserGroup, User, ChangesetStatus, Gist)
52 RepoGroup, Repository, UserGroup, User, ChangesetStatus, Gist)
53 from rhodecode.model.settings import VcsSettingsModel
53 from rhodecode.model.settings import VcsSettingsModel
54
54
55 # silence warnings and pylint
55 # silence warnings and pylint
56 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set, \
56 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set, \
57 NotEmpty, IPAddress, CIDR, String, FancyValidator
57 NotEmpty, IPAddress, CIDR, String, FancyValidator
58
58
59 log = logging.getLogger(__name__)
59 log = logging.getLogger(__name__)
60
60
61
61
62 class _Missing(object):
62 class _Missing(object):
63 pass
63 pass
64
64
65 Missing = _Missing()
65 Missing = _Missing()
66
66
67
67
68 class StateObj(object):
68 class StateObj(object):
69 """
69 """
70 this is needed to translate the messages using _() in validators
70 this is needed to translate the messages using _() in validators
71 """
71 """
72 _ = staticmethod(_)
72 _ = staticmethod(_)
73
73
74
74
75 def M(self, key, state=None, **kwargs):
75 def M(self, key, state=None, **kwargs):
76 """
76 """
77 returns string from self.message based on given key,
77 returns string from self.message based on given key,
78 passed kw params are used to substitute %(named)s params inside
78 passed kw params are used to substitute %(named)s params inside
79 translated strings
79 translated strings
80
80
81 :param msg:
81 :param msg:
82 :param state:
82 :param state:
83 """
83 """
84 if state is None:
84 if state is None:
85 state = StateObj()
85 state = StateObj()
86 else:
86 else:
87 state._ = staticmethod(_)
87 state._ = staticmethod(_)
88 # inject validator into state object
88 # inject validator into state object
89 return self.message(key, state, **kwargs)
89 return self.message(key, state, **kwargs)
90
90
91
91
92 def UniqueList(convert=None):
92 def UniqueList(convert=None):
93 class _UniqueList(formencode.FancyValidator):
93 class _UniqueList(formencode.FancyValidator):
94 """
94 """
95 Unique List !
95 Unique List !
96 """
96 """
97 messages = {
97 messages = {
98 'empty': _(u'Value cannot be an empty list'),
98 'empty': _(u'Value cannot be an empty list'),
99 'missing_value': _(u'Value cannot be an empty list'),
99 'missing_value': _(u'Value cannot be an empty list'),
100 }
100 }
101
101
102 def _to_python(self, value, state):
102 def _to_python(self, value, state):
103 ret_val = []
103 ret_val = []
104
104
105 def make_unique(value):
105 def make_unique(value):
106 seen = []
106 seen = []
107 return [c for c in value if not (c in seen or seen.append(c))]
107 return [c for c in value if not (c in seen or seen.append(c))]
108
108
109 if isinstance(value, list):
109 if isinstance(value, list):
110 ret_val = make_unique(value)
110 ret_val = make_unique(value)
111 elif isinstance(value, set):
111 elif isinstance(value, set):
112 ret_val = make_unique(list(value))
112 ret_val = make_unique(list(value))
113 elif isinstance(value, tuple):
113 elif isinstance(value, tuple):
114 ret_val = make_unique(list(value))
114 ret_val = make_unique(list(value))
115 elif value is None:
115 elif value is None:
116 ret_val = []
116 ret_val = []
117 else:
117 else:
118 ret_val = [value]
118 ret_val = [value]
119
119
120 if convert:
120 if convert:
121 ret_val = map(convert, ret_val)
121 ret_val = map(convert, ret_val)
122 return ret_val
122 return ret_val
123
123
124 def empty_value(self, value):
124 def empty_value(self, value):
125 return []
125 return []
126
126
127 return _UniqueList
127 return _UniqueList
128
128
129
129
130 def UniqueListFromString():
130 def UniqueListFromString():
131 class _UniqueListFromString(UniqueList()):
131 class _UniqueListFromString(UniqueList()):
132 def _to_python(self, value, state):
132 def _to_python(self, value, state):
133 if isinstance(value, basestring):
133 if isinstance(value, basestring):
134 value = aslist(value, ',')
134 value = aslist(value, ',')
135 return super(_UniqueListFromString, self)._to_python(value, state)
135 return super(_UniqueListFromString, self)._to_python(value, state)
136 return _UniqueListFromString
136 return _UniqueListFromString
137
137
138
138
139 def ValidSvnPattern(section, repo_name=None):
139 def ValidSvnPattern(section, repo_name=None):
140 class _validator(formencode.validators.FancyValidator):
140 class _validator(formencode.validators.FancyValidator):
141 messages = {
141 messages = {
142 'pattern_exists': _(u'Pattern already exists'),
142 'pattern_exists': _(u'Pattern already exists'),
143 }
143 }
144
144
145 def validate_python(self, value, state):
145 def validate_python(self, value, state):
146 if not value:
146 if not value:
147 return
147 return
148 model = VcsSettingsModel(repo=repo_name)
148 model = VcsSettingsModel(repo=repo_name)
149 ui_settings = model.get_svn_patterns(section=section)
149 ui_settings = model.get_svn_patterns(section=section)
150 for entry in ui_settings:
150 for entry in ui_settings:
151 if value == entry.value:
151 if value == entry.value:
152 msg = M(self, 'pattern_exists', state)
152 msg = M(self, 'pattern_exists', state)
153 raise formencode.Invalid(msg, value, state)
153 raise formencode.Invalid(msg, value, state)
154 return _validator
154 return _validator
155
155
156
156
157 def ValidUsername(edit=False, old_data={}):
157 def ValidUsername(edit=False, old_data={}):
158 class _validator(formencode.validators.FancyValidator):
158 class _validator(formencode.validators.FancyValidator):
159 messages = {
159 messages = {
160 'username_exists': _(u'Username "%(username)s" already exists'),
160 'username_exists': _(u'Username "%(username)s" already exists'),
161 'system_invalid_username':
161 'system_invalid_username':
162 _(u'Username "%(username)s" is forbidden'),
162 _(u'Username "%(username)s" is forbidden'),
163 'invalid_username':
163 'invalid_username':
164 _(u'Username may only contain alphanumeric characters '
164 _(u'Username may only contain alphanumeric characters '
165 u'underscores, periods or dashes and must begin with '
165 u'underscores, periods or dashes and must begin with '
166 u'alphanumeric character or underscore')
166 u'alphanumeric character or underscore')
167 }
167 }
168
168
169 def validate_python(self, value, state):
169 def validate_python(self, value, state):
170 if value in ['default', 'new_user']:
170 if value in ['default', 'new_user']:
171 msg = M(self, 'system_invalid_username', state, username=value)
171 msg = M(self, 'system_invalid_username', state, username=value)
172 raise formencode.Invalid(msg, value, state)
172 raise formencode.Invalid(msg, value, state)
173 # check if user is unique
173 # check if user is unique
174 old_un = None
174 old_un = None
175 if edit:
175 if edit:
176 old_un = User.get(old_data.get('user_id')).username
176 old_un = User.get(old_data.get('user_id')).username
177
177
178 if old_un != value or not edit:
178 if old_un != value or not edit:
179 if User.get_by_username(value, case_insensitive=True):
179 if User.get_by_username(value, case_insensitive=True):
180 msg = M(self, 'username_exists', state, username=value)
180 msg = M(self, 'username_exists', state, username=value)
181 raise formencode.Invalid(msg, value, state)
181 raise formencode.Invalid(msg, value, state)
182
182
183 if (re.match(r'^[\w]{1}[\w\-\.]{0,254}$', value)
183 if (re.match(r'^[\w]{1}[\w\-\.]{0,254}$', value)
184 is None):
184 is None):
185 msg = M(self, 'invalid_username', state)
185 msg = M(self, 'invalid_username', state)
186 raise formencode.Invalid(msg, value, state)
186 raise formencode.Invalid(msg, value, state)
187 return _validator
187 return _validator
188
188
189
189
190 def ValidRegex(msg=None):
190 def ValidRegex(msg=None):
191 class _validator(formencode.validators.Regex):
191 class _validator(formencode.validators.Regex):
192 messages = {'invalid': msg or _(u'The input is not valid')}
192 messages = {'invalid': msg or _(u'The input is not valid')}
193 return _validator
193 return _validator
194
194
195
195
196 def ValidRepoUser(allow_disabled=False):
196 def ValidRepoUser(allow_disabled=False):
197 class _validator(formencode.validators.FancyValidator):
197 class _validator(formencode.validators.FancyValidator):
198 messages = {
198 messages = {
199 'invalid_username': _(u'Username %(username)s is not valid'),
199 'invalid_username': _(u'Username %(username)s is not valid'),
200 'disabled_username': _(u'Username %(username)s is disabled')
200 'disabled_username': _(u'Username %(username)s is disabled')
201 }
201 }
202
202
203 def validate_python(self, value, state):
203 def validate_python(self, value, state):
204 try:
204 try:
205 user = User.query().filter(User.username == value).one()
205 user = User.query().filter(User.username == value).one()
206 except Exception:
206 except Exception:
207 msg = M(self, 'invalid_username', state, username=value)
207 msg = M(self, 'invalid_username', state, username=value)
208 raise formencode.Invalid(
208 raise formencode.Invalid(
209 msg, value, state, error_dict={'username': msg}
209 msg, value, state, error_dict={'username': msg}
210 )
210 )
211 if user and (not allow_disabled and not user.active):
211 if user and (not allow_disabled and not user.active):
212 msg = M(self, 'disabled_username', state, username=value)
212 msg = M(self, 'disabled_username', state, username=value)
213 raise formencode.Invalid(
213 raise formencode.Invalid(
214 msg, value, state, error_dict={'username': msg}
214 msg, value, state, error_dict={'username': msg}
215 )
215 )
216
216
217 return _validator
217 return _validator
218
218
219
219
220 def ValidUserGroup(edit=False, old_data={}):
220 def ValidUserGroup(edit=False, old_data={}):
221 class _validator(formencode.validators.FancyValidator):
221 class _validator(formencode.validators.FancyValidator):
222 messages = {
222 messages = {
223 'invalid_group': _(u'Invalid user group name'),
223 'invalid_group': _(u'Invalid user group name'),
224 'group_exist': _(u'User group "%(usergroup)s" already exists'),
224 'group_exist': _(u'User group "%(usergroup)s" already exists'),
225 'invalid_usergroup_name':
225 'invalid_usergroup_name':
226 _(u'user group name may only contain alphanumeric '
226 _(u'user group name may only contain alphanumeric '
227 u'characters underscores, periods or dashes and must begin '
227 u'characters underscores, periods or dashes and must begin '
228 u'with alphanumeric character')
228 u'with alphanumeric character')
229 }
229 }
230
230
231 def validate_python(self, value, state):
231 def validate_python(self, value, state):
232 if value in ['default']:
232 if value in ['default']:
233 msg = M(self, 'invalid_group', state)
233 msg = M(self, 'invalid_group', state)
234 raise formencode.Invalid(
234 raise formencode.Invalid(
235 msg, value, state, error_dict={'users_group_name': msg}
235 msg, value, state, error_dict={'users_group_name': msg}
236 )
236 )
237 # check if group is unique
237 # check if group is unique
238 old_ugname = None
238 old_ugname = None
239 if edit:
239 if edit:
240 old_id = old_data.get('users_group_id')
240 old_id = old_data.get('users_group_id')
241 old_ugname = UserGroup.get(old_id).users_group_name
241 old_ugname = UserGroup.get(old_id).users_group_name
242
242
243 if old_ugname != value or not edit:
243 if old_ugname != value or not edit:
244 is_existing_group = UserGroup.get_by_group_name(
244 is_existing_group = UserGroup.get_by_group_name(
245 value, case_insensitive=True)
245 value, case_insensitive=True)
246 if is_existing_group:
246 if is_existing_group:
247 msg = M(self, 'group_exist', state, usergroup=value)
247 msg = M(self, 'group_exist', state, usergroup=value)
248 raise formencode.Invalid(
248 raise formencode.Invalid(
249 msg, value, state, error_dict={'users_group_name': msg}
249 msg, value, state, error_dict={'users_group_name': msg}
250 )
250 )
251
251
252 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
252 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
253 msg = M(self, 'invalid_usergroup_name', state)
253 msg = M(self, 'invalid_usergroup_name', state)
254 raise formencode.Invalid(
254 raise formencode.Invalid(
255 msg, value, state, error_dict={'users_group_name': msg}
255 msg, value, state, error_dict={'users_group_name': msg}
256 )
256 )
257
257
258 return _validator
258 return _validator
259
259
260
260
261 def ValidRepoGroup(edit=False, old_data={}, can_create_in_root=False):
261 def ValidRepoGroup(edit=False, old_data={}, can_create_in_root=False):
262 class _validator(formencode.validators.FancyValidator):
262 class _validator(formencode.validators.FancyValidator):
263 messages = {
263 messages = {
264 'group_parent_id': _(u'Cannot assign this group as parent'),
264 'group_parent_id': _(u'Cannot assign this group as parent'),
265 'group_exists': _(u'Group "%(group_name)s" already exists'),
265 'group_exists': _(u'Group "%(group_name)s" already exists'),
266 'repo_exists': _(u'Repository with name "%(group_name)s" '
266 'repo_exists': _(u'Repository with name "%(group_name)s" '
267 u'already exists'),
267 u'already exists'),
268 'permission_denied': _(u"no permission to store repository group"
268 'permission_denied': _(u"no permission to store repository group"
269 u"in this location"),
269 u"in this location"),
270 'permission_denied_root': _(
270 'permission_denied_root': _(
271 u"no permission to store repository group "
271 u"no permission to store repository group "
272 u"in root location")
272 u"in root location")
273 }
273 }
274
274
275 def _to_python(self, value, state):
275 def _to_python(self, value, state):
276 group_name = repo_name_slug(value.get('group_name', ''))
276 group_name = repo_name_slug(value.get('group_name', ''))
277 group_parent_id = safe_int(value.get('group_parent_id'))
277 group_parent_id = safe_int(value.get('group_parent_id'))
278 gr = RepoGroup.get(group_parent_id)
278 gr = RepoGroup.get(group_parent_id)
279 if gr:
279 if gr:
280 parent_group_path = gr.full_path
280 parent_group_path = gr.full_path
281 # value needs to be aware of group name in order to check
281 # value needs to be aware of group name in order to check
282 # db key This is an actual just the name to store in the
282 # db key This is an actual just the name to store in the
283 # database
283 # database
284 group_name_full = (
284 group_name_full = (
285 parent_group_path + RepoGroup.url_sep() + group_name)
285 parent_group_path + RepoGroup.url_sep() + group_name)
286 else:
286 else:
287 group_name_full = group_name
287 group_name_full = group_name
288
288
289 value['group_name'] = group_name
289 value['group_name'] = group_name
290 value['group_name_full'] = group_name_full
290 value['group_name_full'] = group_name_full
291 value['group_parent_id'] = group_parent_id
291 value['group_parent_id'] = group_parent_id
292 return value
292 return value
293
293
294 def validate_python(self, value, state):
294 def validate_python(self, value, state):
295
295
296 old_group_name = None
296 old_group_name = None
297 group_name = value.get('group_name')
297 group_name = value.get('group_name')
298 group_name_full = value.get('group_name_full')
298 group_name_full = value.get('group_name_full')
299 group_parent_id = safe_int(value.get('group_parent_id'))
299 group_parent_id = safe_int(value.get('group_parent_id'))
300 if group_parent_id == -1:
300 if group_parent_id == -1:
301 group_parent_id = None
301 group_parent_id = None
302
302
303 group_obj = RepoGroup.get(old_data.get('group_id'))
303 group_obj = RepoGroup.get(old_data.get('group_id'))
304 parent_group_changed = False
304 parent_group_changed = False
305 if edit:
305 if edit:
306 old_group_name = group_obj.group_name
306 old_group_name = group_obj.group_name
307 old_group_parent_id = group_obj.group_parent_id
307 old_group_parent_id = group_obj.group_parent_id
308
308
309 if group_parent_id != old_group_parent_id:
309 if group_parent_id != old_group_parent_id:
310 parent_group_changed = True
310 parent_group_changed = True
311
311
312 # TODO: mikhail: the following if statement is not reached
312 # TODO: mikhail: the following if statement is not reached
313 # since group_parent_id's OneOf validation fails before.
313 # since group_parent_id's OneOf validation fails before.
314 # Can be removed.
314 # Can be removed.
315
315
316 # check against setting a parent of self
316 # check against setting a parent of self
317 parent_of_self = (
317 parent_of_self = (
318 old_data['group_id'] == group_parent_id
318 old_data['group_id'] == group_parent_id
319 if group_parent_id else False
319 if group_parent_id else False
320 )
320 )
321 if parent_of_self:
321 if parent_of_self:
322 msg = M(self, 'group_parent_id', state)
322 msg = M(self, 'group_parent_id', state)
323 raise formencode.Invalid(
323 raise formencode.Invalid(
324 msg, value, state, error_dict={'group_parent_id': msg}
324 msg, value, state, error_dict={'group_parent_id': msg}
325 )
325 )
326
326
327 # group we're moving current group inside
327 # group we're moving current group inside
328 child_group = None
328 child_group = None
329 if group_parent_id:
329 if group_parent_id:
330 child_group = RepoGroup.query().filter(
330 child_group = RepoGroup.query().filter(
331 RepoGroup.group_id == group_parent_id).scalar()
331 RepoGroup.group_id == group_parent_id).scalar()
332
332
333 # do a special check that we cannot move a group to one of
333 # do a special check that we cannot move a group to one of
334 # it's children
334 # it's children
335 if edit and child_group:
335 if edit and child_group:
336 parents = [x.group_id for x in child_group.parents]
336 parents = [x.group_id for x in child_group.parents]
337 move_to_children = old_data['group_id'] in parents
337 move_to_children = old_data['group_id'] in parents
338 if move_to_children:
338 if move_to_children:
339 msg = M(self, 'group_parent_id', state)
339 msg = M(self, 'group_parent_id', state)
340 raise formencode.Invalid(
340 raise formencode.Invalid(
341 msg, value, state, error_dict={'group_parent_id': msg})
341 msg, value, state, error_dict={'group_parent_id': msg})
342
342
343 # Check if we have permission to store in the parent.
343 # Check if we have permission to store in the parent.
344 # Only check if the parent group changed.
344 # Only check if the parent group changed.
345 if parent_group_changed:
345 if parent_group_changed:
346 if child_group is None:
346 if child_group is None:
347 if not can_create_in_root:
347 if not can_create_in_root:
348 msg = M(self, 'permission_denied_root', state)
348 msg = M(self, 'permission_denied_root', state)
349 raise formencode.Invalid(
349 raise formencode.Invalid(
350 msg, value, state,
350 msg, value, state,
351 error_dict={'group_parent_id': msg})
351 error_dict={'group_parent_id': msg})
352 else:
352 else:
353 valid = HasRepoGroupPermissionAny('group.admin')
353 valid = HasRepoGroupPermissionAny('group.admin')
354 forbidden = not valid(
354 forbidden = not valid(
355 child_group.group_name, 'can create group validator')
355 child_group.group_name, 'can create group validator')
356 if forbidden:
356 if forbidden:
357 msg = M(self, 'permission_denied', state)
357 msg = M(self, 'permission_denied', state)
358 raise formencode.Invalid(
358 raise formencode.Invalid(
359 msg, value, state,
359 msg, value, state,
360 error_dict={'group_parent_id': msg})
360 error_dict={'group_parent_id': msg})
361
361
362 # if we change the name or it's new group, check for existing names
362 # if we change the name or it's new group, check for existing names
363 # or repositories with the same name
363 # or repositories with the same name
364 if old_group_name != group_name_full or not edit:
364 if old_group_name != group_name_full or not edit:
365 # check group
365 # check group
366 gr = RepoGroup.get_by_group_name(group_name_full)
366 gr = RepoGroup.get_by_group_name(group_name_full)
367 if gr:
367 if gr:
368 msg = M(self, 'group_exists', state, group_name=group_name)
368 msg = M(self, 'group_exists', state, group_name=group_name)
369 raise formencode.Invalid(
369 raise formencode.Invalid(
370 msg, value, state, error_dict={'group_name': msg})
370 msg, value, state, error_dict={'group_name': msg})
371
371
372 # check for same repo
372 # check for same repo
373 repo = Repository.get_by_repo_name(group_name_full)
373 repo = Repository.get_by_repo_name(group_name_full)
374 if repo:
374 if repo:
375 msg = M(self, 'repo_exists', state, group_name=group_name)
375 msg = M(self, 'repo_exists', state, group_name=group_name)
376 raise formencode.Invalid(
376 raise formencode.Invalid(
377 msg, value, state, error_dict={'group_name': msg})
377 msg, value, state, error_dict={'group_name': msg})
378
378
379 return _validator
379 return _validator
380
380
381
381
382 def ValidPassword():
382 def ValidPassword():
383 class _validator(formencode.validators.FancyValidator):
383 class _validator(formencode.validators.FancyValidator):
384 messages = {
384 messages = {
385 'invalid_password':
385 'invalid_password':
386 _(u'Invalid characters (non-ascii) in password')
386 _(u'Invalid characters (non-ascii) in password')
387 }
387 }
388
388
389 def validate_python(self, value, state):
389 def validate_python(self, value, state):
390 try:
390 try:
391 (value or '').decode('ascii')
391 (value or '').decode('ascii')
392 except UnicodeError:
392 except UnicodeError:
393 msg = M(self, 'invalid_password', state)
393 msg = M(self, 'invalid_password', state)
394 raise formencode.Invalid(msg, value, state,)
394 raise formencode.Invalid(msg, value, state,)
395 return _validator
395 return _validator
396
396
397
397
398 def ValidOldPassword(username):
398 def ValidOldPassword(username):
399 class _validator(formencode.validators.FancyValidator):
399 class _validator(formencode.validators.FancyValidator):
400 messages = {
400 messages = {
401 'invalid_password': _(u'Invalid old password')
401 'invalid_password': _(u'Invalid old password')
402 }
402 }
403
403
404 def validate_python(self, value, state):
404 def validate_python(self, value, state):
405 from rhodecode.authentication.base import authenticate, HTTP_TYPE
405 from rhodecode.authentication.base import authenticate, HTTP_TYPE
406 if not authenticate(username, value, '', HTTP_TYPE):
406 if not authenticate(username, value, '', HTTP_TYPE):
407 msg = M(self, 'invalid_password', state)
407 msg = M(self, 'invalid_password', state)
408 raise formencode.Invalid(
408 raise formencode.Invalid(
409 msg, value, state, error_dict={'current_password': msg}
409 msg, value, state, error_dict={'current_password': msg}
410 )
410 )
411 return _validator
411 return _validator
412
412
413
413
414 def ValidPasswordsMatch(
414 def ValidPasswordsMatch(
415 passwd='new_password', passwd_confirmation='password_confirmation'):
415 passwd='new_password', passwd_confirmation='password_confirmation'):
416 class _validator(formencode.validators.FancyValidator):
416 class _validator(formencode.validators.FancyValidator):
417 messages = {
417 messages = {
418 'password_mismatch': _(u'Passwords do not match'),
418 'password_mismatch': _(u'Passwords do not match'),
419 }
419 }
420
420
421 def validate_python(self, value, state):
421 def validate_python(self, value, state):
422
422
423 pass_val = value.get('password') or value.get(passwd)
423 pass_val = value.get('password') or value.get(passwd)
424 if pass_val != value[passwd_confirmation]:
424 if pass_val != value[passwd_confirmation]:
425 msg = M(self, 'password_mismatch', state)
425 msg = M(self, 'password_mismatch', state)
426 raise formencode.Invalid(
426 raise formencode.Invalid(
427 msg, value, state,
427 msg, value, state,
428 error_dict={passwd: msg, passwd_confirmation: msg}
428 error_dict={passwd: msg, passwd_confirmation: msg}
429 )
429 )
430 return _validator
430 return _validator
431
431
432
432
433 def ValidAuth():
433 def ValidAuth():
434 class _validator(formencode.validators.FancyValidator):
434 class _validator(formencode.validators.FancyValidator):
435 messages = {
435 messages = {
436 'invalid_password': _(u'invalid password'),
436 'invalid_password': _(u'invalid password'),
437 'invalid_username': _(u'invalid user name'),
437 'invalid_username': _(u'invalid user name'),
438 'disabled_account': _(u'Your account is disabled')
438 'disabled_account': _(u'Your account is disabled')
439 }
439 }
440
440
441 def validate_python(self, value, state):
441 def validate_python(self, value, state):
442 from rhodecode.authentication.base import authenticate, HTTP_TYPE
442 from rhodecode.authentication.base import authenticate, HTTP_TYPE
443
443
444 password = value['password']
444 password = value['password']
445 username = value['username']
445 username = value['username']
446
446
447 if not authenticate(username, password, '', HTTP_TYPE,
447 if not authenticate(username, password, '', HTTP_TYPE,
448 skip_missing=True):
448 skip_missing=True):
449 user = User.get_by_username(username)
449 user = User.get_by_username(username)
450 if user and not user.active:
450 if user and not user.active:
451 log.warning('user %s is disabled', username)
451 log.warning('user %s is disabled', username)
452 msg = M(self, 'disabled_account', state)
452 msg = M(self, 'disabled_account', state)
453 raise formencode.Invalid(
453 raise formencode.Invalid(
454 msg, value, state, error_dict={'username': msg}
454 msg, value, state, error_dict={'username': msg}
455 )
455 )
456 else:
456 else:
457 log.warning('user `%s` failed to authenticate', username)
457 log.warning('user `%s` failed to authenticate', username)
458 msg = M(self, 'invalid_username', state)
458 msg = M(self, 'invalid_username', state)
459 msg2 = M(self, 'invalid_password', state)
459 msg2 = M(self, 'invalid_password', state)
460 raise formencode.Invalid(
460 raise formencode.Invalid(
461 msg, value, state,
461 msg, value, state,
462 error_dict={'username': msg, 'password': msg2}
462 error_dict={'username': msg, 'password': msg2}
463 )
463 )
464 return _validator
464 return _validator
465
465
466
466
467 def ValidAuthToken():
467 def ValidAuthToken():
468 class _validator(formencode.validators.FancyValidator):
468 class _validator(formencode.validators.FancyValidator):
469 messages = {
469 messages = {
470 'invalid_token': _(u'Token mismatch')
470 'invalid_token': _(u'Token mismatch')
471 }
471 }
472
472
473 def validate_python(self, value, state):
473 def validate_python(self, value, state):
474 if value != authentication_token():
474 if value != authentication_token():
475 msg = M(self, 'invalid_token', state)
475 msg = M(self, 'invalid_token', state)
476 raise formencode.Invalid(msg, value, state)
476 raise formencode.Invalid(msg, value, state)
477 return _validator
477 return _validator
478
478
479
479
480 def ValidRepoName(edit=False, old_data={}):
480 def ValidRepoName(edit=False, old_data={}):
481 class _validator(formencode.validators.FancyValidator):
481 class _validator(formencode.validators.FancyValidator):
482 messages = {
482 messages = {
483 'invalid_repo_name':
483 'invalid_repo_name':
484 _(u'Repository name %(repo)s is disallowed'),
484 _(u'Repository name %(repo)s is disallowed'),
485 # top level
485 # top level
486 'repository_exists': _(u'Repository with name %(repo)s '
486 'repository_exists': _(u'Repository with name %(repo)s '
487 u'already exists'),
487 u'already exists'),
488 'group_exists': _(u'Repository group with name "%(repo)s" '
488 'group_exists': _(u'Repository group with name "%(repo)s" '
489 u'already exists'),
489 u'already exists'),
490 # inside a group
490 # inside a group
491 'repository_in_group_exists': _(u'Repository with name %(repo)s '
491 'repository_in_group_exists': _(u'Repository with name %(repo)s '
492 u'exists in group "%(group)s"'),
492 u'exists in group "%(group)s"'),
493 'group_in_group_exists': _(
493 'group_in_group_exists': _(
494 u'Repository group with name "%(repo)s" '
494 u'Repository group with name "%(repo)s" '
495 u'exists in group "%(group)s"'),
495 u'exists in group "%(group)s"'),
496 }
496 }
497
497
498 def _to_python(self, value, state):
498 def _to_python(self, value, state):
499 repo_name = repo_name_slug(value.get('repo_name', ''))
499 repo_name = repo_name_slug(value.get('repo_name', ''))
500 repo_group = value.get('repo_group')
500 repo_group = value.get('repo_group')
501 if repo_group:
501 if repo_group:
502 gr = RepoGroup.get(repo_group)
502 gr = RepoGroup.get(repo_group)
503 group_path = gr.full_path
503 group_path = gr.full_path
504 group_name = gr.group_name
504 group_name = gr.group_name
505 # value needs to be aware of group name in order to check
505 # value needs to be aware of group name in order to check
506 # db key This is an actual just the name to store in the
506 # db key This is an actual just the name to store in the
507 # database
507 # database
508 repo_name_full = group_path + RepoGroup.url_sep() + repo_name
508 repo_name_full = group_path + RepoGroup.url_sep() + repo_name
509 else:
509 else:
510 group_name = group_path = ''
510 group_name = group_path = ''
511 repo_name_full = repo_name
511 repo_name_full = repo_name
512
512
513 value['repo_name'] = repo_name
513 value['repo_name'] = repo_name
514 value['repo_name_full'] = repo_name_full
514 value['repo_name_full'] = repo_name_full
515 value['group_path'] = group_path
515 value['group_path'] = group_path
516 value['group_name'] = group_name
516 value['group_name'] = group_name
517 return value
517 return value
518
518
519 def validate_python(self, value, state):
519 def validate_python(self, value, state):
520
520
521 repo_name = value.get('repo_name')
521 repo_name = value.get('repo_name')
522 repo_name_full = value.get('repo_name_full')
522 repo_name_full = value.get('repo_name_full')
523 group_path = value.get('group_path')
523 group_path = value.get('group_path')
524 group_name = value.get('group_name')
524 group_name = value.get('group_name')
525
525
526 if repo_name in [ADMIN_PREFIX, '']:
526 if repo_name in [ADMIN_PREFIX, '']:
527 msg = M(self, 'invalid_repo_name', state, repo=repo_name)
527 msg = M(self, 'invalid_repo_name', state, repo=repo_name)
528 raise formencode.Invalid(
528 raise formencode.Invalid(
529 msg, value, state, error_dict={'repo_name': msg})
529 msg, value, state, error_dict={'repo_name': msg})
530
530
531 rename = old_data.get('repo_name') != repo_name_full
531 rename = old_data.get('repo_name') != repo_name_full
532 create = not edit
532 create = not edit
533 if rename or create:
533 if rename or create:
534
534
535 if group_path:
535 if group_path:
536 if Repository.get_by_repo_name(repo_name_full):
536 if Repository.get_by_repo_name(repo_name_full):
537 msg = M(self, 'repository_in_group_exists', state,
537 msg = M(self, 'repository_in_group_exists', state,
538 repo=repo_name, group=group_name)
538 repo=repo_name, group=group_name)
539 raise formencode.Invalid(
539 raise formencode.Invalid(
540 msg, value, state, error_dict={'repo_name': msg})
540 msg, value, state, error_dict={'repo_name': msg})
541 if RepoGroup.get_by_group_name(repo_name_full):
541 if RepoGroup.get_by_group_name(repo_name_full):
542 msg = M(self, 'group_in_group_exists', state,
542 msg = M(self, 'group_in_group_exists', state,
543 repo=repo_name, group=group_name)
543 repo=repo_name, group=group_name)
544 raise formencode.Invalid(
544 raise formencode.Invalid(
545 msg, value, state, error_dict={'repo_name': msg})
545 msg, value, state, error_dict={'repo_name': msg})
546 else:
546 else:
547 if RepoGroup.get_by_group_name(repo_name_full):
547 if RepoGroup.get_by_group_name(repo_name_full):
548 msg = M(self, 'group_exists', state, repo=repo_name)
548 msg = M(self, 'group_exists', state, repo=repo_name)
549 raise formencode.Invalid(
549 raise formencode.Invalid(
550 msg, value, state, error_dict={'repo_name': msg})
550 msg, value, state, error_dict={'repo_name': msg})
551
551
552 if Repository.get_by_repo_name(repo_name_full):
552 if Repository.get_by_repo_name(repo_name_full):
553 msg = M(
553 msg = M(
554 self, 'repository_exists', state, repo=repo_name)
554 self, 'repository_exists', state, repo=repo_name)
555 raise formencode.Invalid(
555 raise formencode.Invalid(
556 msg, value, state, error_dict={'repo_name': msg})
556 msg, value, state, error_dict={'repo_name': msg})
557 return value
557 return value
558 return _validator
558 return _validator
559
559
560
560
561 def ValidForkName(*args, **kwargs):
561 def ValidForkName(*args, **kwargs):
562 return ValidRepoName(*args, **kwargs)
562 return ValidRepoName(*args, **kwargs)
563
563
564
564
565 def SlugifyName():
565 def SlugifyName():
566 class _validator(formencode.validators.FancyValidator):
566 class _validator(formencode.validators.FancyValidator):
567
567
568 def _to_python(self, value, state):
568 def _to_python(self, value, state):
569 return repo_name_slug(value)
569 return repo_name_slug(value)
570
570
571 def validate_python(self, value, state):
571 def validate_python(self, value, state):
572 pass
572 pass
573
573
574 return _validator
574 return _validator
575
575
576
576
577 def CannotHaveGitSuffix():
577 def CannotHaveGitSuffix():
578 class _validator(formencode.validators.FancyValidator):
578 class _validator(formencode.validators.FancyValidator):
579 messages = {
579 messages = {
580 'has_git_suffix':
580 'has_git_suffix':
581 _(u'Repository name cannot end with .git'),
581 _(u'Repository name cannot end with .git'),
582 }
582 }
583
583
584 def _to_python(self, value, state):
584 def _to_python(self, value, state):
585 return value
585 return value
586
586
587 def validate_python(self, value, state):
587 def validate_python(self, value, state):
588 if value and value.endswith('.git'):
588 if value and value.endswith('.git'):
589 msg = M(
589 msg = M(
590 self, 'has_git_suffix', state)
590 self, 'has_git_suffix', state)
591 raise formencode.Invalid(
591 raise formencode.Invalid(
592 msg, value, state, error_dict={'repo_name': msg})
592 msg, value, state, error_dict={'repo_name': msg})
593
593
594 return _validator
594 return _validator
595
595
596
596
597 def ValidCloneUri():
597 def ValidCloneUri():
598 class InvalidCloneUrl(Exception):
598 class InvalidCloneUrl(Exception):
599 allowed_prefixes = ()
599 allowed_prefixes = ()
600
600
601 def url_handler(repo_type, url):
601 def url_handler(repo_type, url):
602 config = make_db_config(clear_session=False)
602 config = make_db_config(clear_session=False)
603 if repo_type == 'hg':
603 if repo_type == 'hg':
604 allowed_prefixes = ('http', 'svn+http', 'git+http')
604 allowed_prefixes = ('http', 'svn+http', 'git+http')
605
605
606 if 'http' in url[:4]:
606 if 'http' in url[:4]:
607 # initially check if it's at least the proper URL
607 # initially check if it's at least the proper URL
608 # or does it pass basic auth
608 # or does it pass basic auth
609 MercurialRepository.check_url(url, config)
609 MercurialRepository.check_url(url, config)
610 elif 'svn+http' in url[:8]: # svn->hg import
610 elif 'svn+http' in url[:8]: # svn->hg import
611 SubversionRepository.check_url(url, config)
611 SubversionRepository.check_url(url, config)
612 elif 'git+http' in url[:8]: # git->hg import
612 elif 'git+http' in url[:8]: # git->hg import
613 raise NotImplementedError()
613 raise NotImplementedError()
614 else:
614 else:
615 exc = InvalidCloneUrl('Clone from URI %s not allowed. '
615 exc = InvalidCloneUrl('Clone from URI %s not allowed. '
616 'Allowed url must start with one of %s'
616 'Allowed url must start with one of %s'
617 % (url, ','.join(allowed_prefixes)))
617 % (url, ','.join(allowed_prefixes)))
618 exc.allowed_prefixes = allowed_prefixes
618 exc.allowed_prefixes = allowed_prefixes
619 raise exc
619 raise exc
620
620
621 elif repo_type == 'git':
621 elif repo_type == 'git':
622 allowed_prefixes = ('http', 'svn+http', 'hg+http')
622 allowed_prefixes = ('http', 'svn+http', 'hg+http')
623 if 'http' in url[:4]:
623 if 'http' in url[:4]:
624 # initially check if it's at least the proper URL
624 # initially check if it's at least the proper URL
625 # or does it pass basic auth
625 # or does it pass basic auth
626 GitRepository.check_url(url, config)
626 GitRepository.check_url(url, config)
627 elif 'svn+http' in url[:8]: # svn->git import
627 elif 'svn+http' in url[:8]: # svn->git import
628 raise NotImplementedError()
628 raise NotImplementedError()
629 elif 'hg+http' in url[:8]: # hg->git import
629 elif 'hg+http' in url[:8]: # hg->git import
630 raise NotImplementedError()
630 raise NotImplementedError()
631 else:
631 else:
632 exc = InvalidCloneUrl('Clone from URI %s not allowed. '
632 exc = InvalidCloneUrl('Clone from URI %s not allowed. '
633 'Allowed url must start with one of %s'
633 'Allowed url must start with one of %s'
634 % (url, ','.join(allowed_prefixes)))
634 % (url, ','.join(allowed_prefixes)))
635 exc.allowed_prefixes = allowed_prefixes
635 exc.allowed_prefixes = allowed_prefixes
636 raise exc
636 raise exc
637
637
638 class _validator(formencode.validators.FancyValidator):
638 class _validator(formencode.validators.FancyValidator):
639 messages = {
639 messages = {
640 'clone_uri': _(u'invalid clone url for %(rtype)s repository'),
640 'clone_uri': _(u'invalid clone url for %(rtype)s repository'),
641 'invalid_clone_uri': _(
641 'invalid_clone_uri': _(
642 u'Invalid clone url, provide a valid clone '
642 u'Invalid clone url, provide a valid clone '
643 u'url starting with one of %(allowed_prefixes)s')
643 u'url starting with one of %(allowed_prefixes)s')
644 }
644 }
645
645
646 def validate_python(self, value, state):
646 def validate_python(self, value, state):
647 repo_type = value.get('repo_type')
647 repo_type = value.get('repo_type')
648 url = value.get('clone_uri')
648 url = value.get('clone_uri')
649
649
650 if url:
650 if url:
651 try:
651 try:
652 url_handler(repo_type, url)
652 url_handler(repo_type, url)
653 except InvalidCloneUrl as e:
653 except InvalidCloneUrl as e:
654 log.warning(e)
654 log.warning(e)
655 msg = M(self, 'invalid_clone_uri', rtype=repo_type,
655 msg = M(self, 'invalid_clone_uri', rtype=repo_type,
656 allowed_prefixes=','.join(e.allowed_prefixes))
656 allowed_prefixes=','.join(e.allowed_prefixes))
657 raise formencode.Invalid(msg, value, state,
657 raise formencode.Invalid(msg, value, state,
658 error_dict={'clone_uri': msg})
658 error_dict={'clone_uri': msg})
659 except Exception:
659 except Exception:
660 log.exception('Url validation failed')
660 log.exception('Url validation failed')
661 msg = M(self, 'clone_uri', rtype=repo_type)
661 msg = M(self, 'clone_uri', rtype=repo_type)
662 raise formencode.Invalid(msg, value, state,
662 raise formencode.Invalid(msg, value, state,
663 error_dict={'clone_uri': msg})
663 error_dict={'clone_uri': msg})
664 return _validator
664 return _validator
665
665
666
666
667 def ValidForkType(old_data={}):
667 def ValidForkType(old_data={}):
668 class _validator(formencode.validators.FancyValidator):
668 class _validator(formencode.validators.FancyValidator):
669 messages = {
669 messages = {
670 'invalid_fork_type': _(u'Fork have to be the same type as parent')
670 'invalid_fork_type': _(u'Fork have to be the same type as parent')
671 }
671 }
672
672
673 def validate_python(self, value, state):
673 def validate_python(self, value, state):
674 if old_data['repo_type'] != value:
674 if old_data['repo_type'] != value:
675 msg = M(self, 'invalid_fork_type', state)
675 msg = M(self, 'invalid_fork_type', state)
676 raise formencode.Invalid(
676 raise formencode.Invalid(
677 msg, value, state, error_dict={'repo_type': msg}
677 msg, value, state, error_dict={'repo_type': msg}
678 )
678 )
679 return _validator
679 return _validator
680
680
681
681
682 def CanWriteGroup(old_data=None):
682 def CanWriteGroup(old_data=None):
683 class _validator(formencode.validators.FancyValidator):
683 class _validator(formencode.validators.FancyValidator):
684 messages = {
684 messages = {
685 'permission_denied': _(
685 'permission_denied': _(
686 u"You do not have the permission "
686 u"You do not have the permission "
687 u"to create repositories in this group."),
687 u"to create repositories in this group."),
688 'permission_denied_root': _(
688 'permission_denied_root': _(
689 u"You do not have the permission to store repositories in "
689 u"You do not have the permission to store repositories in "
690 u"the root location.")
690 u"the root location.")
691 }
691 }
692
692
693 def _to_python(self, value, state):
693 def _to_python(self, value, state):
694 # root location
694 # root location
695 if value in [-1, "-1"]:
695 if value in [-1, "-1"]:
696 return None
696 return None
697 return value
697 return value
698
698
699 def validate_python(self, value, state):
699 def validate_python(self, value, state):
700 gr = RepoGroup.get(value)
700 gr = RepoGroup.get(value)
701 gr_name = gr.group_name if gr else None # None means ROOT location
701 gr_name = gr.group_name if gr else None # None means ROOT location
702 # create repositories with write permission on group is set to true
702 # create repositories with write permission on group is set to true
703 create_on_write = HasPermissionAny(
703 create_on_write = HasPermissionAny(
704 'hg.create.write_on_repogroup.true')()
704 'hg.create.write_on_repogroup.true')()
705 group_admin = HasRepoGroupPermissionAny('group.admin')(
705 group_admin = HasRepoGroupPermissionAny('group.admin')(
706 gr_name, 'can write into group validator')
706 gr_name, 'can write into group validator')
707 group_write = HasRepoGroupPermissionAny('group.write')(
707 group_write = HasRepoGroupPermissionAny('group.write')(
708 gr_name, 'can write into group validator')
708 gr_name, 'can write into group validator')
709 forbidden = not (group_admin or (group_write and create_on_write))
709 forbidden = not (group_admin or (group_write and create_on_write))
710 can_create_repos = HasPermissionAny(
710 can_create_repos = HasPermissionAny(
711 'hg.admin', 'hg.create.repository')
711 'hg.admin', 'hg.create.repository')
712 gid = (old_data['repo_group'].get('group_id')
712 gid = (old_data['repo_group'].get('group_id')
713 if (old_data and 'repo_group' in old_data) else None)
713 if (old_data and 'repo_group' in old_data) else None)
714 value_changed = gid != safe_int(value)
714 value_changed = gid != safe_int(value)
715 new = not old_data
715 new = not old_data
716 # do check if we changed the value, there's a case that someone got
716 # do check if we changed the value, there's a case that someone got
717 # revoked write permissions to a repository, he still created, we
717 # revoked write permissions to a repository, he still created, we
718 # don't need to check permission if he didn't change the value of
718 # don't need to check permission if he didn't change the value of
719 # groups in form box
719 # groups in form box
720 if value_changed or new:
720 if value_changed or new:
721 # parent group need to be existing
721 # parent group need to be existing
722 if gr and forbidden:
722 if gr and forbidden:
723 msg = M(self, 'permission_denied', state)
723 msg = M(self, 'permission_denied', state)
724 raise formencode.Invalid(
724 raise formencode.Invalid(
725 msg, value, state, error_dict={'repo_type': msg}
725 msg, value, state, error_dict={'repo_type': msg}
726 )
726 )
727 # check if we can write to root location !
727 # check if we can write to root location !
728 elif gr is None and not can_create_repos():
728 elif gr is None and not can_create_repos():
729 msg = M(self, 'permission_denied_root', state)
729 msg = M(self, 'permission_denied_root', state)
730 raise formencode.Invalid(
730 raise formencode.Invalid(
731 msg, value, state, error_dict={'repo_type': msg}
731 msg, value, state, error_dict={'repo_type': msg}
732 )
732 )
733
733
734 return _validator
734 return _validator
735
735
736
736
737 def ValidPerms(type_='repo'):
737 def ValidPerms(type_='repo'):
738 if type_ == 'repo_group':
738 if type_ == 'repo_group':
739 EMPTY_PERM = 'group.none'
739 EMPTY_PERM = 'group.none'
740 elif type_ == 'repo':
740 elif type_ == 'repo':
741 EMPTY_PERM = 'repository.none'
741 EMPTY_PERM = 'repository.none'
742 elif type_ == 'user_group':
742 elif type_ == 'user_group':
743 EMPTY_PERM = 'usergroup.none'
743 EMPTY_PERM = 'usergroup.none'
744
744
745 class _validator(formencode.validators.FancyValidator):
745 class _validator(formencode.validators.FancyValidator):
746 messages = {
746 messages = {
747 'perm_new_member_name':
747 'perm_new_member_name':
748 _(u'This username or user group name is not valid')
748 _(u'This username or user group name is not valid')
749 }
749 }
750
750
751 def _to_python(self, value, state):
751 def _to_python(self, value, state):
752 perm_updates = OrderedSet()
752 perm_updates = OrderedSet()
753 perm_additions = OrderedSet()
753 perm_additions = OrderedSet()
754 perm_deletions = OrderedSet()
754 perm_deletions = OrderedSet()
755 # build a list of permission to update/delete and new permission
755 # build a list of permission to update/delete and new permission
756
756
757 # Read the perm_new_member/perm_del_member attributes and group
757 # Read the perm_new_member/perm_del_member attributes and group
758 # them by they IDs
758 # them by they IDs
759 new_perms_group = defaultdict(dict)
759 new_perms_group = defaultdict(dict)
760 del_perms_group = defaultdict(dict)
760 del_perms_group = defaultdict(dict)
761 for k, v in value.copy().iteritems():
761 for k, v in value.copy().iteritems():
762 if k.startswith('perm_del_member'):
762 if k.startswith('perm_del_member'):
763 # delete from org storage so we don't process that later
763 # delete from org storage so we don't process that later
764 del value[k]
764 del value[k]
765 # part is `id`, `type`
765 # part is `id`, `type`
766 _type, part = k.split('perm_del_member_')
766 _type, part = k.split('perm_del_member_')
767 args = part.split('_')
767 args = part.split('_')
768 if len(args) == 2:
768 if len(args) == 2:
769 _key, pos = args
769 _key, pos = args
770 del_perms_group[pos][_key] = v
770 del_perms_group[pos][_key] = v
771 if k.startswith('perm_new_member'):
771 if k.startswith('perm_new_member'):
772 # delete from org storage so we don't process that later
772 # delete from org storage so we don't process that later
773 del value[k]
773 del value[k]
774 # part is `id`, `type`, `perm`
774 # part is `id`, `type`, `perm`
775 _type, part = k.split('perm_new_member_')
775 _type, part = k.split('perm_new_member_')
776 args = part.split('_')
776 args = part.split('_')
777 if len(args) == 2:
777 if len(args) == 2:
778 _key, pos = args
778 _key, pos = args
779 new_perms_group[pos][_key] = v
779 new_perms_group[pos][_key] = v
780
780
781 # store the deletes
781 # store the deletes
782 for k in sorted(del_perms_group.keys()):
782 for k in sorted(del_perms_group.keys()):
783 perm_dict = del_perms_group[k]
783 perm_dict = del_perms_group[k]
784 del_member = perm_dict.get('id')
784 del_member = perm_dict.get('id')
785 del_type = perm_dict.get('type')
785 del_type = perm_dict.get('type')
786 if del_member and del_type:
786 if del_member and del_type:
787 perm_deletions.add((del_member, None, del_type))
787 perm_deletions.add(
788 (del_member, None, del_type))
788
789
789 # store additions in order of how they were added in web form
790 # store additions in order of how they were added in web form
790 for k in sorted(new_perms_group.keys()):
791 for k in sorted(new_perms_group.keys()):
791 perm_dict = new_perms_group[k]
792 perm_dict = new_perms_group[k]
792 new_member = perm_dict.get('id')
793 new_member = perm_dict.get('id')
793 new_type = perm_dict.get('type')
794 new_type = perm_dict.get('type')
794 new_perm = perm_dict.get('perm')
795 new_perm = perm_dict.get('perm')
795 if new_member and new_perm and new_type:
796 if new_member and new_perm and new_type:
796 perm_additions.add((new_member, new_perm, new_type))
797 perm_additions.add(
798 (new_member, new_perm, new_type))
797
799
798 # get updates of permissions
800 # get updates of permissions
799 # (read the existing radio button states)
801 # (read the existing radio button states)
802 default_user_id = User.get_default_user().user_id
800 for k, update_value in value.iteritems():
803 for k, update_value in value.iteritems():
801 if k.startswith('u_perm_') or k.startswith('g_perm_'):
804 if k.startswith('u_perm_') or k.startswith('g_perm_'):
802 member = k[7:]
805 member = k[7:]
803 update_type = {'u': 'user',
806 update_type = {'u': 'user',
804 'g': 'users_group'}[k[0]]
807 'g': 'users_group'}[k[0]]
805 if member == User.DEFAULT_USER:
808
809 if safe_int(member) == default_user_id:
806 if str2bool(value.get('repo_private')):
810 if str2bool(value.get('repo_private')):
807 # set none for default when updating to
811 # prevent from updating default user permissions
808 # private repo protects agains form manipulation
812 # when this repository is marked as private
809 update_value = EMPTY_PERM
813 update_value = EMPTY_PERM
810 perm_updates.add((member, update_value, update_type))
811 # check the deletes
812
814
813 value['perm_additions'] = list(perm_additions)
815 perm_updates.add(
816 (member, update_value, update_type))
817
818 value['perm_additions'] = [] # propagated later
814 value['perm_updates'] = list(perm_updates)
819 value['perm_updates'] = list(perm_updates)
815 value['perm_deletions'] = list(perm_deletions)
820 value['perm_deletions'] = list(perm_deletions)
816
821
817 # validate users they exist and they are active !
822 updates_map = dict(
818 for member_id, _perm, member_type in perm_additions:
823 (x[0], (x[1], x[2])) for x in value['perm_updates'])
824 # make sure Additions don't override updates.
825 for member_id, perm, member_type in list(perm_additions):
826 if member_id in updates_map:
827 perm = updates_map[member_id][0]
828 value['perm_additions'].append((member_id, perm, member_type))
829
830 # on new entries validate users they exist and they are active !
831 # this leaves feedback to the form
819 try:
832 try:
820 if member_type == 'user':
833 if member_type == 'user':
821 self.user_db = User.query()\
834 User.query()\
822 .filter(User.active == true())\
835 .filter(User.active == true())\
823 .filter(User.user_id == member_id).one()
836 .filter(User.user_id == member_id).one()
824 if member_type == 'users_group':
837 if member_type == 'users_group':
825 self.user_db = UserGroup.query()\
838 UserGroup.query()\
826 .filter(UserGroup.users_group_active == true())\
839 .filter(UserGroup.users_group_active == true())\
827 .filter(UserGroup.users_group_id == member_id)\
840 .filter(UserGroup.users_group_id == member_id)\
828 .one()
841 .one()
829
842
830 except Exception:
843 except Exception:
831 log.exception('Updated permission failed: org_exc:')
844 log.exception('Updated permission failed: org_exc:')
832 msg = M(self, 'perm_new_member_type', state)
845 msg = M(self, 'perm_new_member_type', state)
833 raise formencode.Invalid(
846 raise formencode.Invalid(
834 msg, value, state, error_dict={
847 msg, value, state, error_dict={
835 'perm_new_member_name': msg}
848 'perm_new_member_name': msg}
836 )
849 )
837 return value
850 return value
838 return _validator
851 return _validator
839
852
840
853
841 def ValidSettings():
854 def ValidSettings():
842 class _validator(formencode.validators.FancyValidator):
855 class _validator(formencode.validators.FancyValidator):
843 def _to_python(self, value, state):
856 def _to_python(self, value, state):
844 # settings form for users that are not admin
857 # settings form for users that are not admin
845 # can't edit certain parameters, it's extra backup if they mangle
858 # can't edit certain parameters, it's extra backup if they mangle
846 # with forms
859 # with forms
847
860
848 forbidden_params = [
861 forbidden_params = [
849 'user', 'repo_type', 'repo_enable_locking',
862 'user', 'repo_type', 'repo_enable_locking',
850 'repo_enable_downloads', 'repo_enable_statistics'
863 'repo_enable_downloads', 'repo_enable_statistics'
851 ]
864 ]
852
865
853 for param in forbidden_params:
866 for param in forbidden_params:
854 if param in value:
867 if param in value:
855 del value[param]
868 del value[param]
856 return value
869 return value
857
870
858 def validate_python(self, value, state):
871 def validate_python(self, value, state):
859 pass
872 pass
860 return _validator
873 return _validator
861
874
862
875
863 def ValidPath():
876 def ValidPath():
864 class _validator(formencode.validators.FancyValidator):
877 class _validator(formencode.validators.FancyValidator):
865 messages = {
878 messages = {
866 'invalid_path': _(u'This is not a valid path')
879 'invalid_path': _(u'This is not a valid path')
867 }
880 }
868
881
869 def validate_python(self, value, state):
882 def validate_python(self, value, state):
870 if not os.path.isdir(value):
883 if not os.path.isdir(value):
871 msg = M(self, 'invalid_path', state)
884 msg = M(self, 'invalid_path', state)
872 raise formencode.Invalid(
885 raise formencode.Invalid(
873 msg, value, state, error_dict={'paths_root_path': msg}
886 msg, value, state, error_dict={'paths_root_path': msg}
874 )
887 )
875 return _validator
888 return _validator
876
889
877
890
878 def UniqSystemEmail(old_data={}):
891 def UniqSystemEmail(old_data={}):
879 class _validator(formencode.validators.FancyValidator):
892 class _validator(formencode.validators.FancyValidator):
880 messages = {
893 messages = {
881 'email_taken': _(u'This e-mail address is already taken')
894 'email_taken': _(u'This e-mail address is already taken')
882 }
895 }
883
896
884 def _to_python(self, value, state):
897 def _to_python(self, value, state):
885 return value.lower()
898 return value.lower()
886
899
887 def validate_python(self, value, state):
900 def validate_python(self, value, state):
888 if (old_data.get('email') or '').lower() != value:
901 if (old_data.get('email') or '').lower() != value:
889 user = User.get_by_email(value, case_insensitive=True)
902 user = User.get_by_email(value, case_insensitive=True)
890 if user:
903 if user:
891 msg = M(self, 'email_taken', state)
904 msg = M(self, 'email_taken', state)
892 raise formencode.Invalid(
905 raise formencode.Invalid(
893 msg, value, state, error_dict={'email': msg}
906 msg, value, state, error_dict={'email': msg}
894 )
907 )
895 return _validator
908 return _validator
896
909
897
910
898 def ValidSystemEmail():
911 def ValidSystemEmail():
899 class _validator(formencode.validators.FancyValidator):
912 class _validator(formencode.validators.FancyValidator):
900 messages = {
913 messages = {
901 'non_existing_email': _(u'e-mail "%(email)s" does not exist.')
914 'non_existing_email': _(u'e-mail "%(email)s" does not exist.')
902 }
915 }
903
916
904 def _to_python(self, value, state):
917 def _to_python(self, value, state):
905 return value.lower()
918 return value.lower()
906
919
907 def validate_python(self, value, state):
920 def validate_python(self, value, state):
908 user = User.get_by_email(value, case_insensitive=True)
921 user = User.get_by_email(value, case_insensitive=True)
909 if user is None:
922 if user is None:
910 msg = M(self, 'non_existing_email', state, email=value)
923 msg = M(self, 'non_existing_email', state, email=value)
911 raise formencode.Invalid(
924 raise formencode.Invalid(
912 msg, value, state, error_dict={'email': msg}
925 msg, value, state, error_dict={'email': msg}
913 )
926 )
914
927
915 return _validator
928 return _validator
916
929
917
930
918 def NotReviewedRevisions(repo_id):
931 def NotReviewedRevisions(repo_id):
919 class _validator(formencode.validators.FancyValidator):
932 class _validator(formencode.validators.FancyValidator):
920 messages = {
933 messages = {
921 'rev_already_reviewed':
934 'rev_already_reviewed':
922 _(u'Revisions %(revs)s are already part of pull request '
935 _(u'Revisions %(revs)s are already part of pull request '
923 u'or have set status'),
936 u'or have set status'),
924 }
937 }
925
938
926 def validate_python(self, value, state):
939 def validate_python(self, value, state):
927 # check revisions if they are not reviewed, or a part of another
940 # check revisions if they are not reviewed, or a part of another
928 # pull request
941 # pull request
929 statuses = ChangesetStatus.query()\
942 statuses = ChangesetStatus.query()\
930 .filter(ChangesetStatus.revision.in_(value))\
943 .filter(ChangesetStatus.revision.in_(value))\
931 .filter(ChangesetStatus.repo_id == repo_id)\
944 .filter(ChangesetStatus.repo_id == repo_id)\
932 .all()
945 .all()
933
946
934 errors = []
947 errors = []
935 for status in statuses:
948 for status in statuses:
936 if status.pull_request_id:
949 if status.pull_request_id:
937 errors.append(['pull_req', status.revision[:12]])
950 errors.append(['pull_req', status.revision[:12]])
938 elif status.status:
951 elif status.status:
939 errors.append(['status', status.revision[:12]])
952 errors.append(['status', status.revision[:12]])
940
953
941 if errors:
954 if errors:
942 revs = ','.join([x[1] for x in errors])
955 revs = ','.join([x[1] for x in errors])
943 msg = M(self, 'rev_already_reviewed', state, revs=revs)
956 msg = M(self, 'rev_already_reviewed', state, revs=revs)
944 raise formencode.Invalid(
957 raise formencode.Invalid(
945 msg, value, state, error_dict={'revisions': revs})
958 msg, value, state, error_dict={'revisions': revs})
946
959
947 return _validator
960 return _validator
948
961
949
962
950 def ValidIp():
963 def ValidIp():
951 class _validator(CIDR):
964 class _validator(CIDR):
952 messages = {
965 messages = {
953 'badFormat': _(u'Please enter a valid IPv4 or IpV6 address'),
966 'badFormat': _(u'Please enter a valid IPv4 or IpV6 address'),
954 'illegalBits': _(
967 'illegalBits': _(
955 u'The network size (bits) must be within the range '
968 u'The network size (bits) must be within the range '
956 u'of 0-32 (not %(bits)r)'),
969 u'of 0-32 (not %(bits)r)'),
957 }
970 }
958
971
959 # we ovveride the default to_python() call
972 # we ovveride the default to_python() call
960 def to_python(self, value, state):
973 def to_python(self, value, state):
961 v = super(_validator, self).to_python(value, state)
974 v = super(_validator, self).to_python(value, state)
962 v = v.strip()
975 v = v.strip()
963 net = ipaddress.ip_network(address=v, strict=False)
976 net = ipaddress.ip_network(address=v, strict=False)
964 return str(net)
977 return str(net)
965
978
966 def validate_python(self, value, state):
979 def validate_python(self, value, state):
967 try:
980 try:
968 addr = value.strip()
981 addr = value.strip()
969 # this raises an ValueError if address is not IpV4 or IpV6
982 # this raises an ValueError if address is not IpV4 or IpV6
970 ipaddress.ip_network(addr, strict=False)
983 ipaddress.ip_network(addr, strict=False)
971 except ValueError:
984 except ValueError:
972 raise formencode.Invalid(self.message('badFormat', state),
985 raise formencode.Invalid(self.message('badFormat', state),
973 value, state)
986 value, state)
974
987
975 return _validator
988 return _validator
976
989
977
990
978 def FieldKey():
991 def FieldKey():
979 class _validator(formencode.validators.FancyValidator):
992 class _validator(formencode.validators.FancyValidator):
980 messages = {
993 messages = {
981 'badFormat': _(
994 'badFormat': _(
982 u'Key name can only consist of letters, '
995 u'Key name can only consist of letters, '
983 u'underscore, dash or numbers'),
996 u'underscore, dash or numbers'),
984 }
997 }
985
998
986 def validate_python(self, value, state):
999 def validate_python(self, value, state):
987 if not re.match('[a-zA-Z0-9_-]+$', value):
1000 if not re.match('[a-zA-Z0-9_-]+$', value):
988 raise formencode.Invalid(self.message('badFormat', state),
1001 raise formencode.Invalid(self.message('badFormat', state),
989 value, state)
1002 value, state)
990 return _validator
1003 return _validator
991
1004
992
1005
993 def ValidAuthPlugins():
1006 def ValidAuthPlugins():
994 class _validator(formencode.validators.FancyValidator):
1007 class _validator(formencode.validators.FancyValidator):
995 messages = {
1008 messages = {
996 'import_duplicate': _(
1009 'import_duplicate': _(
997 u'Plugins %(loaded)s and %(next_to_load)s '
1010 u'Plugins %(loaded)s and %(next_to_load)s '
998 u'both export the same name'),
1011 u'both export the same name'),
999 'missing_includeme': _(
1012 'missing_includeme': _(
1000 u'The plugin "%(plugin_id)s" is missing an includeme '
1013 u'The plugin "%(plugin_id)s" is missing an includeme '
1001 u'function.'),
1014 u'function.'),
1002 'import_error': _(
1015 'import_error': _(
1003 u'Can not load plugin "%(plugin_id)s"'),
1016 u'Can not load plugin "%(plugin_id)s"'),
1004 'no_plugin': _(
1017 'no_plugin': _(
1005 u'No plugin available with ID "%(plugin_id)s"'),
1018 u'No plugin available with ID "%(plugin_id)s"'),
1006 }
1019 }
1007
1020
1008 def _to_python(self, value, state):
1021 def _to_python(self, value, state):
1009 # filter empty values
1022 # filter empty values
1010 return filter(lambda s: s not in [None, ''], value)
1023 return filter(lambda s: s not in [None, ''], value)
1011
1024
1012 def _validate_legacy_plugin_id(self, plugin_id, value, state):
1025 def _validate_legacy_plugin_id(self, plugin_id, value, state):
1013 """
1026 """
1014 Validates that the plugin import works. It also checks that the
1027 Validates that the plugin import works. It also checks that the
1015 plugin has an includeme attribute.
1028 plugin has an includeme attribute.
1016 """
1029 """
1017 try:
1030 try:
1018 plugin = _import_legacy_plugin(plugin_id)
1031 plugin = _import_legacy_plugin(plugin_id)
1019 except Exception as e:
1032 except Exception as e:
1020 log.exception(
1033 log.exception(
1021 'Exception during import of auth legacy plugin "{}"'
1034 'Exception during import of auth legacy plugin "{}"'
1022 .format(plugin_id))
1035 .format(plugin_id))
1023 msg = M(self, 'import_error', plugin_id=plugin_id)
1036 msg = M(self, 'import_error', plugin_id=plugin_id)
1024 raise formencode.Invalid(msg, value, state)
1037 raise formencode.Invalid(msg, value, state)
1025
1038
1026 if not hasattr(plugin, 'includeme'):
1039 if not hasattr(plugin, 'includeme'):
1027 msg = M(self, 'missing_includeme', plugin_id=plugin_id)
1040 msg = M(self, 'missing_includeme', plugin_id=plugin_id)
1028 raise formencode.Invalid(msg, value, state)
1041 raise formencode.Invalid(msg, value, state)
1029
1042
1030 return plugin
1043 return plugin
1031
1044
1032 def _validate_plugin_id(self, plugin_id, value, state):
1045 def _validate_plugin_id(self, plugin_id, value, state):
1033 """
1046 """
1034 Plugins are already imported during app start up. Therefore this
1047 Plugins are already imported during app start up. Therefore this
1035 validation only retrieves the plugin from the plugin registry and
1048 validation only retrieves the plugin from the plugin registry and
1036 if it returns something not None everything is OK.
1049 if it returns something not None everything is OK.
1037 """
1050 """
1038 plugin = loadplugin(plugin_id)
1051 plugin = loadplugin(plugin_id)
1039
1052
1040 if plugin is None:
1053 if plugin is None:
1041 msg = M(self, 'no_plugin', plugin_id=plugin_id)
1054 msg = M(self, 'no_plugin', plugin_id=plugin_id)
1042 raise formencode.Invalid(msg, value, state)
1055 raise formencode.Invalid(msg, value, state)
1043
1056
1044 return plugin
1057 return plugin
1045
1058
1046 def validate_python(self, value, state):
1059 def validate_python(self, value, state):
1047 unique_names = {}
1060 unique_names = {}
1048 for plugin_id in value:
1061 for plugin_id in value:
1049
1062
1050 # Validate legacy or normal plugin.
1063 # Validate legacy or normal plugin.
1051 if plugin_id.startswith(legacy_plugin_prefix):
1064 if plugin_id.startswith(legacy_plugin_prefix):
1052 plugin = self._validate_legacy_plugin_id(
1065 plugin = self._validate_legacy_plugin_id(
1053 plugin_id, value, state)
1066 plugin_id, value, state)
1054 else:
1067 else:
1055 plugin = self._validate_plugin_id(plugin_id, value, state)
1068 plugin = self._validate_plugin_id(plugin_id, value, state)
1056
1069
1057 # Only allow unique plugin names.
1070 # Only allow unique plugin names.
1058 if plugin.name in unique_names:
1071 if plugin.name in unique_names:
1059 msg = M(self, 'import_duplicate', state,
1072 msg = M(self, 'import_duplicate', state,
1060 loaded=unique_names[plugin.name],
1073 loaded=unique_names[plugin.name],
1061 next_to_load=plugin)
1074 next_to_load=plugin)
1062 raise formencode.Invalid(msg, value, state)
1075 raise formencode.Invalid(msg, value, state)
1063 unique_names[plugin.name] = plugin
1076 unique_names[plugin.name] = plugin
1064
1077
1065 return _validator
1078 return _validator
1066
1079
1067
1080
1068 def ValidPattern():
1081 def ValidPattern():
1069
1082
1070 class _Validator(formencode.validators.FancyValidator):
1083 class _Validator(formencode.validators.FancyValidator):
1071
1084
1072 def _to_python(self, value, state):
1085 def _to_python(self, value, state):
1073 patterns = []
1086 patterns = []
1074
1087
1075 prefix = 'new_pattern'
1088 prefix = 'new_pattern'
1076 for name, v in value.iteritems():
1089 for name, v in value.iteritems():
1077 pattern_name = '_'.join((prefix, 'pattern'))
1090 pattern_name = '_'.join((prefix, 'pattern'))
1078 if name.startswith(pattern_name):
1091 if name.startswith(pattern_name):
1079 new_item_id = name[len(pattern_name)+1:]
1092 new_item_id = name[len(pattern_name)+1:]
1080
1093
1081 def _field(name):
1094 def _field(name):
1082 return '%s_%s_%s' % (prefix, name, new_item_id)
1095 return '%s_%s_%s' % (prefix, name, new_item_id)
1083
1096
1084 values = {
1097 values = {
1085 'issuetracker_pat': value.get(_field('pattern')),
1098 'issuetracker_pat': value.get(_field('pattern')),
1086 'issuetracker_pat': value.get(_field('pattern')),
1099 'issuetracker_pat': value.get(_field('pattern')),
1087 'issuetracker_url': value.get(_field('url')),
1100 'issuetracker_url': value.get(_field('url')),
1088 'issuetracker_pref': value.get(_field('prefix')),
1101 'issuetracker_pref': value.get(_field('prefix')),
1089 'issuetracker_desc': value.get(_field('description'))
1102 'issuetracker_desc': value.get(_field('description'))
1090 }
1103 }
1091 new_uid = md5(values['issuetracker_pat'])
1104 new_uid = md5(values['issuetracker_pat'])
1092
1105
1093 has_required_fields = (
1106 has_required_fields = (
1094 values['issuetracker_pat']
1107 values['issuetracker_pat']
1095 and values['issuetracker_url'])
1108 and values['issuetracker_url'])
1096
1109
1097 if has_required_fields:
1110 if has_required_fields:
1098 settings = [
1111 settings = [
1099 ('_'.join((key, new_uid)), values[key], 'unicode')
1112 ('_'.join((key, new_uid)), values[key], 'unicode')
1100 for key in values]
1113 for key in values]
1101 patterns.append(settings)
1114 patterns.append(settings)
1102
1115
1103 value['patterns'] = patterns
1116 value['patterns'] = patterns
1104 delete_patterns = value.get('uid') or []
1117 delete_patterns = value.get('uid') or []
1105 if not isinstance(delete_patterns, (list, tuple)):
1118 if not isinstance(delete_patterns, (list, tuple)):
1106 delete_patterns = [delete_patterns]
1119 delete_patterns = [delete_patterns]
1107 value['delete_patterns'] = delete_patterns
1120 value['delete_patterns'] = delete_patterns
1108 return value
1121 return value
1109 return _Validator
1122 return _Validator
@@ -1,118 +1,118 b''
1
1
2 /******************************************************************************
2 /******************************************************************************
3 * *
3 * *
4 * DO NOT CHANGE THIS FILE MANUALLY *
4 * DO NOT CHANGE THIS FILE MANUALLY *
5 * *
5 * *
6 * *
6 * *
7 * This file is automatically generated when the app starts up with *
7 * This file is automatically generated when the app starts up with *
8 * generate_js_files = true *
8 * generate_js_files = true *
9 * *
9 * *
10 * To add a route here pass jsroute=True to the route definition in the app *
10 * To add a route here pass jsroute=True to the route definition in the app *
11 * *
11 * *
12 ******************************************************************************/
12 ******************************************************************************/
13 function registerRCRoutes() {
13 function registerRCRoutes() {
14 // routes registration
14 // routes registration
15 pyroutes.register('home', '/', []);
15 pyroutes.register('home', '/', []);
16 pyroutes.register('new_repo', '/_admin/create_repository', []);
16 pyroutes.register('new_repo', '/_admin/create_repository', []);
17 pyroutes.register('edit_user', '/_admin/users/%(user_id)s/edit', ['user_id']);
17 pyroutes.register('edit_user', '/_admin/users/%(user_id)s/edit', ['user_id']);
18 pyroutes.register('edit_user_group_members', '/_admin/user_groups/%(user_group_id)s/edit/members', ['user_group_id']);
18 pyroutes.register('edit_user_group_members', '/_admin/user_groups/%(user_group_id)s/edit/members', ['user_group_id']);
19 pyroutes.register('gists', '/_admin/gists', []);
19 pyroutes.register('gists', '/_admin/gists', []);
20 pyroutes.register('new_gist', '/_admin/gists/new', []);
20 pyroutes.register('new_gist', '/_admin/gists/new', []);
21 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
21 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
22 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
22 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
23 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
23 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
24 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
24 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
25 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/default-reviewers', ['repo_name']);
25 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/default-reviewers', ['repo_name']);
26 pyroutes.register('changeset_home', '/%(repo_name)s/changeset/%(revision)s', ['repo_name', 'revision']);
26 pyroutes.register('changeset_home', '/%(repo_name)s/changeset/%(revision)s', ['repo_name', 'revision']);
27 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
28 pyroutes.register('changeset_comment', '/%(repo_name)s/changeset/%(revision)s/comment', ['repo_name', 'revision']);
27 pyroutes.register('changeset_comment', '/%(repo_name)s/changeset/%(revision)s/comment', ['repo_name', 'revision']);
29 pyroutes.register('changeset_comment_preview', '/%(repo_name)s/changeset/comment/preview', ['repo_name']);
28 pyroutes.register('changeset_comment_preview', '/%(repo_name)s/changeset/comment/preview', ['repo_name']);
30 pyroutes.register('changeset_comment_delete', '/%(repo_name)s/changeset/comment/%(comment_id)s/delete', ['repo_name', 'comment_id']);
29 pyroutes.register('changeset_comment_delete', '/%(repo_name)s/changeset/comment/%(comment_id)s/delete', ['repo_name', 'comment_id']);
31 pyroutes.register('changeset_info', '/%(repo_name)s/changeset_info/%(revision)s', ['repo_name', 'revision']);
30 pyroutes.register('changeset_info', '/%(repo_name)s/changeset_info/%(revision)s', ['repo_name', 'revision']);
32 pyroutes.register('compare_url', '/%(repo_name)s/compare/%(source_ref_type)s@%(source_ref)s...%(target_ref_type)s@%(target_ref)s', ['repo_name', 'source_ref_type', 'source_ref', 'target_ref_type', 'target_ref']);
31 pyroutes.register('compare_url', '/%(repo_name)s/compare/%(source_ref_type)s@%(source_ref)s...%(target_ref_type)s@%(target_ref)s', ['repo_name', 'source_ref_type', 'source_ref', 'target_ref_type', 'target_ref']);
33 pyroutes.register('pullrequest_home', '/%(repo_name)s/pull-request/new', ['repo_name']);
32 pyroutes.register('pullrequest_home', '/%(repo_name)s/pull-request/new', ['repo_name']);
34 pyroutes.register('pullrequest', '/%(repo_name)s/pull-request/new', ['repo_name']);
33 pyroutes.register('pullrequest', '/%(repo_name)s/pull-request/new', ['repo_name']);
35 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
34 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
36 pyroutes.register('pullrequest_repo_destinations', '/%(repo_name)s/pull-request/repo-destinations', ['repo_name']);
35 pyroutes.register('pullrequest_repo_destinations', '/%(repo_name)s/pull-request/repo-destinations', ['repo_name']);
37 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
36 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
38 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
37 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
39 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
38 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
40 pyroutes.register('pullrequest_comment', '/%(repo_name)s/pull-request-comment/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
39 pyroutes.register('pullrequest_comment', '/%(repo_name)s/pull-request-comment/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
41 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request-comment/%(comment_id)s/delete', ['repo_name', 'comment_id']);
40 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request-comment/%(comment_id)s/delete', ['repo_name', 'comment_id']);
42 pyroutes.register('changelog_home', '/%(repo_name)s/changelog', ['repo_name']);
41 pyroutes.register('changelog_home', '/%(repo_name)s/changelog', ['repo_name']);
43 pyroutes.register('changelog_file_home', '/%(repo_name)s/changelog/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
42 pyroutes.register('changelog_file_home', '/%(repo_name)s/changelog/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
44 pyroutes.register('changelog_elements', '/%(repo_name)s/changelog_details', ['repo_name']);
43 pyroutes.register('changelog_elements', '/%(repo_name)s/changelog_details', ['repo_name']);
45 pyroutes.register('files_home', '/%(repo_name)s/files/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
44 pyroutes.register('files_home', '/%(repo_name)s/files/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
46 pyroutes.register('files_history_home', '/%(repo_name)s/history/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
45 pyroutes.register('files_history_home', '/%(repo_name)s/history/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
47 pyroutes.register('files_authors_home', '/%(repo_name)s/authors/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
46 pyroutes.register('files_authors_home', '/%(repo_name)s/authors/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
48 pyroutes.register('files_annotate_home', '/%(repo_name)s/annotate/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
47 pyroutes.register('files_annotate_home', '/%(repo_name)s/annotate/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
49 pyroutes.register('files_annotate_previous', '/%(repo_name)s/annotate-previous/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
48 pyroutes.register('files_annotate_previous', '/%(repo_name)s/annotate-previous/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
50 pyroutes.register('files_archive_home', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
49 pyroutes.register('files_archive_home', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
51 pyroutes.register('files_nodelist_home', '/%(repo_name)s/nodelist/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
50 pyroutes.register('files_nodelist_home', '/%(repo_name)s/nodelist/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
52 pyroutes.register('files_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
51 pyroutes.register('files_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
53 pyroutes.register('summary_home_slash', '/%(repo_name)s/', ['repo_name']);
52 pyroutes.register('summary_home_slash', '/%(repo_name)s/', ['repo_name']);
54 pyroutes.register('summary_home', '/%(repo_name)s', ['repo_name']);
53 pyroutes.register('summary_home', '/%(repo_name)s', ['repo_name']);
55 pyroutes.register('favicon', '/favicon.ico', []);
54 pyroutes.register('favicon', '/favicon.ico', []);
56 pyroutes.register('robots', '/robots.txt', []);
55 pyroutes.register('robots', '/robots.txt', []);
57 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
56 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
58 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
57 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
59 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
58 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
60 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
59 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
61 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
60 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
62 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
61 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
63 pyroutes.register('repo_group_integrations_home', '%(repo_group_name)s/settings/integrations', ['repo_group_name']);
62 pyroutes.register('repo_group_integrations_home', '%(repo_group_name)s/settings/integrations', ['repo_group_name']);
64 pyroutes.register('repo_group_integrations_list', '%(repo_group_name)s/settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
63 pyroutes.register('repo_group_integrations_list', '%(repo_group_name)s/settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
65 pyroutes.register('repo_group_integrations_new', '%(repo_group_name)s/settings/integrations/new', ['repo_group_name']);
64 pyroutes.register('repo_group_integrations_new', '%(repo_group_name)s/settings/integrations/new', ['repo_group_name']);
66 pyroutes.register('repo_group_integrations_create', '%(repo_group_name)s/settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
65 pyroutes.register('repo_group_integrations_create', '%(repo_group_name)s/settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
67 pyroutes.register('repo_group_integrations_edit', '%(repo_group_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
66 pyroutes.register('repo_group_integrations_edit', '%(repo_group_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
68 pyroutes.register('repo_integrations_home', '%(repo_name)s/settings/integrations', ['repo_name']);
67 pyroutes.register('repo_integrations_home', '%(repo_name)s/settings/integrations', ['repo_name']);
69 pyroutes.register('repo_integrations_list', '%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
68 pyroutes.register('repo_integrations_list', '%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
70 pyroutes.register('repo_integrations_new', '%(repo_name)s/settings/integrations/new', ['repo_name']);
69 pyroutes.register('repo_integrations_new', '%(repo_name)s/settings/integrations/new', ['repo_name']);
71 pyroutes.register('repo_integrations_create', '%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
70 pyroutes.register('repo_integrations_create', '%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
72 pyroutes.register('repo_integrations_edit', '%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
71 pyroutes.register('repo_integrations_edit', '%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
73 pyroutes.register('ops_ping', '_admin/ops/ping', []);
72 pyroutes.register('ops_ping', '_admin/ops/ping', []);
74 pyroutes.register('admin_settings_open_source', '_admin/settings/open_source', []);
73 pyroutes.register('admin_settings_open_source', '_admin/settings/open_source', []);
75 pyroutes.register('admin_settings_vcs_svn_generate_cfg', '_admin/settings/vcs/svn_generate_cfg', []);
74 pyroutes.register('admin_settings_vcs_svn_generate_cfg', '_admin/settings/vcs/svn_generate_cfg', []);
76 pyroutes.register('admin_settings_system', '_admin/settings/system', []);
75 pyroutes.register('admin_settings_system', '_admin/settings/system', []);
77 pyroutes.register('admin_settings_system_update', '_admin/settings/system/updates', []);
76 pyroutes.register('admin_settings_system_update', '_admin/settings/system/updates', []);
78 pyroutes.register('admin_settings_sessions', '_admin/settings/sessions', []);
77 pyroutes.register('admin_settings_sessions', '_admin/settings/sessions', []);
79 pyroutes.register('admin_settings_sessions_cleanup', '_admin/settings/sessions/cleanup', []);
78 pyroutes.register('admin_settings_sessions_cleanup', '_admin/settings/sessions/cleanup', []);
80 pyroutes.register('users', '_admin/users', []);
79 pyroutes.register('users', '_admin/users', []);
81 pyroutes.register('users_data', '_admin/users_data', []);
80 pyroutes.register('users_data', '_admin/users_data', []);
82 pyroutes.register('edit_user_auth_tokens', '_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
81 pyroutes.register('edit_user_auth_tokens', '_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
83 pyroutes.register('edit_user_auth_tokens_add', '_admin/users/%(user_id)s/edit/auth_tokens/new', ['user_id']);
82 pyroutes.register('edit_user_auth_tokens_add', '_admin/users/%(user_id)s/edit/auth_tokens/new', ['user_id']);
84 pyroutes.register('edit_user_auth_tokens_delete', '_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
83 pyroutes.register('edit_user_auth_tokens_delete', '_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
85 pyroutes.register('edit_user_groups_management', '_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
84 pyroutes.register('edit_user_groups_management', '_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
86 pyroutes.register('edit_user_groups_management_updates', '_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
85 pyroutes.register('edit_user_groups_management_updates', '_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
87 pyroutes.register('edit_user_audit_logs', '_admin/users/%(user_id)s/edit/audit', ['user_id']);
86 pyroutes.register('edit_user_audit_logs', '_admin/users/%(user_id)s/edit/audit', ['user_id']);
88 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
87 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
89 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
88 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
90 pyroutes.register('channelstream_proxy', '/_channelstream', []);
89 pyroutes.register('channelstream_proxy', '/_channelstream', []);
91 pyroutes.register('login', '/_admin/login', []);
90 pyroutes.register('login', '/_admin/login', []);
92 pyroutes.register('logout', '/_admin/logout', []);
91 pyroutes.register('logout', '/_admin/logout', []);
93 pyroutes.register('register', '/_admin/register', []);
92 pyroutes.register('register', '/_admin/register', []);
94 pyroutes.register('reset_password', '/_admin/password_reset', []);
93 pyroutes.register('reset_password', '/_admin/password_reset', []);
95 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
94 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
96 pyroutes.register('user_autocomplete_data', '/_users', []);
95 pyroutes.register('user_autocomplete_data', '/_users', []);
97 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
96 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
98 pyroutes.register('repo_list_data', '/_repos', []);
97 pyroutes.register('repo_list_data', '/_repos', []);
99 pyroutes.register('goto_switcher_data', '/_goto_data', []);
98 pyroutes.register('goto_switcher_data', '/_goto_data', []);
100 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
99 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
101 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
100 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
101 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
102 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
102 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
103 pyroutes.register('repo_maintenance', '/%(repo_name)s/maintenance', ['repo_name']);
103 pyroutes.register('repo_maintenance', '/%(repo_name)s/maintenance', ['repo_name']);
104 pyroutes.register('repo_maintenance_execute', '/%(repo_name)s/maintenance/execute', ['repo_name']);
104 pyroutes.register('repo_maintenance_execute', '/%(repo_name)s/maintenance/execute', ['repo_name']);
105 pyroutes.register('strip', '/%(repo_name)s/strip', ['repo_name']);
105 pyroutes.register('strip', '/%(repo_name)s/strip', ['repo_name']);
106 pyroutes.register('strip_check', '/%(repo_name)s/strip_check', ['repo_name']);
106 pyroutes.register('strip_check', '/%(repo_name)s/strip_check', ['repo_name']);
107 pyroutes.register('strip_execute', '/%(repo_name)s/strip_execute', ['repo_name']);
107 pyroutes.register('strip_execute', '/%(repo_name)s/strip_execute', ['repo_name']);
108 pyroutes.register('search', '/_admin/search', []);
108 pyroutes.register('search', '/_admin/search', []);
109 pyroutes.register('search_repo', '/%(repo_name)s/search', ['repo_name']);
109 pyroutes.register('search_repo', '/%(repo_name)s/search', ['repo_name']);
110 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
110 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
111 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
111 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
112 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
112 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
113 pyroutes.register('my_account_password_update', '/_admin/my_account/password', []);
113 pyroutes.register('my_account_password_update', '/_admin/my_account/password', []);
114 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
114 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
115 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
115 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
116 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
116 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
117 pyroutes.register('apiv2', '/_admin/api', []);
117 pyroutes.register('apiv2', '/_admin/api', []);
118 }
118 }
@@ -1,95 +1,95 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 ##
2 ##
3 ## See also repo_settings.html
3 ## See also repo_settings.html
4 ##
4 ##
5 <%inherit file="/base/base.mako"/>
5 <%inherit file="/base/base.mako"/>
6
6
7 <%def name="title()">
7 <%def name="title()">
8 ${_('%s repository settings') % c.repo_info.repo_name}
8 ${_('%s repository settings') % c.repo_info.repo_name}
9 %if c.rhodecode_name:
9 %if c.rhodecode_name:
10 &middot; ${h.branding(c.rhodecode_name)}
10 &middot; ${h.branding(c.rhodecode_name)}
11 %endif
11 %endif
12 </%def>
12 </%def>
13
13
14 <%def name="breadcrumbs_links()">
14 <%def name="breadcrumbs_links()">
15 ${_('Settings')}
15 ${_('Settings')}
16 </%def>
16 </%def>
17
17
18 <%def name="menu_bar_nav()">
18 <%def name="menu_bar_nav()">
19 ${self.menu_items(active='repositories')}
19 ${self.menu_items(active='repositories')}
20 </%def>
20 </%def>
21
21
22 <%def name="menu_bar_subnav()">
22 <%def name="menu_bar_subnav()">
23 ${self.repo_menu(active='options')}
23 ${self.repo_menu(active='options')}
24 </%def>
24 </%def>
25
25
26 <%def name="main_content()">
26 <%def name="main_content()">
27 <%include file="/admin/repos/repo_edit_${c.active}.mako"/>
27 <%include file="/admin/repos/repo_edit_${c.active}.mako"/>
28 </%def>
28 </%def>
29
29
30
30
31 <%def name="main()">
31 <%def name="main()">
32 <div class="box">
32 <div class="box">
33 <div class="title">
33 <div class="title">
34 ${self.repo_page_title(c.rhodecode_db_repo)}
34 ${self.repo_page_title(c.rhodecode_db_repo)}
35 ${self.breadcrumbs()}
35 ${self.breadcrumbs()}
36 </div>
36 </div>
37
37
38 <div class="sidebar-col-wrapper scw-small">
38 <div class="sidebar-col-wrapper scw-small">
39 <div class="sidebar">
39 <div class="sidebar">
40 <ul class="nav nav-pills nav-stacked">
40 <ul class="nav nav-pills nav-stacked">
41 <li class="${'active' if c.active=='settings' else ''}">
41 <li class="${'active' if c.active=='settings' else ''}">
42 <a href="${h.route_path('edit_repo', repo_name=c.repo_name)}">${_('Settings')}</a>
42 <a href="${h.route_path('edit_repo', repo_name=c.repo_name)}">${_('Settings')}</a>
43 </li>
43 </li>
44 <li class="${'active' if c.active=='permissions' else ''}">
44 <li class="${'active' if c.active=='permissions' else ''}">
45 <a href="${h.url('edit_repo_perms', repo_name=c.repo_name)}">${_('Permissions')}</a>
45 <a href="${h.route_path('edit_repo_perms', repo_name=c.repo_name)}">${_('Permissions')}</a>
46 </li>
46 </li>
47 <li class="${'active' if c.active=='advanced' else ''}">
47 <li class="${'active' if c.active=='advanced' else ''}">
48 <a href="${h.url('edit_repo_advanced', repo_name=c.repo_name)}">${_('Advanced')}</a>
48 <a href="${h.url('edit_repo_advanced', repo_name=c.repo_name)}">${_('Advanced')}</a>
49 </li>
49 </li>
50 <li class="${'active' if c.active=='vcs' else ''}">
50 <li class="${'active' if c.active=='vcs' else ''}">
51 <a href="${h.url('repo_vcs_settings', repo_name=c.repo_name)}">${_('VCS')}</a>
51 <a href="${h.url('repo_vcs_settings', repo_name=c.repo_name)}">${_('VCS')}</a>
52 </li>
52 </li>
53 <li class="${'active' if c.active=='fields' else ''}">
53 <li class="${'active' if c.active=='fields' else ''}">
54 <a href="${h.url('edit_repo_fields', repo_name=c.repo_name)}">${_('Extra Fields')}</a>
54 <a href="${h.url('edit_repo_fields', repo_name=c.repo_name)}">${_('Extra Fields')}</a>
55 </li>
55 </li>
56 <li class="${'active' if c.active=='issuetracker' else ''}">
56 <li class="${'active' if c.active=='issuetracker' else ''}">
57 <a href="${h.url('repo_settings_issuetracker', repo_name=c.repo_name)}">${_('Issue Tracker')}</a>
57 <a href="${h.url('repo_settings_issuetracker', repo_name=c.repo_name)}">${_('Issue Tracker')}</a>
58 </li>
58 </li>
59 <li class="${'active' if c.active=='caches' else ''}">
59 <li class="${'active' if c.active=='caches' else ''}">
60 <a href="${h.route_path('edit_repo_caches', repo_name=c.repo_name)}">${_('Caches')}</a>
60 <a href="${h.route_path('edit_repo_caches', repo_name=c.repo_name)}">${_('Caches')}</a>
61 </li>
61 </li>
62 %if c.repo_info.repo_type != 'svn':
62 %if c.repo_info.repo_type != 'svn':
63 <li class="${'active' if c.active=='remote' else ''}">
63 <li class="${'active' if c.active=='remote' else ''}">
64 <a href="${h.url('edit_repo_remote', repo_name=c.repo_name)}">${_('Remote')}</a>
64 <a href="${h.url('edit_repo_remote', repo_name=c.repo_name)}">${_('Remote')}</a>
65 </li>
65 </li>
66 %endif
66 %endif
67 <li class="${'active' if c.active=='statistics' else ''}">
67 <li class="${'active' if c.active=='statistics' else ''}">
68 <a href="${h.url('edit_repo_statistics', repo_name=c.repo_name)}">${_('Statistics')}</a>
68 <a href="${h.url('edit_repo_statistics', repo_name=c.repo_name)}">${_('Statistics')}</a>
69 </li>
69 </li>
70 <li class="${'active' if c.active=='integrations' else ''}">
70 <li class="${'active' if c.active=='integrations' else ''}">
71 <a href="${h.route_path('repo_integrations_home', repo_name=c.repo_name)}">${_('Integrations')}</a>
71 <a href="${h.route_path('repo_integrations_home', repo_name=c.repo_name)}">${_('Integrations')}</a>
72 </li>
72 </li>
73 %if c.repo_info.repo_type != 'svn':
73 %if c.repo_info.repo_type != 'svn':
74 <li class="${'active' if c.active=='reviewers' else ''}">
74 <li class="${'active' if c.active=='reviewers' else ''}">
75 <a href="${h.route_path('repo_reviewers', repo_name=c.repo_name)}">${_('Reviewer Rules')}</a>
75 <a href="${h.route_path('repo_reviewers', repo_name=c.repo_name)}">${_('Reviewer Rules')}</a>
76 </li>
76 </li>
77 %endif
77 %endif
78 <li class="${'active' if c.active=='maintenance' else ''}">
78 <li class="${'active' if c.active=='maintenance' else ''}">
79 <a href="${h.route_path('repo_maintenance', repo_name=c.repo_name)}">${_('Maintenance')}</a>
79 <a href="${h.route_path('repo_maintenance', repo_name=c.repo_name)}">${_('Maintenance')}</a>
80 </li>
80 </li>
81 <li class="${'active' if c.active=='strip' else ''}">
81 <li class="${'active' if c.active=='strip' else ''}">
82 <a href="${h.route_path('strip', repo_name=c.repo_name)}">${_('Strip')}</a>
82 <a href="${h.route_path('strip', repo_name=c.repo_name)}">${_('Strip')}</a>
83 </li>
83 </li>
84
84
85 </ul>
85 </ul>
86 </div>
86 </div>
87
87
88 <div class="main-content-full-width">
88 <div class="main-content-full-width">
89 ${self.main_content()}
89 ${self.main_content()}
90 </div>
90 </div>
91
91
92 </div>
92 </div>
93 </div>
93 </div>
94
94
95 </%def> No newline at end of file
95 </%def>
@@ -1,124 +1,123 b''
1 <%namespace name="base" file="/base/base.mako"/>
1 <%namespace name="base" file="/base/base.mako"/>
2
2
3 <div class="panel panel-default">
3 <div class="panel panel-default">
4 <div class="panel-heading">
4 <div class="panel-heading">
5 <h3 class="panel-title">${_('Repository Permissions')}</h3>
5 <h3 class="panel-title">${_('Repository Permissions')}</h3>
6 </div>
6 </div>
7 <div class="panel-body">
7 <div class="panel-body">
8 ${h.secure_form(url('edit_repo_perms_update', repo_name=c.repo_name), method='put')}
8 ${h.secure_form(h.route_path('edit_repo_perms', repo_name=c.repo_name), method='POST')}
9 ${h.hidden('repo_private')}
10 <table id="permissions_manage" class="rctable permissions">
9 <table id="permissions_manage" class="rctable permissions">
11 <tr>
10 <tr>
12 <th class="td-radio">${_('None')}</th>
11 <th class="td-radio">${_('None')}</th>
13 <th class="td-radio">${_('Read')}</th>
12 <th class="td-radio">${_('Read')}</th>
14 <th class="td-radio">${_('Write')}</th>
13 <th class="td-radio">${_('Write')}</th>
15 <th class="td-radio">${_('Admin')}</th>
14 <th class="td-radio">${_('Admin')}</th>
16 <th class="td-owner">${_('User/User Group')}</th>
15 <th class="td-owner">${_('User/User Group')}</th>
17 <th></th>
16 <th></th>
18 </tr>
17 </tr>
19 ## USERS
18 ## USERS
20 %for _user in c.repo_info.permissions():
19 %for _user in c.repo_info.permissions():
21 %if getattr(_user, 'admin_row', None) or getattr(_user, 'owner_row', None):
20 %if getattr(_user, 'admin_row', None) or getattr(_user, 'owner_row', None):
22 <tr class="perm_admin_row">
21 <tr class="perm_admin_row">
23 <td class="td-radio">${h.radio('admin_perm_%s' % _user.user_id,'repository.none', disabled="disabled")}</td>
22 <td class="td-radio">${h.radio('admin_perm_%s' % _user.user_id,'repository.none', disabled="disabled")}</td>
24 <td class="td-radio">${h.radio('admin_perm_%s' % _user.user_id,'repository.read', disabled="disabled")}</td>
23 <td class="td-radio">${h.radio('admin_perm_%s' % _user.user_id,'repository.read', disabled="disabled")}</td>
25 <td class="td-radio">${h.radio('admin_perm_%s' % _user.user_id,'repository.write', disabled="disabled")}</td>
24 <td class="td-radio">${h.radio('admin_perm_%s' % _user.user_id,'repository.write', disabled="disabled")}</td>
26 <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>
27 <td class="td-user">
26 <td class="td-user">
28 ${base.gravatar(_user.email, 16)}
27 ${base.gravatar(_user.email, 16)}
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')})
32 %endif
31 %endif
33 %if getattr(_user, 'owner_row', None):
32 %if getattr(_user, 'owner_row', None):
34 (${_('owner')})
33 (${_('owner')})
35 %endif
34 %endif
36 </td>
35 </td>
37 <td></td>
36 <td></td>
38 </tr>
37 </tr>
39 %elif _user.username == h.DEFAULT_USER and c.repo_info.private:
38 %elif _user.username == h.DEFAULT_USER and c.repo_info.private:
40 <tr>
39 <tr>
41 <td colspan="4">
40 <td colspan="4">
42 <span class="private_repo_msg">
41 <span class="private_repo_msg">
43 <strong>${_('private repository')}</strong>
42 <strong title="${_user.permission}">${_('private repository')}</strong>
44 </span>
43 </span>
45 </td>
44 </td>
46 <td class="private_repo_msg">
45 <td class="private_repo_msg">
47 ${base.gravatar(h.DEFAULT_USER_EMAIL, 16)}
46 ${base.gravatar(h.DEFAULT_USER_EMAIL, 16)}
48 ${h.DEFAULT_USER} - ${_('only users/user groups explicitly added here will have access')}</td>
47 ${h.DEFAULT_USER} - ${_('only users/user groups explicitly added here will have access')}</td>
49 <td></td>
48 <td></td>
50 </tr>
49 </tr>
51 %else:
50 %else:
52 <tr>
51 <tr>
53 <td class="td-radio">${h.radio('u_perm_%s' % _user.user_id,'repository.none')}</td>
52 <td class="td-radio">${h.radio('u_perm_%s' % _user.user_id,'repository.none', checked=_user.permission=='repository.none')}</td>
54 <td class="td-radio">${h.radio('u_perm_%s' % _user.user_id,'repository.read')}</td>
53 <td class="td-radio">${h.radio('u_perm_%s' % _user.user_id,'repository.read', checked=_user.permission=='repository.read')}</td>
55 <td class="td-radio">${h.radio('u_perm_%s' % _user.user_id,'repository.write')}</td>
54 <td class="td-radio">${h.radio('u_perm_%s' % _user.user_id,'repository.write', checked=_user.permission=='repository.write')}</td>
56 <td class="td-radio">${h.radio('u_perm_%s' % _user.user_id,'repository.admin')}</td>
55 <td class="td-radio">${h.radio('u_perm_%s' % _user.user_id,'repository.admin', checked=_user.permission=='repository.admin')}</td>
57 <td class="td-user">
56 <td class="td-user">
58 ${base.gravatar(_user.email, 16)}
57 ${base.gravatar(_user.email, 16)}
59 <span class="user">
58 <span class="user">
60 % if _user.username == h.DEFAULT_USER:
59 % if _user.username == h.DEFAULT_USER:
61 ${h.DEFAULT_USER} <span class="user-perm-help-text"> - ${_('permission for all other users')}</span>
60 ${h.DEFAULT_USER} <span class="user-perm-help-text"> - ${_('permission for all other users')}</span>
62 % else:
61 % else:
63 ${h.link_to_user(_user.username)}
62 ${h.link_to_user(_user.username)}
64 % endif
63 % endif
65 </span>
64 </span>
66 </td>
65 </td>
67 <td class="td-action">
66 <td class="td-action">
68 %if _user.username != h.DEFAULT_USER:
67 %if _user.username != h.DEFAULT_USER:
69 <span class="btn btn-link btn-danger revoke_perm"
68 <span class="btn btn-link btn-danger revoke_perm"
70 member="${_user.user_id}" member_type="user">
69 member="${_user.user_id}" member_type="user">
71 <i class="icon-remove"></i> ${_('Revoke')}
70 <i class="icon-remove"></i> ${_('Revoke')}
72 </span>
71 </span>
73 %endif
72 %endif
74 </td>
73 </td>
75 </tr>
74 </tr>
76 %endif
75 %endif
77 %endfor
76 %endfor
78
77
79 ## USER GROUPS
78 ## USER GROUPS
80 %for _user_group in c.repo_info.permission_user_groups():
79 %for _user_group in c.repo_info.permission_user_groups():
81 <tr>
80 <tr>
82 <td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'repository.none')}</td>
81 <td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'repository.none', checked=_user_group.permission=='repository.none')}</td>
83 <td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'repository.read')}</td>
82 <td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'repository.read', checked=_user_group.permission=='repository.read')}</td>
84 <td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'repository.write')}</td>
83 <td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'repository.write', checked=_user_group.permission=='repository.write')}</td>
85 <td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'repository.admin')}</td>
84 <td class="td-radio">${h.radio('g_perm_%s' % _user_group.users_group_id,'repository.admin', checked=_user_group.permission=='repository.admin')}</td>
86 <td class="td-componentname">
85 <td class="td-componentname">
87 <i class="icon-group" ></i>
86 <i class="icon-group" ></i>
88 %if h.HasPermissionAny('hg.admin')():
87 %if h.HasPermissionAny('hg.admin')():
89 <a href="${h.url('edit_users_group',user_group_id=_user_group.users_group_id)}">
88 <a href="${h.url('edit_users_group',user_group_id=_user_group.users_group_id)}">
90 ${_user_group.users_group_name}
89 ${_user_group.users_group_name}
91 </a>
90 </a>
92 %else:
91 %else:
93 ${_user_group.users_group_name}
92 ${_user_group.users_group_name}
94 %endif
93 %endif
95 </td>
94 </td>
96 <td class="td-action">
95 <td class="td-action">
97 <span class="btn btn-link btn-danger revoke_perm"
96 <span class="btn btn-link btn-danger revoke_perm"
98 member="${_user_group.users_group_id}" member_type="user_group">
97 member="${_user_group.users_group_id}" member_type="user_group">
99 <i class="icon-remove"></i> ${_('Revoke')}
98 <i class="icon-remove"></i> ${_('Revoke')}
100 </span>
99 </span>
101 </td>
100 </td>
102 </tr>
101 </tr>
103 %endfor
102 %endfor
104 <tr class="new_members" id="add_perm_input"></tr>
103 <tr class="new_members" id="add_perm_input"></tr>
105 </table>
104 </table>
106 <div id="add_perm" class="link">
105 <div id="add_perm" class="link">
107 ${_('Add new')}
106 ${_('Add new')}
108 </div>
107 </div>
109 <div class="buttons">
108 <div class="buttons">
110 ${h.submit('save',_('Save'),class_="btn btn-primary")}
109 ${h.submit('save',_('Save'),class_="btn btn-primary")}
111 ${h.reset('reset',_('Reset'),class_="btn btn-danger")}
110 ${h.reset('reset',_('Reset'),class_="btn btn-danger")}
112 </div>
111 </div>
113 ${h.end_form()}
112 ${h.end_form()}
114 </div>
113 </div>
115 </div>
114 </div>
116
115
117 <script type="text/javascript">
116 <script type="text/javascript">
118 $('#add_perm').on('click', function(e){
117 $('#add_perm').on('click', function(e){
119 addNewPermInput($(this), 'repository');
118 addNewPermInput($(this), 'repository');
120 });
119 });
121 $('.revoke_perm').on('click', function(e){
120 $('.revoke_perm').on('click', function(e){
122 markRevokePermInput($(this), 'repository');
121 markRevokePermInput($(this), 'repository');
123 });
122 });
124 </script>
123 </script>
@@ -1,207 +1,207 b''
1 ## snippet for displaying permissions overview for users
1 ## snippet for displaying permissions overview for users
2 ## usage:
2 ## usage:
3 ## <%namespace name="p" file="/base/perms_summary.mako"/>
3 ## <%namespace name="p" file="/base/perms_summary.mako"/>
4 ## ${p.perms_summary(c.perm_user.permissions)}
4 ## ${p.perms_summary(c.perm_user.permissions)}
5
5
6 <%def name="perms_summary(permissions, show_all=False, actions=True)">
6 <%def name="perms_summary(permissions, show_all=False, actions=True)">
7 <div id="perms" class="table fields">
7 <div id="perms" class="table fields">
8 %for section in sorted(permissions.keys()):
8 %for section in sorted(permissions.keys()):
9 <div class="panel panel-default">
9 <div class="panel panel-default">
10 <div class="panel-heading">
10 <div class="panel-heading">
11 <h3 class="panel-title">${section.replace("_"," ").capitalize()}</h3>
11 <h3 class="panel-title">${section.replace("_"," ").capitalize()}</h3>
12 </div>
12 </div>
13 <div class="panel-body">
13 <div class="panel-body">
14 <div class="perms_section_head field">
14 <div class="perms_section_head field">
15 <div class="radios">
15 <div class="radios">
16 %if section != 'global':
16 %if section != 'global':
17 <span class="permissions_boxes">
17 <span class="permissions_boxes">
18 <span class="desc">${_('show')}: </span>
18 <span class="desc">${_('show')}: </span>
19 ${h.checkbox('perms_filter_none_%s' % section, 'none', 'checked', class_='perm_filter filter_%s' % section, section=section, perm_type='none')} <label for="${'perms_filter_none_%s' % section}"><span class="perm_tag none">${_('none')}</span></label>
19 ${h.checkbox('perms_filter_none_%s' % section, 'none', 'checked', class_='perm_filter filter_%s' % section, section=section, perm_type='none')} <label for="${'perms_filter_none_%s' % section}"><span class="perm_tag none">${_('none')}</span></label>
20 ${h.checkbox('perms_filter_read_%s' % section, 'read', 'checked', class_='perm_filter filter_%s' % section, section=section, perm_type='read')} <label for="${'perms_filter_read_%s' % section}"><span class="perm_tag read">${_('read')}</span></label>
20 ${h.checkbox('perms_filter_read_%s' % section, 'read', 'checked', class_='perm_filter filter_%s' % section, section=section, perm_type='read')} <label for="${'perms_filter_read_%s' % section}"><span class="perm_tag read">${_('read')}</span></label>
21 ${h.checkbox('perms_filter_write_%s' % section, 'write', 'checked', class_='perm_filter filter_%s' % section, section=section, perm_type='write')} <label for="${'perms_filter_write_%s' % section}"> <span class="perm_tag write">${_('write')}</span></label>
21 ${h.checkbox('perms_filter_write_%s' % section, 'write', 'checked', class_='perm_filter filter_%s' % section, section=section, perm_type='write')} <label for="${'perms_filter_write_%s' % section}"> <span class="perm_tag write">${_('write')}</span></label>
22 ${h.checkbox('perms_filter_admin_%s' % section, 'admin', 'checked', class_='perm_filter filter_%s' % section, section=section, perm_type='admin')} <label for="${'perms_filter_admin_%s' % section}"><span class="perm_tag admin">${_('admin')}</span></label>
22 ${h.checkbox('perms_filter_admin_%s' % section, 'admin', 'checked', class_='perm_filter filter_%s' % section, section=section, perm_type='admin')} <label for="${'perms_filter_admin_%s' % section}"><span class="perm_tag admin">${_('admin')}</span></label>
23 </span>
23 </span>
24 %endif
24 %endif
25 </div>
25 </div>
26 </div>
26 </div>
27 <div class="field">
27 <div class="field">
28 %if not permissions[section]:
28 %if not permissions[section]:
29 <p class="empty_data help-block">${_('No permissions defined')}</p>
29 <p class="empty_data help-block">${_('No permissions defined')}</p>
30 %else:
30 %else:
31 <div id='tbl_list_wrap_${section}'>
31 <div id='tbl_list_wrap_${section}'>
32 <table id="tbl_list_${section}" class="rctable">
32 <table id="tbl_list_${section}" class="rctable">
33 ## global permission box
33 ## global permission box
34 %if section == 'global':
34 %if section == 'global':
35 <thead>
35 <thead>
36 <tr>
36 <tr>
37 <th colspan="2" class="left">${_('Permission')}</th>
37 <th colspan="2" class="left">${_('Permission')}</th>
38 %if actions:
38 %if actions:
39 <th>${_('Edit Permission')}</th>
39 <th>${_('Edit Permission')}</th>
40 %endif
40 %endif
41 </thead>
41 </thead>
42 <tbody>
42 <tbody>
43
43
44 <%
44 <%
45 def get_section_perms(prefix, opts):
45 def get_section_perms(prefix, opts):
46 _selected = []
46 _selected = []
47 for op in opts:
47 for op in opts:
48 if op.startswith(prefix) and not op.startswith('hg.create.write_on_repogroup'):
48 if op.startswith(prefix) and not op.startswith('hg.create.write_on_repogroup'):
49 _selected.append(op)
49 _selected.append(op)
50 admin = 'hg.admin' in opts
50 admin = 'hg.admin' in opts
51 _selected_vals = [x.partition(prefix)[-1] for x in _selected]
51 _selected_vals = [x.partition(prefix)[-1] for x in _selected]
52 return admin, _selected_vals, _selected
52 return admin, _selected_vals, _selected
53 %>
53 %>
54 <%def name="glob(lbl, val, val_lbl=None, custom_url=None)">
54 <%def name="glob(lbl, val, val_lbl=None, custom_url=None)">
55 <tr>
55 <tr>
56 <td class="td-tags">
56 <td class="td-tags">
57 ${lbl}
57 ${lbl}
58 </td>
58 </td>
59 <td class="td-tags">
59 <td class="td-tags">
60 %if val[0]:
60 %if val[0]:
61 %if not val_lbl:
61 %if not val_lbl:
62 ${h.bool2icon(True)}
62 ${h.bool2icon(True)}
63 %else:
63 %else:
64 <span class="perm_tag admin">${val_lbl}.admin</span>
64 <span class="perm_tag admin">${val_lbl}.admin</span>
65 %endif
65 %endif
66 %else:
66 %else:
67 %if not val_lbl:
67 %if not val_lbl:
68 ${h.bool2icon({'false': False,
68 ${h.bool2icon({'false': False,
69 'true': True,
69 'true': True,
70 'none': False,
70 'none': False,
71 'repository': True}.get(val[1][0] if 0 < len(val[1]) else 'false'))}
71 'repository': True}.get(val[1][0] if 0 < len(val[1]) else 'false'))}
72 %else:
72 %else:
73 <span class="perm_tag ${val[1][0]}">${val_lbl}.${val[1][0]}</span>
73 <span class="perm_tag ${val[1][0]}">${val_lbl}.${val[1][0]}</span>
74 %endif
74 %endif
75 %endif
75 %endif
76 </td>
76 </td>
77 %if actions:
77 %if actions:
78 <td class="td-action">
78 <td class="td-action">
79 <a href="${custom_url or h.url('admin_permissions_global')}">${_('edit')}</a>
79 <a href="${custom_url or h.url('admin_permissions_global')}">${_('edit')}</a>
80 </td>
80 </td>
81 %endif
81 %endif
82 </tr>
82 </tr>
83 </%def>
83 </%def>
84
84
85 ${glob(_('Super admin'), get_section_perms('hg.admin', permissions[section]))}
85 ${glob(_('Super admin'), get_section_perms('hg.admin', permissions[section]))}
86
86
87 ${glob(_('Repository default permission'), get_section_perms('repository.', permissions[section]), 'repository', h.url('admin_permissions_object'))}
87 ${glob(_('Repository default permission'), get_section_perms('repository.', permissions[section]), 'repository', h.url('admin_permissions_object'))}
88 ${glob(_('Repository group default permission'), get_section_perms('group.', permissions[section]), 'group', h.url('admin_permissions_object'))}
88 ${glob(_('Repository group default permission'), get_section_perms('group.', permissions[section]), 'group', h.url('admin_permissions_object'))}
89 ${glob(_('User group default permission'), get_section_perms('usergroup.', permissions[section]), 'usergroup', h.url('admin_permissions_object'))}
89 ${glob(_('User group default permission'), get_section_perms('usergroup.', permissions[section]), 'usergroup', h.url('admin_permissions_object'))}
90
90
91 ${glob(_('Create repositories'), get_section_perms('hg.create.', permissions[section]), custom_url=h.url('admin_permissions_global'))}
91 ${glob(_('Create repositories'), get_section_perms('hg.create.', permissions[section]), custom_url=h.url('admin_permissions_global'))}
92 ${glob(_('Fork repositories'), get_section_perms('hg.fork.', permissions[section]), custom_url=h.url('admin_permissions_global'))}
92 ${glob(_('Fork repositories'), get_section_perms('hg.fork.', permissions[section]), custom_url=h.url('admin_permissions_global'))}
93 ${glob(_('Create repository groups'), get_section_perms('hg.repogroup.create.', permissions[section]), custom_url=h.url('admin_permissions_global'))}
93 ${glob(_('Create repository groups'), get_section_perms('hg.repogroup.create.', permissions[section]), custom_url=h.url('admin_permissions_global'))}
94 ${glob(_('Create user groups'), get_section_perms('hg.usergroup.create.', permissions[section]), custom_url=h.url('admin_permissions_global'))}
94 ${glob(_('Create user groups'), get_section_perms('hg.usergroup.create.', permissions[section]), custom_url=h.url('admin_permissions_global'))}
95
95
96
96
97 </tbody>
97 </tbody>
98 %else:
98 %else:
99 ## none/read/write/admin permissions on groups/repos etc
99 ## none/read/write/admin permissions on groups/repos etc
100 <thead>
100 <thead>
101 <tr>
101 <tr>
102 <th>${_('Name')}</th>
102 <th>${_('Name')}</th>
103 <th>${_('Permission')}</th>
103 <th>${_('Permission')}</th>
104 %if actions:
104 %if actions:
105 <th>${_('Edit Permission')}</th>
105 <th>${_('Edit Permission')}</th>
106 %endif
106 %endif
107 </thead>
107 </thead>
108 <tbody class="section_${section}">
108 <tbody class="section_${section}">
109 <%
109 <%
110 def sorter(permissions):
110 def sorter(permissions):
111 def custom_sorter(item):
111 def custom_sorter(item):
112 ## read/write/admin
112 ## read/write/admin
113 section = item[1].split('.')[-1]
113 section = item[1].split('.')[-1]
114 section_importance = {'none': u'0',
114 section_importance = {'none': u'0',
115 'read': u'1',
115 'read': u'1',
116 'write':u'2',
116 'write':u'2',
117 'admin':u'3'}.get(section)
117 'admin':u'3'}.get(section)
118 ## sort by group importance+name
118 ## sort by group importance+name
119 return section_importance+item[0]
119 return section_importance+item[0]
120 return sorted(permissions, key=custom_sorter)
120 return sorted(permissions, key=custom_sorter)
121 %>
121 %>
122 %for k, section_perm in sorter(permissions[section].items()):
122 %for k, section_perm in sorter(permissions[section].items()):
123 %if section_perm.split('.')[-1] != 'none' or show_all:
123 %if section_perm.split('.')[-1] != 'none' or show_all:
124 <tr class="perm_row ${'%s_%s' % (section, section_perm.split('.')[-1])}">
124 <tr class="perm_row ${'%s_%s' % (section, section_perm.split('.')[-1])}">
125 <td class="td-componentname">
125 <td class="td-componentname">
126 %if section == 'repositories':
126 %if section == 'repositories':
127 <a href="${h.url('summary_home',repo_name=k)}">${k}</a>
127 <a href="${h.url('summary_home',repo_name=k)}">${k}</a>
128 %elif section == 'repositories_groups':
128 %elif section == 'repositories_groups':
129 <a href="${h.url('repo_group_home',group_name=k)}">${k}</a>
129 <a href="${h.url('repo_group_home',group_name=k)}">${k}</a>
130 %elif section == 'user_groups':
130 %elif section == 'user_groups':
131 ##<a href="${h.url('edit_users_group',user_group_id=k)}">${k}</a>
131 ##<a href="${h.url('edit_users_group',user_group_id=k)}">${k}</a>
132 ${k}
132 ${k}
133 %endif
133 %endif
134 </td>
134 </td>
135 <td class="td-tags">
135 <td class="td-tags">
136 %if hasattr(permissions[section], 'perm_origin_stack'):
136 %if hasattr(permissions[section], 'perm_origin_stack'):
137 %for i, (perm, origin) in enumerate(reversed(permissions[section].perm_origin_stack[k])):
137 %for i, (perm, origin) in enumerate(reversed(permissions[section].perm_origin_stack[k])):
138 <span class="${i > 0 and 'perm_overriden' or ''} perm_tag ${perm.split('.')[-1]}">
138 <span class="${i > 0 and 'perm_overriden' or ''} perm_tag ${perm.split('.')[-1]}">
139 ${perm} (${origin})
139 ${perm} (${origin})
140 </span>
140 </span>
141 %endfor
141 %endfor
142 %else:
142 %else:
143 <span class="perm_tag ${section_perm.split('.')[-1]}">${section_perm}</span>
143 <span class="perm_tag ${section_perm.split('.')[-1]}">${section_perm}</span>
144 %endif
144 %endif
145 </td>
145 </td>
146 %if actions:
146 %if actions:
147 <td class="td-action">
147 <td class="td-action">
148 %if section == 'repositories':
148 %if section == 'repositories':
149 <a href="${h.url('edit_repo_perms',repo_name=k,anchor='permissions_manage')}">${_('edit')}</a>
149 <a href="${h.route_path('edit_repo_perms',repo_name=k,anchor='permissions_manage')}">${_('edit')}</a>
150 %elif section == 'repositories_groups':
150 %elif section == 'repositories_groups':
151 <a href="${h.url('edit_repo_group_perms',group_name=k,anchor='permissions_manage')}">${_('edit')}</a>
151 <a href="${h.url('edit_repo_group_perms',group_name=k,anchor='permissions_manage')}">${_('edit')}</a>
152 %elif section == 'user_groups':
152 %elif section == 'user_groups':
153 ##<a href="${h.url('edit_users_group',user_group_id=k)}">${_('edit')}</a>
153 ##<a href="${h.url('edit_users_group',user_group_id=k)}">${_('edit')}</a>
154 %endif
154 %endif
155 </td>
155 </td>
156 %endif
156 %endif
157 </tr>
157 </tr>
158 %endif
158 %endif
159 %endfor
159 %endfor
160
160
161 <tr id="empty_${section}" class="noborder" style="display:none;">
161 <tr id="empty_${section}" class="noborder" style="display:none;">
162 <td colspan="6">${_('No permission defined')}</td>
162 <td colspan="6">${_('No permission defined')}</td>
163 </tr>
163 </tr>
164
164
165 </tbody>
165 </tbody>
166 %endif
166 %endif
167 </table>
167 </table>
168 </div>
168 </div>
169 %endif
169 %endif
170 </div>
170 </div>
171 </div>
171 </div>
172 </div>
172 </div>
173 %endfor
173 %endfor
174 </div>
174 </div>
175
175
176 <script>
176 <script>
177 $(document).ready(function(){
177 $(document).ready(function(){
178 var show_empty = function(section){
178 var show_empty = function(section){
179 var visible = $('.section_{0} tr.perm_row:visible'.format(section)).length;
179 var visible = $('.section_{0} tr.perm_row:visible'.format(section)).length;
180 if(visible == 0){
180 if(visible == 0){
181 $('#empty_{0}'.format(section)).show();
181 $('#empty_{0}'.format(section)).show();
182 }
182 }
183 else{
183 else{
184 $('#empty_{0}'.format(section)).hide();
184 $('#empty_{0}'.format(section)).hide();
185 }
185 }
186 };
186 };
187 $('.perm_filter').on('change', function(e){
187 $('.perm_filter').on('change', function(e){
188 var self = this;
188 var self = this;
189 var section = $(this).attr('section');
189 var section = $(this).attr('section');
190
190
191 var opts = {};
191 var opts = {};
192 var elems = $('.filter_' + section).each(function(el){
192 var elems = $('.filter_' + section).each(function(el){
193 var perm_type = $(this).attr('perm_type');
193 var perm_type = $(this).attr('perm_type');
194 var checked = this.checked;
194 var checked = this.checked;
195 opts[perm_type] = checked;
195 opts[perm_type] = checked;
196 if(checked){
196 if(checked){
197 $('.'+section+'_'+perm_type).show();
197 $('.'+section+'_'+perm_type).show();
198 }
198 }
199 else{
199 else{
200 $('.'+section+'_'+perm_type).hide();
200 $('.'+section+'_'+perm_type).hide();
201 }
201 }
202 });
202 });
203 show_empty(section);
203 show_empty(section);
204 })
204 })
205 })
205 })
206 </script>
206 </script>
207 </%def>
207 </%def>
General Comments 0
You need to be logged in to leave comments. Login now