##// END OF EJS Templates
repo-settings: moved advanced setion into pyramid views....
marcink -
r1751:aa1d7f99 default
parent child Browse files
Show More
@@ -0,0 +1,150 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import pytest
22
23 from rhodecode.lib.utils2 import safe_unicode, safe_str
24 from rhodecode.model.db import Repository
25 from rhodecode.model.repo import RepoModel
26 from rhodecode.tests import (
27 HG_REPO, GIT_REPO, assert_session_flash, no_newline_id_generator)
28 from rhodecode.tests.fixture import Fixture
29 from rhodecode.tests.utils import repo_on_filesystem
30
31 fixture = Fixture()
32
33
34 def route_path(name, params=None, **kwargs):
35 import urllib
36
37 base_url = {
38 'repo_summary_explicit': '/{repo_name}/summary',
39 'repo_summary': '/{repo_name}',
40 'edit_repo_advanced': '/{repo_name}/settings/advanced',
41 'edit_repo_advanced_delete': '/{repo_name}/settings/advanced/delete',
42 'edit_repo_advanced_fork': '/{repo_name}/settings/advanced/fork',
43 'edit_repo_advanced_locking': '/{repo_name}/settings/advanced/locking',
44 'edit_repo_advanced_journal': '/{repo_name}/settings/advanced/journal',
45
46 }[name].format(**kwargs)
47
48 if params:
49 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
50 return base_url
51
52
53 @pytest.mark.usefixtures('autologin_user', 'app')
54 class TestAdminRepoSettingsAdvanced(object):
55
56 def test_set_repo_fork_has_no_self_id(self, autologin_user, backend):
57 repo = backend.repo
58 response = self.app.get(
59 route_path('edit_repo_advanced', repo_name=backend.repo_name))
60 opt = """<option value="%s">vcs_test_git</option>""" % repo.repo_id
61 response.mustcontain(no=[opt])
62
63 def test_set_fork_of_target_repo(
64 self, autologin_user, backend, csrf_token):
65 target_repo = 'target_%s' % backend.alias
66 fixture.create_repo(target_repo, repo_type=backend.alias)
67 repo2 = Repository.get_by_repo_name(target_repo)
68 response = self.app.post(
69 route_path('edit_repo_advanced_fork', repo_name=backend.repo_name),
70 params={'id_fork_of': repo2.repo_id,
71 'csrf_token': csrf_token})
72 repo = Repository.get_by_repo_name(backend.repo_name)
73 repo2 = Repository.get_by_repo_name(target_repo)
74 assert_session_flash(
75 response,
76 'Marked repo %s as fork of %s' % (repo.repo_name, repo2.repo_name))
77
78 assert repo.fork == repo2
79 response = response.follow()
80 # check if given repo is selected
81
82 opt = 'This repository is a fork of <a href="%s">%s</a>' % (
83 route_path('repo_summary', repo_name=repo2.repo_name),
84 repo2.repo_name)
85
86 response.mustcontain(opt)
87
88 fixture.destroy_repo(target_repo, forks='detach')
89
90 @pytest.mark.backends("hg", "git")
91 def test_set_fork_of_other_type_repo(
92 self, autologin_user, backend, csrf_token):
93 TARGET_REPO_MAP = {
94 'git': {
95 'type': 'hg',
96 'repo_name': HG_REPO},
97 'hg': {
98 'type': 'git',
99 'repo_name': GIT_REPO},
100 }
101 target_repo = TARGET_REPO_MAP[backend.alias]
102
103 repo2 = Repository.get_by_repo_name(target_repo['repo_name'])
104 response = self.app.post(
105 route_path('edit_repo_advanced_fork', repo_name=backend.repo_name),
106 params={'id_fork_of': repo2.repo_id,
107 'csrf_token': csrf_token})
108 assert_session_flash(
109 response,
110 'Cannot set repository as fork of repository with other type')
111
112 def test_set_fork_of_none(self, autologin_user, backend, csrf_token):
113 # mark it as None
114 response = self.app.post(
115 route_path('edit_repo_advanced_fork', repo_name=backend.repo_name),
116 params={'id_fork_of': None, '_method': 'put',
117 'csrf_token': csrf_token})
118 assert_session_flash(
119 response,
120 'Marked repo %s as fork of %s'
121 % (backend.repo_name, "Nothing"))
122 assert backend.repo.fork is None
123
124 def test_set_fork_of_same_repo(self, autologin_user, backend, csrf_token):
125 repo = Repository.get_by_repo_name(backend.repo_name)
126 response = self.app.post(
127 route_path('edit_repo_advanced_fork', repo_name=backend.repo_name),
128 params={'id_fork_of': repo.repo_id, 'csrf_token': csrf_token})
129 assert_session_flash(
130 response, 'An error occurred during this operation')
131
132 @pytest.mark.parametrize(
133 "suffix",
134 ['', u'Δ…Δ™Ε‚' , '123'],
135 ids=no_newline_id_generator)
136 def test_advanced_delete(self, autologin_user, backend, suffix, csrf_token):
137 repo = backend.create_repo(name_suffix=suffix)
138 repo_name = repo.repo_name
139 repo_name_str = safe_str(repo.repo_name)
140
141 response = self.app.post(
142 route_path('edit_repo_advanced_delete', repo_name=repo_name_str),
143 params={'csrf_token': csrf_token})
144 assert_session_flash(response,
145 u'Deleted repository `{}`'.format(repo_name))
146 response.follow()
147
148 # check if repo was deleted from db
149 assert RepoModel().get_by_repo_name(repo_name) is None
150 assert not repo_on_filesystem(repo_name_str)
@@ -0,0 +1,225 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2011-2017 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import logging
22
23 from pyramid.view import view_config
24 from pyramid.httpexceptions import HTTPFound
25
26 from rhodecode.apps._base import RepoAppView
27 from rhodecode.lib import helpers as h
28 from rhodecode.lib import audit_logger
29 from rhodecode.lib.auth import (
30 LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired)
31 from rhodecode.lib.exceptions import AttachedForksError
32 from rhodecode.lib.utils2 import safe_int
33 from rhodecode.lib.vcs import RepositoryError
34 from rhodecode.model.db import Session, UserFollowing, User, Repository
35 from rhodecode.model.repo import RepoModel
36 from rhodecode.model.scm import ScmModel
37
38 log = logging.getLogger(__name__)
39
40
41 class RepoSettingsView(RepoAppView):
42
43 def load_default_context(self):
44 c = self._get_local_tmpl_context()
45
46 # TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead
47 c.repo_info = self.db_repo
48
49 self._register_global_c(c)
50 return c
51
52 @LoginRequired()
53 @HasRepoPermissionAnyDecorator('repository.admin')
54 @view_config(
55 route_name='edit_repo_advanced', request_method='GET',
56 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
57 def edit_advanced(self):
58 c = self.load_default_context()
59 c.active = 'advanced'
60
61 c.default_user_id = User.get_default_user().user_id
62 c.in_public_journal = UserFollowing.query() \
63 .filter(UserFollowing.user_id == c.default_user_id) \
64 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
65
66 c.has_origin_repo_read_perm = False
67 if self.db_repo.fork:
68 c.has_origin_repo_read_perm = h.HasRepoPermissionAny(
69 'repository.write', 'repository.read', 'repository.admin')(
70 self.db_repo.fork.repo_name, 'repo set as fork page')
71
72 return self._get_template_context(c)
73
74 @LoginRequired()
75 @HasRepoPermissionAnyDecorator('repository.admin')
76 @view_config(
77 route_name='edit_repo_advanced_delete', request_method='POST',
78 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
79 def edit_advanced_delete(self):
80 """
81 Deletes the repository, or shows warnings if deletion is not possible
82 because of attached forks or other errors.
83 """
84 _ = self.request.translate
85 handle_forks = self.request.POST.get('forks', None)
86
87 try:
88 _forks = self.db_repo.forks.count()
89 if _forks and handle_forks:
90 if handle_forks == 'detach_forks':
91 handle_forks = 'detach'
92 h.flash(_('Detached %s forks') % _forks, category='success')
93 elif handle_forks == 'delete_forks':
94 handle_forks = 'delete'
95 h.flash(_('Deleted %s forks') % _forks, category='success')
96
97 repo_data = self.db_repo.get_api_data()
98 RepoModel().delete(self.db_repo, forks=handle_forks)
99
100 repo = audit_logger.RepoWrap(repo_id=self.db_repo.repo_id,
101 repo_name=self.db_repo.repo_name)
102 audit_logger.store(
103 action='repo.delete', action_data={'repo_data': repo_data},
104 user=self._rhodecode_user, repo=repo, commit=False)
105
106 ScmModel().mark_for_invalidation(self.db_repo_name)
107 h.flash(
108 _('Deleted repository `%s`') % self.db_repo_name,
109 category='success')
110 Session().commit()
111 except AttachedForksError:
112 repo_advanced_url = h.route_path(
113 'edit_repo_advanced', repo_name=self.db_repo_name,
114 _anchor='advanced-delete')
115 delete_anchor = h.link_to(_('detach or delete'), repo_advanced_url)
116 h.flash(_('Cannot delete `{repo}` it still contains attached forks. '
117 'Try using {delete_or_detach} option.')
118 .format(repo=self.db_repo_name, delete_or_detach=delete_anchor),
119 category='warning')
120
121 # redirect to advanced for forks handle action ?
122 raise HTTPFound(repo_advanced_url)
123
124 except Exception:
125 log.exception("Exception during deletion of repository")
126 h.flash(_('An error occurred during deletion of `%s`')
127 % self.db_repo_name, category='error')
128 # redirect to advanced for more deletion options
129 raise HTTPFound(
130 h.route_path('edit_repo_advanced', repo_name=self.db_repo_name),
131 _anchor='advanced-delete')
132
133 raise HTTPFound(h.route_path('home'))
134
135 @LoginRequired()
136 @HasRepoPermissionAnyDecorator('repository.admin')
137 @CSRFRequired()
138 @view_config(
139 route_name='edit_repo_advanced_journal', request_method='POST',
140 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
141 def edit_advanced_journal(self):
142 """
143 Set's this repository to be visible in public journal,
144 in other words making default user to follow this repo
145 """
146 _ = self.request.translate
147
148 try:
149 user_id = User.get_default_user().user_id
150 ScmModel().toggle_following_repo(self.db_repo.repo_id, user_id)
151 h.flash(_('Updated repository visibility in public journal'),
152 category='success')
153 Session().commit()
154 except Exception:
155 h.flash(_('An error occurred during setting this '
156 'repository in public journal'),
157 category='error')
158
159 raise HTTPFound(
160 h.route_path('edit_repo_advanced', repo_name=self.db_repo_name))
161
162 @LoginRequired()
163 @HasRepoPermissionAnyDecorator('repository.admin')
164 @CSRFRequired()
165 @view_config(
166 route_name='edit_repo_advanced_fork', request_method='POST',
167 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
168 def edit_advanced_fork(self):
169 """
170 Mark given repository as a fork of another
171 """
172 _ = self.request.translate
173
174 new_fork_id = self.request.POST.get('id_fork_of')
175 try:
176
177 if new_fork_id and not new_fork_id.isdigit():
178 log.error('Given fork id %s is not an INT', new_fork_id)
179
180 fork_id = safe_int(new_fork_id)
181 repo = ScmModel().mark_as_fork(
182 self.db_repo_name, fork_id, self._rhodecode_user.user_id)
183 fork = repo.fork.repo_name if repo.fork else _('Nothing')
184 Session().commit()
185 h.flash(_('Marked repo %s as fork of %s') % (self.db_repo_name, fork),
186 category='success')
187 except RepositoryError as e:
188 log.exception("Repository Error occurred")
189 h.flash(str(e), category='error')
190 except Exception as e:
191 log.exception("Exception while editing fork")
192 h.flash(_('An error occurred during this operation'),
193 category='error')
194
195 raise HTTPFound(
196 h.route_path('edit_repo_advanced', repo_name=self.db_repo_name))
197
198 @LoginRequired()
199 @HasRepoPermissionAnyDecorator('repository.admin')
200 @CSRFRequired()
201 @view_config(
202 route_name='edit_repo_advanced_locking', request_method='POST',
203 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
204 def edit_advanced_locking(self):
205 """
206 Toggle locking of repository
207 """
208 _ = self.request.translate
209 set_lock = self.request.POST.get('set_lock')
210 set_unlock = self.request.POST.get('set_unlock')
211
212 try:
213 if set_lock:
214 Repository.lock(self.db_repo, self._rhodecode_user.user_id,
215 lock_reason=Repository.LOCK_WEB)
216 h.flash(_('Locked repository'), category='success')
217 elif set_unlock:
218 Repository.unlock(self.db_repo)
219 h.flash(_('Unlocked repository'), category='success')
220 except Exception as e:
221 log.exception("Exception during unlocking")
222 h.flash(_('An error occurred during unlocking'), category='error')
223
224 raise HTTPFound(
225 h.route_path('edit_repo_advanced', repo_name=self.db_repo_name))
@@ -1,81 +1,109 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 # Summary
25 config.add_route(
26 name='repo_summary_explicit',
27 pattern='/{repo_name:.*?[^/]}/summary', repo_route=True)
28
24 # Tags
29 # Tags
25 config.add_route(
30 config.add_route(
26 name='tags_home',
31 name='tags_home',
27 pattern='/{repo_name:.*?[^/]}/tags', repo_route=True)
32 pattern='/{repo_name:.*?[^/]}/tags', repo_route=True)
28
33
29 # Branches
34 # Branches
30 config.add_route(
35 config.add_route(
31 name='branches_home',
36 name='branches_home',
32 pattern='/{repo_name:.*?[^/]}/branches', repo_route=True)
37 pattern='/{repo_name:.*?[^/]}/branches', repo_route=True)
33
38
34 # Bookmarks
39 # Bookmarks
35 config.add_route(
40 config.add_route(
36 name='bookmarks_home',
41 name='bookmarks_home',
37 pattern='/{repo_name:.*?[^/]}/bookmarks', repo_route=True)
42 pattern='/{repo_name:.*?[^/]}/bookmarks', repo_route=True)
38
43
39 # Settings
44 # Settings
40 config.add_route(
45 config.add_route(
41 name='edit_repo',
46 name='edit_repo',
42 pattern='/{repo_name:.*?[^/]}/settings', repo_route=True)
47 pattern='/{repo_name:.*?[^/]}/settings', repo_route=True)
43
48
49 # Settings advanced
50 config.add_route(
51 name='edit_repo_advanced',
52 pattern='/{repo_name:.*?[^/]}/settings/advanced', repo_route=True)
53 config.add_route(
54 name='edit_repo_advanced_delete',
55 pattern='/{repo_name:.*?[^/]}/settings/advanced/delete', repo_route=True)
56 config.add_route(
57 name='edit_repo_advanced_locking',
58 pattern='/{repo_name:.*?[^/]}/settings/advanced/locking', repo_route=True)
59 config.add_route(
60 name='edit_repo_advanced_journal',
61 pattern='/{repo_name:.*?[^/]}/settings/advanced/journal', repo_route=True)
62 config.add_route(
63 name='edit_repo_advanced_fork',
64 pattern='/{repo_name:.*?[^/]}/settings/advanced/fork', repo_route=True)
65
44 # Caches
66 # Caches
45 config.add_route(
67 config.add_route(
46 name='edit_repo_caches',
68 name='edit_repo_caches',
47 pattern='/{repo_name:.*?[^/]}/settings/caches', repo_route=True)
69 pattern='/{repo_name:.*?[^/]}/settings/caches', repo_route=True)
48
70
49 # Permissions
71 # Permissions
50 config.add_route(
72 config.add_route(
51 name='edit_repo_perms',
73 name='edit_repo_perms',
52 pattern='/{repo_name:.*?[^/]}/settings/permissions', repo_route=True)
74 pattern='/{repo_name:.*?[^/]}/settings/permissions', repo_route=True)
53
75
54 # Repo Review Rules
76 # Repo Review Rules
55 config.add_route(
77 config.add_route(
56 name='repo_reviewers',
78 name='repo_reviewers',
57 pattern='/{repo_name:.*?[^/]}/settings/review/rules', repo_route=True)
79 pattern='/{repo_name:.*?[^/]}/settings/review/rules', repo_route=True)
58
80
59 # Maintenance
81 # Maintenance
60 config.add_route(
82 config.add_route(
61 name='repo_maintenance',
83 name='repo_maintenance',
62 pattern='/{repo_name:.*?[^/]}/settings/maintenance', repo_route=True)
84 pattern='/{repo_name:.*?[^/]}/settings/maintenance', repo_route=True)
63
85
64 config.add_route(
86 config.add_route(
65 name='repo_maintenance_execute',
87 name='repo_maintenance_execute',
66 pattern='/{repo_name:.*?[^/]}/settings/maintenance/execute', repo_route=True)
88 pattern='/{repo_name:.*?[^/]}/settings/maintenance/execute', repo_route=True)
67
89
68 # Strip
90 # Strip
69 config.add_route(
91 config.add_route(
70 name='strip',
92 name='strip',
71 pattern='/{repo_name:.*?[^/]}/settings/strip', repo_route=True)
93 pattern='/{repo_name:.*?[^/]}/settings/strip', repo_route=True)
72
94
73 config.add_route(
95 config.add_route(
74 name='strip_check',
96 name='strip_check',
75 pattern='/{repo_name:.*?[^/]}/settings/strip_check', repo_route=True)
97 pattern='/{repo_name:.*?[^/]}/settings/strip_check', repo_route=True)
76
98
77 config.add_route(
99 config.add_route(
78 name='strip_execute',
100 name='strip_execute',
79 pattern='/{repo_name:.*?[^/]}/settings/strip_execute', repo_route=True)
101 pattern='/{repo_name:.*?[^/]}/settings/strip_execute', repo_route=True)
102
103 # NOTE(marcink): needs to be at the end for catch-all
104 # config.add_route(
105 # name='repo_summary',
106 # pattern='/{repo_name:.*?[^/]}', repo_route=True)
107
80 # Scan module for configuration decorators.
108 # Scan module for configuration decorators.
81 config.scan()
109 config.scan()
@@ -1,232 +1,233 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_advanced': '/{repo_name}/settings/advanced',
41 'edit_repo_caches': '/{repo_name}/settings/caches',
42 'edit_repo_caches': '/{repo_name}/settings/caches',
42 'edit_repo_perms': '/{repo_name}/settings/permissions',
43 'edit_repo_perms': '/{repo_name}/settings/permissions',
43 }[name].format(**kwargs)
44 }[name].format(**kwargs)
44
45
45 if params:
46 if params:
46 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
47 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
47 return base_url
48 return base_url
48
49
49
50
50 def _get_permission_for_user(user, repo):
51 def _get_permission_for_user(user, repo):
51 perm = UserRepoToPerm.query()\
52 perm = UserRepoToPerm.query()\
52 .filter(UserRepoToPerm.repository ==
53 .filter(UserRepoToPerm.repository ==
53 Repository.get_by_repo_name(repo))\
54 Repository.get_by_repo_name(repo))\
54 .filter(UserRepoToPerm.user == User.get_by_username(user))\
55 .filter(UserRepoToPerm.user == User.get_by_username(user))\
55 .all()
56 .all()
56 return perm
57 return perm
57
58
58
59
59 @pytest.mark.usefixtures('autologin_user', 'app')
60 @pytest.mark.usefixtures('autologin_user', 'app')
60 class TestAdminRepoSettings(object):
61 class TestAdminRepoSettings(object):
61 @pytest.mark.parametrize('urlname', [
62 @pytest.mark.parametrize('urlname', [
62 'edit_repo',
63 'edit_repo',
63 'edit_repo_caches',
64 'edit_repo_caches',
64 'edit_repo_perms',
65 'edit_repo_perms',
66 'edit_repo_advanced',
65 ])
67 ])
66 def test_show_page(self, urlname, app, backend):
68 def test_show_page(self, urlname, app, backend):
67 app.get(route_path(urlname, repo_name=backend.repo_name), status=200)
69 app.get(route_path(urlname, repo_name=backend.repo_name), status=200)
68
70
69 def test_edit_accessible_when_missing_requirements(
71 def test_edit_accessible_when_missing_requirements(
70 self, backend_hg, autologin_user):
72 self, backend_hg, autologin_user):
71 scm_patcher = mock.patch.object(
73 scm_patcher = mock.patch.object(
72 Repository, 'scm_instance', side_effect=RepositoryRequirementError)
74 Repository, 'scm_instance', side_effect=RepositoryRequirementError)
73 with scm_patcher:
75 with scm_patcher:
74 self.app.get(route_path('edit_repo', repo_name=backend_hg.repo_name))
76 self.app.get(route_path('edit_repo', repo_name=backend_hg.repo_name))
75
77
76 @pytest.mark.parametrize('urlname', [
78 @pytest.mark.parametrize('urlname', [
77 'edit_repo_advanced',
78 'repo_vcs_settings',
79 'repo_vcs_settings',
80 'repo_settings_issuetracker',
79 'edit_repo_fields',
81 'edit_repo_fields',
80 'repo_settings_issuetracker',
81 'edit_repo_remote',
82 'edit_repo_remote',
82 'edit_repo_statistics',
83 'edit_repo_statistics',
83 ])
84 ])
84 def test_show_page_pylons(self, urlname, app):
85 def test_show_page_pylons(self, urlname, app):
85 app.get(url(urlname, repo_name=HG_REPO))
86 app.get(url(urlname, repo_name=HG_REPO))
86
87
87 @pytest.mark.parametrize('update_settings', [
88 @pytest.mark.parametrize('update_settings', [
88 {'repo_description': 'alter-desc'},
89 {'repo_description': 'alter-desc'},
89 {'repo_owner': TEST_USER_REGULAR_LOGIN},
90 {'repo_owner': TEST_USER_REGULAR_LOGIN},
90 {'repo_private': 'true'},
91 {'repo_private': 'true'},
91 {'repo_enable_locking': 'true'},
92 {'repo_enable_locking': 'true'},
92 {'repo_enable_downloads': 'true'},
93 {'repo_enable_downloads': 'true'},
93 ])
94 ])
94 def test_update_repo_settings(self, update_settings, csrf_token, backend, user_util):
95 def test_update_repo_settings(self, update_settings, csrf_token, backend, user_util):
95 repo = user_util.create_repo(repo_type=backend.alias)
96 repo = user_util.create_repo(repo_type=backend.alias)
96 repo_name = repo.repo_name
97 repo_name = repo.repo_name
97
98
98 params = fixture._get_repo_create_params(
99 params = fixture._get_repo_create_params(
99 csrf_token=csrf_token,
100 csrf_token=csrf_token,
100 repo_name=repo_name,
101 repo_name=repo_name,
101 repo_type=backend.alias,
102 repo_type=backend.alias,
102 repo_owner=TEST_USER_ADMIN_LOGIN,
103 repo_owner=TEST_USER_ADMIN_LOGIN,
103 repo_description='DESC',
104 repo_description='DESC',
104
105
105 repo_private='false',
106 repo_private='false',
106 repo_enable_locking='false',
107 repo_enable_locking='false',
107 repo_enable_downloads='false')
108 repo_enable_downloads='false')
108 params.update(update_settings)
109 params.update(update_settings)
109 self.app.post(
110 self.app.post(
110 route_path('edit_repo', repo_name=repo_name),
111 route_path('edit_repo', repo_name=repo_name),
111 params=params, status=302)
112 params=params, status=302)
112
113
113 repo = Repository.get_by_repo_name(repo_name)
114 repo = Repository.get_by_repo_name(repo_name)
114 assert repo.user.username == \
115 assert repo.user.username == \
115 update_settings.get('repo_owner', repo.user.username)
116 update_settings.get('repo_owner', repo.user.username)
116
117
117 assert repo.description == \
118 assert repo.description == \
118 update_settings.get('repo_description', repo.description)
119 update_settings.get('repo_description', repo.description)
119
120
120 assert repo.private == \
121 assert repo.private == \
121 str2bool(update_settings.get(
122 str2bool(update_settings.get(
122 'repo_private', repo.private))
123 'repo_private', repo.private))
123
124
124 assert repo.enable_locking == \
125 assert repo.enable_locking == \
125 str2bool(update_settings.get(
126 str2bool(update_settings.get(
126 'repo_enable_locking', repo.enable_locking))
127 'repo_enable_locking', repo.enable_locking))
127
128
128 assert repo.enable_downloads == \
129 assert repo.enable_downloads == \
129 str2bool(update_settings.get(
130 str2bool(update_settings.get(
130 'repo_enable_downloads', repo.enable_downloads))
131 'repo_enable_downloads', repo.enable_downloads))
131
132
132 def test_update_repo_name_via_settings(self, csrf_token, user_util, backend):
133 def test_update_repo_name_via_settings(self, csrf_token, user_util, backend):
133 repo = user_util.create_repo(repo_type=backend.alias)
134 repo = user_util.create_repo(repo_type=backend.alias)
134 repo_name = repo.repo_name
135 repo_name = repo.repo_name
135
136
136 repo_group = user_util.create_repo_group()
137 repo_group = user_util.create_repo_group()
137 repo_group_name = repo_group.group_name
138 repo_group_name = repo_group.group_name
138 new_name = repo_group_name + '_' + repo_name
139 new_name = repo_group_name + '_' + repo_name
139
140
140 params = fixture._get_repo_create_params(
141 params = fixture._get_repo_create_params(
141 csrf_token=csrf_token,
142 csrf_token=csrf_token,
142 repo_name=new_name,
143 repo_name=new_name,
143 repo_type=backend.alias,
144 repo_type=backend.alias,
144 repo_owner=TEST_USER_ADMIN_LOGIN,
145 repo_owner=TEST_USER_ADMIN_LOGIN,
145 repo_description='DESC',
146 repo_description='DESC',
146 repo_private='false',
147 repo_private='false',
147 repo_enable_locking='false',
148 repo_enable_locking='false',
148 repo_enable_downloads='false')
149 repo_enable_downloads='false')
149 self.app.post(
150 self.app.post(
150 route_path('edit_repo', repo_name=repo_name),
151 route_path('edit_repo', repo_name=repo_name),
151 params=params, status=302)
152 params=params, status=302)
152 repo = Repository.get_by_repo_name(new_name)
153 repo = Repository.get_by_repo_name(new_name)
153 assert repo.repo_name == new_name
154 assert repo.repo_name == new_name
154
155
155 def test_update_repo_group_via_settings(self, csrf_token, user_util, backend):
156 def test_update_repo_group_via_settings(self, csrf_token, user_util, backend):
156 repo = user_util.create_repo(repo_type=backend.alias)
157 repo = user_util.create_repo(repo_type=backend.alias)
157 repo_name = repo.repo_name
158 repo_name = repo.repo_name
158
159
159 repo_group = user_util.create_repo_group()
160 repo_group = user_util.create_repo_group()
160 repo_group_name = repo_group.group_name
161 repo_group_name = repo_group.group_name
161 repo_group_id = repo_group.group_id
162 repo_group_id = repo_group.group_id
162
163
163 new_name = repo_group_name + '/' + repo_name
164 new_name = repo_group_name + '/' + repo_name
164 params = fixture._get_repo_create_params(
165 params = fixture._get_repo_create_params(
165 csrf_token=csrf_token,
166 csrf_token=csrf_token,
166 repo_name=repo_name,
167 repo_name=repo_name,
167 repo_type=backend.alias,
168 repo_type=backend.alias,
168 repo_owner=TEST_USER_ADMIN_LOGIN,
169 repo_owner=TEST_USER_ADMIN_LOGIN,
169 repo_description='DESC',
170 repo_description='DESC',
170 repo_group=repo_group_id,
171 repo_group=repo_group_id,
171 repo_private='false',
172 repo_private='false',
172 repo_enable_locking='false',
173 repo_enable_locking='false',
173 repo_enable_downloads='false')
174 repo_enable_downloads='false')
174 self.app.post(
175 self.app.post(
175 route_path('edit_repo', repo_name=repo_name),
176 route_path('edit_repo', repo_name=repo_name),
176 params=params, status=302)
177 params=params, status=302)
177 repo = Repository.get_by_repo_name(new_name)
178 repo = Repository.get_by_repo_name(new_name)
178 assert repo.repo_name == new_name
179 assert repo.repo_name == new_name
179
180
180 def test_set_private_flag_sets_default_user_permissions_to_none(
181 def test_set_private_flag_sets_default_user_permissions_to_none(
181 self, autologin_user, backend, csrf_token):
182 self, autologin_user, backend, csrf_token):
182
183
183 # initially repository perm should be read
184 # initially repository perm should be read
184 perm = _get_permission_for_user(user='default', repo=backend.repo_name)
185 perm = _get_permission_for_user(user='default', repo=backend.repo_name)
185 assert len(perm) == 1
186 assert len(perm) == 1
186 assert perm[0].permission.permission_name == 'repository.read'
187 assert perm[0].permission.permission_name == 'repository.read'
187 assert not backend.repo.private
188 assert not backend.repo.private
188
189
189 response = self.app.post(
190 response = self.app.post(
190 route_path('edit_repo', repo_name=backend.repo_name),
191 route_path('edit_repo', repo_name=backend.repo_name),
191 params=fixture._get_repo_create_params(
192 params=fixture._get_repo_create_params(
192 repo_private='true',
193 repo_private='true',
193 repo_name=backend.repo_name,
194 repo_name=backend.repo_name,
194 repo_type=backend.alias,
195 repo_type=backend.alias,
195 repo_owner=TEST_USER_ADMIN_LOGIN,
196 repo_owner=TEST_USER_ADMIN_LOGIN,
196 csrf_token=csrf_token), status=302)
197 csrf_token=csrf_token), status=302)
197
198
198 assert_session_flash(
199 assert_session_flash(
199 response,
200 response,
200 msg='Repository %s updated successfully' % (backend.repo_name))
201 msg='Repository %s updated successfully' % (backend.repo_name))
201
202
202 repo = Repository.get_by_repo_name(backend.repo_name)
203 repo = Repository.get_by_repo_name(backend.repo_name)
203 assert repo.private is True
204 assert repo.private is True
204
205
205 # now the repo default permission should be None
206 # now the repo default permission should be None
206 perm = _get_permission_for_user(user='default', repo=backend.repo_name)
207 perm = _get_permission_for_user(user='default', repo=backend.repo_name)
207 assert len(perm) == 1
208 assert len(perm) == 1
208 assert perm[0].permission.permission_name == 'repository.none'
209 assert perm[0].permission.permission_name == 'repository.none'
209
210
210 response = self.app.post(
211 response = self.app.post(
211 route_path('edit_repo', repo_name=backend.repo_name),
212 route_path('edit_repo', repo_name=backend.repo_name),
212 params=fixture._get_repo_create_params(
213 params=fixture._get_repo_create_params(
213 repo_private='false',
214 repo_private='false',
214 repo_name=backend.repo_name,
215 repo_name=backend.repo_name,
215 repo_type=backend.alias,
216 repo_type=backend.alias,
216 repo_owner=TEST_USER_ADMIN_LOGIN,
217 repo_owner=TEST_USER_ADMIN_LOGIN,
217 csrf_token=csrf_token), status=302)
218 csrf_token=csrf_token), status=302)
218
219
219 assert_session_flash(
220 assert_session_flash(
220 response,
221 response,
221 msg='Repository %s updated successfully' % (backend.repo_name))
222 msg='Repository %s updated successfully' % (backend.repo_name))
222 assert backend.repo.private is False
223 assert backend.repo.private is False
223
224
224 # we turn off private now the repo default permission should stay None
225 # we turn off private now the repo default permission should stay None
225 perm = _get_permission_for_user(user='default', repo=backend.repo_name)
226 perm = _get_permission_for_user(user='default', repo=backend.repo_name)
226 assert len(perm) == 1
227 assert len(perm) == 1
227 assert perm[0].permission.permission_name == 'repository.none'
228 assert perm[0].permission.permission_name == 'repository.none'
228
229
229 # update this permission back
230 # update this permission back
230 perm[0].permission = Permission.get_by_key('repository.read')
231 perm[0].permission = Permission.get_by_key('repository.read')
231 Session().add(perm[0])
232 Session().add(perm[0])
232 Session().commit()
233 Session().commit()
@@ -1,1076 +1,1057 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')
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_fields', '/{repo_name}/settings/fields',
664 rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields',
665 controller='admin/repos', action='edit_fields',
665 controller='admin/repos', action='edit_fields',
666 conditions={'method': ['GET'], 'function': check_repo},
666 conditions={'method': ['GET'], 'function': check_repo},
667 requirements=URL_NAME_REQUIREMENTS)
667 requirements=URL_NAME_REQUIREMENTS)
668 rmap.connect('create_repo_fields', '/{repo_name}/settings/fields/new',
668 rmap.connect('create_repo_fields', '/{repo_name}/settings/fields/new',
669 controller='admin/repos', action='create_repo_field',
669 controller='admin/repos', action='create_repo_field',
670 conditions={'method': ['PUT'], 'function': check_repo},
670 conditions={'method': ['PUT'], 'function': check_repo},
671 requirements=URL_NAME_REQUIREMENTS)
671 requirements=URL_NAME_REQUIREMENTS)
672 rmap.connect('delete_repo_fields', '/{repo_name}/settings/fields/{field_id}',
672 rmap.connect('delete_repo_fields', '/{repo_name}/settings/fields/{field_id}',
673 controller='admin/repos', action='delete_repo_field',
673 controller='admin/repos', action='delete_repo_field',
674 conditions={'method': ['DELETE'], 'function': check_repo},
674 conditions={'method': ['DELETE'], 'function': check_repo},
675 requirements=URL_NAME_REQUIREMENTS)
675 requirements=URL_NAME_REQUIREMENTS)
676
676
677 rmap.connect('edit_repo_advanced', '/{repo_name}/settings/advanced',
678 controller='admin/repos', action='edit_advanced',
679 conditions={'method': ['GET'], 'function': check_repo},
680 requirements=URL_NAME_REQUIREMENTS)
681
682 rmap.connect('edit_repo_advanced_locking', '/{repo_name}/settings/advanced/locking',
683 controller='admin/repos', action='edit_advanced_locking',
684 conditions={'method': ['PUT'], 'function': check_repo},
685 requirements=URL_NAME_REQUIREMENTS)
686 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
677 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
687 controller='admin/repos', action='toggle_locking',
678 controller='admin/repos', action='toggle_locking',
688 conditions={'method': ['GET'], 'function': check_repo},
679 conditions={'method': ['GET'], 'function': check_repo},
689 requirements=URL_NAME_REQUIREMENTS)
680 requirements=URL_NAME_REQUIREMENTS)
690
681
691 rmap.connect('edit_repo_advanced_journal', '/{repo_name}/settings/advanced/journal',
692 controller='admin/repos', action='edit_advanced_journal',
693 conditions={'method': ['PUT'], 'function': check_repo},
694 requirements=URL_NAME_REQUIREMENTS)
695
696 rmap.connect('edit_repo_advanced_fork', '/{repo_name}/settings/advanced/fork',
697 controller='admin/repos', action='edit_advanced_fork',
698 conditions={'method': ['PUT'], 'function': check_repo},
699 requirements=URL_NAME_REQUIREMENTS)
700
701 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
682 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
702 controller='admin/repos', action='edit_remote_form',
683 controller='admin/repos', action='edit_remote_form',
703 conditions={'method': ['GET'], 'function': check_repo},
684 conditions={'method': ['GET'], 'function': check_repo},
704 requirements=URL_NAME_REQUIREMENTS)
685 requirements=URL_NAME_REQUIREMENTS)
705 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
686 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
706 controller='admin/repos', action='edit_remote',
687 controller='admin/repos', action='edit_remote',
707 conditions={'method': ['PUT'], 'function': check_repo},
688 conditions={'method': ['PUT'], 'function': check_repo},
708 requirements=URL_NAME_REQUIREMENTS)
689 requirements=URL_NAME_REQUIREMENTS)
709
690
710 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
691 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
711 controller='admin/repos', action='edit_statistics_form',
692 controller='admin/repos', action='edit_statistics_form',
712 conditions={'method': ['GET'], 'function': check_repo},
693 conditions={'method': ['GET'], 'function': check_repo},
713 requirements=URL_NAME_REQUIREMENTS)
694 requirements=URL_NAME_REQUIREMENTS)
714 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
695 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
715 controller='admin/repos', action='edit_statistics',
696 controller='admin/repos', action='edit_statistics',
716 conditions={'method': ['PUT'], 'function': check_repo},
697 conditions={'method': ['PUT'], 'function': check_repo},
717 requirements=URL_NAME_REQUIREMENTS)
698 requirements=URL_NAME_REQUIREMENTS)
718 rmap.connect('repo_settings_issuetracker',
699 rmap.connect('repo_settings_issuetracker',
719 '/{repo_name}/settings/issue-tracker',
700 '/{repo_name}/settings/issue-tracker',
720 controller='admin/repos', action='repo_issuetracker',
701 controller='admin/repos', action='repo_issuetracker',
721 conditions={'method': ['GET'], 'function': check_repo},
702 conditions={'method': ['GET'], 'function': check_repo},
722 requirements=URL_NAME_REQUIREMENTS)
703 requirements=URL_NAME_REQUIREMENTS)
723 rmap.connect('repo_issuetracker_test',
704 rmap.connect('repo_issuetracker_test',
724 '/{repo_name}/settings/issue-tracker/test',
705 '/{repo_name}/settings/issue-tracker/test',
725 controller='admin/repos', action='repo_issuetracker_test',
706 controller='admin/repos', action='repo_issuetracker_test',
726 conditions={'method': ['POST'], 'function': check_repo},
707 conditions={'method': ['POST'], 'function': check_repo},
727 requirements=URL_NAME_REQUIREMENTS)
708 requirements=URL_NAME_REQUIREMENTS)
728 rmap.connect('repo_issuetracker_delete',
709 rmap.connect('repo_issuetracker_delete',
729 '/{repo_name}/settings/issue-tracker/delete',
710 '/{repo_name}/settings/issue-tracker/delete',
730 controller='admin/repos', action='repo_issuetracker_delete',
711 controller='admin/repos', action='repo_issuetracker_delete',
731 conditions={'method': ['DELETE'], 'function': check_repo},
712 conditions={'method': ['DELETE'], 'function': check_repo},
732 requirements=URL_NAME_REQUIREMENTS)
713 requirements=URL_NAME_REQUIREMENTS)
733 rmap.connect('repo_issuetracker_save',
714 rmap.connect('repo_issuetracker_save',
734 '/{repo_name}/settings/issue-tracker/save',
715 '/{repo_name}/settings/issue-tracker/save',
735 controller='admin/repos', action='repo_issuetracker_save',
716 controller='admin/repos', action='repo_issuetracker_save',
736 conditions={'method': ['POST'], 'function': check_repo},
717 conditions={'method': ['POST'], 'function': check_repo},
737 requirements=URL_NAME_REQUIREMENTS)
718 requirements=URL_NAME_REQUIREMENTS)
738 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
719 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
739 controller='admin/repos', action='repo_settings_vcs_update',
720 controller='admin/repos', action='repo_settings_vcs_update',
740 conditions={'method': ['POST'], 'function': check_repo},
721 conditions={'method': ['POST'], 'function': check_repo},
741 requirements=URL_NAME_REQUIREMENTS)
722 requirements=URL_NAME_REQUIREMENTS)
742 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
723 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
743 controller='admin/repos', action='repo_settings_vcs',
724 controller='admin/repos', action='repo_settings_vcs',
744 conditions={'method': ['GET'], 'function': check_repo},
725 conditions={'method': ['GET'], 'function': check_repo},
745 requirements=URL_NAME_REQUIREMENTS)
726 requirements=URL_NAME_REQUIREMENTS)
746 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
727 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
747 controller='admin/repos', action='repo_delete_svn_pattern',
728 controller='admin/repos', action='repo_delete_svn_pattern',
748 conditions={'method': ['DELETE'], 'function': check_repo},
729 conditions={'method': ['DELETE'], 'function': check_repo},
749 requirements=URL_NAME_REQUIREMENTS)
730 requirements=URL_NAME_REQUIREMENTS)
750 rmap.connect('repo_pullrequest_settings', '/{repo_name}/settings/pullrequest',
731 rmap.connect('repo_pullrequest_settings', '/{repo_name}/settings/pullrequest',
751 controller='admin/repos', action='repo_settings_pullrequest',
732 controller='admin/repos', action='repo_settings_pullrequest',
752 conditions={'method': ['GET', 'POST'], 'function': check_repo},
733 conditions={'method': ['GET', 'POST'], 'function': check_repo},
753 requirements=URL_NAME_REQUIREMENTS)
734 requirements=URL_NAME_REQUIREMENTS)
754
735
755 # still working url for backward compat.
736 # still working url for backward compat.
756 rmap.connect('raw_changeset_home_depraced',
737 rmap.connect('raw_changeset_home_depraced',
757 '/{repo_name}/raw-changeset/{revision}',
738 '/{repo_name}/raw-changeset/{revision}',
758 controller='changeset', action='changeset_raw',
739 controller='changeset', action='changeset_raw',
759 revision='tip', conditions={'function': check_repo},
740 revision='tip', conditions={'function': check_repo},
760 requirements=URL_NAME_REQUIREMENTS)
741 requirements=URL_NAME_REQUIREMENTS)
761
742
762 # new URLs
743 # new URLs
763 rmap.connect('changeset_raw_home',
744 rmap.connect('changeset_raw_home',
764 '/{repo_name}/changeset-diff/{revision}',
745 '/{repo_name}/changeset-diff/{revision}',
765 controller='changeset', action='changeset_raw',
746 controller='changeset', action='changeset_raw',
766 revision='tip', conditions={'function': check_repo},
747 revision='tip', conditions={'function': check_repo},
767 requirements=URL_NAME_REQUIREMENTS)
748 requirements=URL_NAME_REQUIREMENTS)
768
749
769 rmap.connect('changeset_patch_home',
750 rmap.connect('changeset_patch_home',
770 '/{repo_name}/changeset-patch/{revision}',
751 '/{repo_name}/changeset-patch/{revision}',
771 controller='changeset', action='changeset_patch',
752 controller='changeset', action='changeset_patch',
772 revision='tip', conditions={'function': check_repo},
753 revision='tip', conditions={'function': check_repo},
773 requirements=URL_NAME_REQUIREMENTS)
754 requirements=URL_NAME_REQUIREMENTS)
774
755
775 rmap.connect('changeset_download_home',
756 rmap.connect('changeset_download_home',
776 '/{repo_name}/changeset-download/{revision}',
757 '/{repo_name}/changeset-download/{revision}',
777 controller='changeset', action='changeset_download',
758 controller='changeset', action='changeset_download',
778 revision='tip', conditions={'function': check_repo},
759 revision='tip', conditions={'function': check_repo},
779 requirements=URL_NAME_REQUIREMENTS)
760 requirements=URL_NAME_REQUIREMENTS)
780
761
781 rmap.connect('changeset_comment',
762 rmap.connect('changeset_comment',
782 '/{repo_name}/changeset/{revision}/comment', jsroute=True,
763 '/{repo_name}/changeset/{revision}/comment', jsroute=True,
783 controller='changeset', revision='tip', action='comment',
764 controller='changeset', revision='tip', action='comment',
784 conditions={'function': check_repo},
765 conditions={'function': check_repo},
785 requirements=URL_NAME_REQUIREMENTS)
766 requirements=URL_NAME_REQUIREMENTS)
786
767
787 rmap.connect('changeset_comment_preview',
768 rmap.connect('changeset_comment_preview',
788 '/{repo_name}/changeset/comment/preview', jsroute=True,
769 '/{repo_name}/changeset/comment/preview', jsroute=True,
789 controller='changeset', action='preview_comment',
770 controller='changeset', action='preview_comment',
790 conditions={'function': check_repo, 'method': ['POST']},
771 conditions={'function': check_repo, 'method': ['POST']},
791 requirements=URL_NAME_REQUIREMENTS)
772 requirements=URL_NAME_REQUIREMENTS)
792
773
793 rmap.connect('changeset_comment_delete',
774 rmap.connect('changeset_comment_delete',
794 '/{repo_name}/changeset/comment/{comment_id}/delete',
775 '/{repo_name}/changeset/comment/{comment_id}/delete',
795 controller='changeset', action='delete_comment',
776 controller='changeset', action='delete_comment',
796 conditions={'function': check_repo, 'method': ['DELETE']},
777 conditions={'function': check_repo, 'method': ['DELETE']},
797 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
778 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
798
779
799 rmap.connect('changeset_info', '/{repo_name}/changeset_info/{revision}',
780 rmap.connect('changeset_info', '/{repo_name}/changeset_info/{revision}',
800 controller='changeset', action='changeset_info',
781 controller='changeset', action='changeset_info',
801 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
782 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
802
783
803 rmap.connect('compare_home',
784 rmap.connect('compare_home',
804 '/{repo_name}/compare',
785 '/{repo_name}/compare',
805 controller='compare', action='index',
786 controller='compare', action='index',
806 conditions={'function': check_repo},
787 conditions={'function': check_repo},
807 requirements=URL_NAME_REQUIREMENTS)
788 requirements=URL_NAME_REQUIREMENTS)
808
789
809 rmap.connect('compare_url',
790 rmap.connect('compare_url',
810 '/{repo_name}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}',
791 '/{repo_name}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}',
811 controller='compare', action='compare',
792 controller='compare', action='compare',
812 conditions={'function': check_repo},
793 conditions={'function': check_repo},
813 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
794 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
814
795
815 rmap.connect('pullrequest_home',
796 rmap.connect('pullrequest_home',
816 '/{repo_name}/pull-request/new', controller='pullrequests',
797 '/{repo_name}/pull-request/new', controller='pullrequests',
817 action='index', conditions={'function': check_repo,
798 action='index', conditions={'function': check_repo,
818 'method': ['GET']},
799 'method': ['GET']},
819 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
800 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
820
801
821 rmap.connect('pullrequest',
802 rmap.connect('pullrequest',
822 '/{repo_name}/pull-request/new', controller='pullrequests',
803 '/{repo_name}/pull-request/new', controller='pullrequests',
823 action='create', conditions={'function': check_repo,
804 action='create', conditions={'function': check_repo,
824 'method': ['POST']},
805 'method': ['POST']},
825 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
806 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
826
807
827 rmap.connect('pullrequest_repo_refs',
808 rmap.connect('pullrequest_repo_refs',
828 '/{repo_name}/pull-request/refs/{target_repo_name:.*?[^/]}',
809 '/{repo_name}/pull-request/refs/{target_repo_name:.*?[^/]}',
829 controller='pullrequests',
810 controller='pullrequests',
830 action='get_repo_refs',
811 action='get_repo_refs',
831 conditions={'function': check_repo, 'method': ['GET']},
812 conditions={'function': check_repo, 'method': ['GET']},
832 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
813 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
833
814
834 rmap.connect('pullrequest_repo_destinations',
815 rmap.connect('pullrequest_repo_destinations',
835 '/{repo_name}/pull-request/repo-destinations',
816 '/{repo_name}/pull-request/repo-destinations',
836 controller='pullrequests',
817 controller='pullrequests',
837 action='get_repo_destinations',
818 action='get_repo_destinations',
838 conditions={'function': check_repo, 'method': ['GET']},
819 conditions={'function': check_repo, 'method': ['GET']},
839 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
820 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
840
821
841 rmap.connect('pullrequest_show',
822 rmap.connect('pullrequest_show',
842 '/{repo_name}/pull-request/{pull_request_id}',
823 '/{repo_name}/pull-request/{pull_request_id}',
843 controller='pullrequests',
824 controller='pullrequests',
844 action='show', conditions={'function': check_repo,
825 action='show', conditions={'function': check_repo,
845 'method': ['GET']},
826 'method': ['GET']},
846 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
827 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
847
828
848 rmap.connect('pullrequest_update',
829 rmap.connect('pullrequest_update',
849 '/{repo_name}/pull-request/{pull_request_id}',
830 '/{repo_name}/pull-request/{pull_request_id}',
850 controller='pullrequests',
831 controller='pullrequests',
851 action='update', conditions={'function': check_repo,
832 action='update', conditions={'function': check_repo,
852 'method': ['PUT']},
833 'method': ['PUT']},
853 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
834 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
854
835
855 rmap.connect('pullrequest_merge',
836 rmap.connect('pullrequest_merge',
856 '/{repo_name}/pull-request/{pull_request_id}',
837 '/{repo_name}/pull-request/{pull_request_id}',
857 controller='pullrequests',
838 controller='pullrequests',
858 action='merge', conditions={'function': check_repo,
839 action='merge', conditions={'function': check_repo,
859 'method': ['POST']},
840 'method': ['POST']},
860 requirements=URL_NAME_REQUIREMENTS)
841 requirements=URL_NAME_REQUIREMENTS)
861
842
862 rmap.connect('pullrequest_delete',
843 rmap.connect('pullrequest_delete',
863 '/{repo_name}/pull-request/{pull_request_id}',
844 '/{repo_name}/pull-request/{pull_request_id}',
864 controller='pullrequests',
845 controller='pullrequests',
865 action='delete', conditions={'function': check_repo,
846 action='delete', conditions={'function': check_repo,
866 'method': ['DELETE']},
847 'method': ['DELETE']},
867 requirements=URL_NAME_REQUIREMENTS)
848 requirements=URL_NAME_REQUIREMENTS)
868
849
869 rmap.connect('pullrequest_show_all',
850 rmap.connect('pullrequest_show_all',
870 '/{repo_name}/pull-request',
851 '/{repo_name}/pull-request',
871 controller='pullrequests',
852 controller='pullrequests',
872 action='show_all', conditions={'function': check_repo,
853 action='show_all', conditions={'function': check_repo,
873 'method': ['GET']},
854 'method': ['GET']},
874 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
855 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
875
856
876 rmap.connect('pullrequest_comment',
857 rmap.connect('pullrequest_comment',
877 '/{repo_name}/pull-request-comment/{pull_request_id}',
858 '/{repo_name}/pull-request-comment/{pull_request_id}',
878 controller='pullrequests',
859 controller='pullrequests',
879 action='comment', conditions={'function': check_repo,
860 action='comment', conditions={'function': check_repo,
880 'method': ['POST']},
861 'method': ['POST']},
881 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
862 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
882
863
883 rmap.connect('pullrequest_comment_delete',
864 rmap.connect('pullrequest_comment_delete',
884 '/{repo_name}/pull-request-comment/{comment_id}/delete',
865 '/{repo_name}/pull-request-comment/{comment_id}/delete',
885 controller='pullrequests', action='delete_comment',
866 controller='pullrequests', action='delete_comment',
886 conditions={'function': check_repo, 'method': ['DELETE']},
867 conditions={'function': check_repo, 'method': ['DELETE']},
887 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
868 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
888
869
889 rmap.connect('summary_home_explicit', '/{repo_name}/summary',
870 rmap.connect('summary_home_explicit', '/{repo_name}/summary',
890 controller='summary', conditions={'function': check_repo},
871 controller='summary', conditions={'function': check_repo},
891 requirements=URL_NAME_REQUIREMENTS)
872 requirements=URL_NAME_REQUIREMENTS)
892
873
893 rmap.connect('changelog_home', '/{repo_name}/changelog', jsroute=True,
874 rmap.connect('changelog_home', '/{repo_name}/changelog', jsroute=True,
894 controller='changelog', conditions={'function': check_repo},
875 controller='changelog', conditions={'function': check_repo},
895 requirements=URL_NAME_REQUIREMENTS)
876 requirements=URL_NAME_REQUIREMENTS)
896
877
897 rmap.connect('changelog_summary_home', '/{repo_name}/changelog_summary',
878 rmap.connect('changelog_summary_home', '/{repo_name}/changelog_summary',
898 controller='changelog', action='changelog_summary',
879 controller='changelog', action='changelog_summary',
899 conditions={'function': check_repo},
880 conditions={'function': check_repo},
900 requirements=URL_NAME_REQUIREMENTS)
881 requirements=URL_NAME_REQUIREMENTS)
901
882
902 rmap.connect('changelog_file_home',
883 rmap.connect('changelog_file_home',
903 '/{repo_name}/changelog/{revision}/{f_path}',
884 '/{repo_name}/changelog/{revision}/{f_path}',
904 controller='changelog', f_path=None,
885 controller='changelog', f_path=None,
905 conditions={'function': check_repo},
886 conditions={'function': check_repo},
906 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
887 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
907
888
908 rmap.connect('changelog_elements', '/{repo_name}/changelog_details',
889 rmap.connect('changelog_elements', '/{repo_name}/changelog_details',
909 controller='changelog', action='changelog_elements',
890 controller='changelog', action='changelog_elements',
910 conditions={'function': check_repo},
891 conditions={'function': check_repo},
911 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
892 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
912
893
913 rmap.connect('files_home', '/{repo_name}/files/{revision}/{f_path}',
894 rmap.connect('files_home', '/{repo_name}/files/{revision}/{f_path}',
914 controller='files', revision='tip', f_path='',
895 controller='files', revision='tip', f_path='',
915 conditions={'function': check_repo},
896 conditions={'function': check_repo},
916 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
897 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
917
898
918 rmap.connect('files_home_simple_catchrev',
899 rmap.connect('files_home_simple_catchrev',
919 '/{repo_name}/files/{revision}',
900 '/{repo_name}/files/{revision}',
920 controller='files', revision='tip', f_path='',
901 controller='files', revision='tip', f_path='',
921 conditions={'function': check_repo},
902 conditions={'function': check_repo},
922 requirements=URL_NAME_REQUIREMENTS)
903 requirements=URL_NAME_REQUIREMENTS)
923
904
924 rmap.connect('files_home_simple_catchall',
905 rmap.connect('files_home_simple_catchall',
925 '/{repo_name}/files',
906 '/{repo_name}/files',
926 controller='files', revision='tip', f_path='',
907 controller='files', revision='tip', f_path='',
927 conditions={'function': check_repo},
908 conditions={'function': check_repo},
928 requirements=URL_NAME_REQUIREMENTS)
909 requirements=URL_NAME_REQUIREMENTS)
929
910
930 rmap.connect('files_history_home',
911 rmap.connect('files_history_home',
931 '/{repo_name}/history/{revision}/{f_path}',
912 '/{repo_name}/history/{revision}/{f_path}',
932 controller='files', action='history', revision='tip', f_path='',
913 controller='files', action='history', revision='tip', f_path='',
933 conditions={'function': check_repo},
914 conditions={'function': check_repo},
934 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
915 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
935
916
936 rmap.connect('files_authors_home',
917 rmap.connect('files_authors_home',
937 '/{repo_name}/authors/{revision}/{f_path}',
918 '/{repo_name}/authors/{revision}/{f_path}',
938 controller='files', action='authors', revision='tip', f_path='',
919 controller='files', action='authors', revision='tip', f_path='',
939 conditions={'function': check_repo},
920 conditions={'function': check_repo},
940 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
921 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
941
922
942 rmap.connect('files_diff_home', '/{repo_name}/diff/{f_path}',
923 rmap.connect('files_diff_home', '/{repo_name}/diff/{f_path}',
943 controller='files', action='diff', f_path='',
924 controller='files', action='diff', f_path='',
944 conditions={'function': check_repo},
925 conditions={'function': check_repo},
945 requirements=URL_NAME_REQUIREMENTS)
926 requirements=URL_NAME_REQUIREMENTS)
946
927
947 rmap.connect('files_diff_2way_home',
928 rmap.connect('files_diff_2way_home',
948 '/{repo_name}/diff-2way/{f_path}',
929 '/{repo_name}/diff-2way/{f_path}',
949 controller='files', action='diff_2way', f_path='',
930 controller='files', action='diff_2way', f_path='',
950 conditions={'function': check_repo},
931 conditions={'function': check_repo},
951 requirements=URL_NAME_REQUIREMENTS)
932 requirements=URL_NAME_REQUIREMENTS)
952
933
953 rmap.connect('files_rawfile_home',
934 rmap.connect('files_rawfile_home',
954 '/{repo_name}/rawfile/{revision}/{f_path}',
935 '/{repo_name}/rawfile/{revision}/{f_path}',
955 controller='files', action='rawfile', revision='tip',
936 controller='files', action='rawfile', revision='tip',
956 f_path='', conditions={'function': check_repo},
937 f_path='', conditions={'function': check_repo},
957 requirements=URL_NAME_REQUIREMENTS)
938 requirements=URL_NAME_REQUIREMENTS)
958
939
959 rmap.connect('files_raw_home',
940 rmap.connect('files_raw_home',
960 '/{repo_name}/raw/{revision}/{f_path}',
941 '/{repo_name}/raw/{revision}/{f_path}',
961 controller='files', action='raw', revision='tip', f_path='',
942 controller='files', action='raw', revision='tip', f_path='',
962 conditions={'function': check_repo},
943 conditions={'function': check_repo},
963 requirements=URL_NAME_REQUIREMENTS)
944 requirements=URL_NAME_REQUIREMENTS)
964
945
965 rmap.connect('files_render_home',
946 rmap.connect('files_render_home',
966 '/{repo_name}/render/{revision}/{f_path}',
947 '/{repo_name}/render/{revision}/{f_path}',
967 controller='files', action='index', revision='tip', f_path='',
948 controller='files', action='index', revision='tip', f_path='',
968 rendered=True, conditions={'function': check_repo},
949 rendered=True, conditions={'function': check_repo},
969 requirements=URL_NAME_REQUIREMENTS)
950 requirements=URL_NAME_REQUIREMENTS)
970
951
971 rmap.connect('files_annotate_home',
952 rmap.connect('files_annotate_home',
972 '/{repo_name}/annotate/{revision}/{f_path}',
953 '/{repo_name}/annotate/{revision}/{f_path}',
973 controller='files', action='index', revision='tip',
954 controller='files', action='index', revision='tip',
974 f_path='', annotate=True, conditions={'function': check_repo},
955 f_path='', annotate=True, conditions={'function': check_repo},
975 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
956 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
976
957
977 rmap.connect('files_annotate_previous',
958 rmap.connect('files_annotate_previous',
978 '/{repo_name}/annotate-previous/{revision}/{f_path}',
959 '/{repo_name}/annotate-previous/{revision}/{f_path}',
979 controller='files', action='annotate_previous', revision='tip',
960 controller='files', action='annotate_previous', revision='tip',
980 f_path='', annotate=True, conditions={'function': check_repo},
961 f_path='', annotate=True, conditions={'function': check_repo},
981 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
962 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
982
963
983 rmap.connect('files_edit',
964 rmap.connect('files_edit',
984 '/{repo_name}/edit/{revision}/{f_path}',
965 '/{repo_name}/edit/{revision}/{f_path}',
985 controller='files', action='edit', revision='tip',
966 controller='files', action='edit', revision='tip',
986 f_path='',
967 f_path='',
987 conditions={'function': check_repo, 'method': ['POST']},
968 conditions={'function': check_repo, 'method': ['POST']},
988 requirements=URL_NAME_REQUIREMENTS)
969 requirements=URL_NAME_REQUIREMENTS)
989
970
990 rmap.connect('files_edit_home',
971 rmap.connect('files_edit_home',
991 '/{repo_name}/edit/{revision}/{f_path}',
972 '/{repo_name}/edit/{revision}/{f_path}',
992 controller='files', action='edit_home', revision='tip',
973 controller='files', action='edit_home', revision='tip',
993 f_path='', conditions={'function': check_repo},
974 f_path='', conditions={'function': check_repo},
994 requirements=URL_NAME_REQUIREMENTS)
975 requirements=URL_NAME_REQUIREMENTS)
995
976
996 rmap.connect('files_add',
977 rmap.connect('files_add',
997 '/{repo_name}/add/{revision}/{f_path}',
978 '/{repo_name}/add/{revision}/{f_path}',
998 controller='files', action='add', revision='tip',
979 controller='files', action='add', revision='tip',
999 f_path='',
980 f_path='',
1000 conditions={'function': check_repo, 'method': ['POST']},
981 conditions={'function': check_repo, 'method': ['POST']},
1001 requirements=URL_NAME_REQUIREMENTS)
982 requirements=URL_NAME_REQUIREMENTS)
1002
983
1003 rmap.connect('files_add_home',
984 rmap.connect('files_add_home',
1004 '/{repo_name}/add/{revision}/{f_path}',
985 '/{repo_name}/add/{revision}/{f_path}',
1005 controller='files', action='add_home', revision='tip',
986 controller='files', action='add_home', revision='tip',
1006 f_path='', conditions={'function': check_repo},
987 f_path='', conditions={'function': check_repo},
1007 requirements=URL_NAME_REQUIREMENTS)
988 requirements=URL_NAME_REQUIREMENTS)
1008
989
1009 rmap.connect('files_delete',
990 rmap.connect('files_delete',
1010 '/{repo_name}/delete/{revision}/{f_path}',
991 '/{repo_name}/delete/{revision}/{f_path}',
1011 controller='files', action='delete', revision='tip',
992 controller='files', action='delete', revision='tip',
1012 f_path='',
993 f_path='',
1013 conditions={'function': check_repo, 'method': ['POST']},
994 conditions={'function': check_repo, 'method': ['POST']},
1014 requirements=URL_NAME_REQUIREMENTS)
995 requirements=URL_NAME_REQUIREMENTS)
1015
996
1016 rmap.connect('files_delete_home',
997 rmap.connect('files_delete_home',
1017 '/{repo_name}/delete/{revision}/{f_path}',
998 '/{repo_name}/delete/{revision}/{f_path}',
1018 controller='files', action='delete_home', revision='tip',
999 controller='files', action='delete_home', revision='tip',
1019 f_path='', conditions={'function': check_repo},
1000 f_path='', conditions={'function': check_repo},
1020 requirements=URL_NAME_REQUIREMENTS)
1001 requirements=URL_NAME_REQUIREMENTS)
1021
1002
1022 rmap.connect('files_archive_home', '/{repo_name}/archive/{fname}',
1003 rmap.connect('files_archive_home', '/{repo_name}/archive/{fname}',
1023 controller='files', action='archivefile',
1004 controller='files', action='archivefile',
1024 conditions={'function': check_repo},
1005 conditions={'function': check_repo},
1025 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1006 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1026
1007
1027 rmap.connect('files_nodelist_home',
1008 rmap.connect('files_nodelist_home',
1028 '/{repo_name}/nodelist/{revision}/{f_path}',
1009 '/{repo_name}/nodelist/{revision}/{f_path}',
1029 controller='files', action='nodelist',
1010 controller='files', action='nodelist',
1030 conditions={'function': check_repo},
1011 conditions={'function': check_repo},
1031 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1012 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1032
1013
1033 rmap.connect('files_nodetree_full',
1014 rmap.connect('files_nodetree_full',
1034 '/{repo_name}/nodetree_full/{commit_id}/{f_path}',
1015 '/{repo_name}/nodetree_full/{commit_id}/{f_path}',
1035 controller='files', action='nodetree_full',
1016 controller='files', action='nodetree_full',
1036 conditions={'function': check_repo},
1017 conditions={'function': check_repo},
1037 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1018 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1038
1019
1039 rmap.connect('repo_fork_create_home', '/{repo_name}/fork',
1020 rmap.connect('repo_fork_create_home', '/{repo_name}/fork',
1040 controller='forks', action='fork_create',
1021 controller='forks', action='fork_create',
1041 conditions={'function': check_repo, 'method': ['POST']},
1022 conditions={'function': check_repo, 'method': ['POST']},
1042 requirements=URL_NAME_REQUIREMENTS)
1023 requirements=URL_NAME_REQUIREMENTS)
1043
1024
1044 rmap.connect('repo_fork_home', '/{repo_name}/fork',
1025 rmap.connect('repo_fork_home', '/{repo_name}/fork',
1045 controller='forks', action='fork',
1026 controller='forks', action='fork',
1046 conditions={'function': check_repo},
1027 conditions={'function': check_repo},
1047 requirements=URL_NAME_REQUIREMENTS)
1028 requirements=URL_NAME_REQUIREMENTS)
1048
1029
1049 rmap.connect('repo_forks_home', '/{repo_name}/forks',
1030 rmap.connect('repo_forks_home', '/{repo_name}/forks',
1050 controller='forks', action='forks',
1031 controller='forks', action='forks',
1051 conditions={'function': check_repo},
1032 conditions={'function': check_repo},
1052 requirements=URL_NAME_REQUIREMENTS)
1033 requirements=URL_NAME_REQUIREMENTS)
1053
1034
1054 # must be here for proper group/repo catching pattern
1035 # must be here for proper group/repo catching pattern
1055 _connect_with_slash(
1036 _connect_with_slash(
1056 rmap, 'repo_group_home', '/{group_name}',
1037 rmap, 'repo_group_home', '/{group_name}',
1057 controller='home', action='index_repo_group',
1038 controller='home', action='index_repo_group',
1058 conditions={'function': check_group},
1039 conditions={'function': check_group},
1059 requirements=URL_NAME_REQUIREMENTS)
1040 requirements=URL_NAME_REQUIREMENTS)
1060
1041
1061 # catch all, at the end
1042 # catch all, at the end
1062 _connect_with_slash(
1043 _connect_with_slash(
1063 rmap, 'summary_home', '/{repo_name}', jsroute=True,
1044 rmap, 'summary_home', '/{repo_name}', jsroute=True,
1064 controller='summary', action='index',
1045 controller='summary', action='index',
1065 conditions={'function': check_repo},
1046 conditions={'function': check_repo},
1066 requirements=URL_NAME_REQUIREMENTS)
1047 requirements=URL_NAME_REQUIREMENTS)
1067
1048
1068 return rmap
1049 return rmap
1069
1050
1070
1051
1071 def _connect_with_slash(mapper, name, path, *args, **kwargs):
1052 def _connect_with_slash(mapper, name, path, *args, **kwargs):
1072 """
1053 """
1073 Connect a route with an optional trailing slash in `path`.
1054 Connect a route with an optional trailing slash in `path`.
1074 """
1055 """
1075 mapper.connect(name + '_slash', path + '/', *args, **kwargs)
1056 mapper.connect(name + '_slash', path + '/', *args, **kwargs)
1076 mapper.connect(name, path, *args, **kwargs)
1057 mapper.connect(name, path, *args, **kwargs)
@@ -1,754 +1,610 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')
292 @auth.CSRFRequired()
293 def delete(self, repo_name):
294 """
295 DELETE /repos/repo_name: Delete an existing item"""
296 # Forms posted to this method should contain a hidden field:
297 # <input type="hidden" name="_method" value="DELETE" />
298 # Or using helpers:
299 # h.form(url('repo', repo_name=ID),
300 # method='delete')
301 # url('repo', repo_name=ID)
302
303 repo_model = RepoModel()
304 repo = repo_model.get_by_repo_name(repo_name)
305 if not repo:
306 h.not_mapped_error(repo_name)
307 return redirect(url('repos'))
308 try:
309 _forks = repo.forks.count()
310 handle_forks = None
311 if _forks and request.POST.get('forks'):
312 do = request.POST['forks']
313 if do == 'detach_forks':
314 handle_forks = 'detach'
315 h.flash(_('Detached %s forks') % _forks, category='success')
316 elif do == 'delete_forks':
317 handle_forks = 'delete'
318 h.flash(_('Deleted %s forks') % _forks, category='success')
319 repo_model.delete(repo, forks=handle_forks)
320 action_logger(c.rhodecode_user, 'admin_deleted_repo',
321 repo_name, self.ip_addr, self.sa)
322 ScmModel().mark_for_invalidation(repo_name)
323 h.flash(_('Deleted repository %s') % repo_name, category='success')
324 Session().commit()
325 except AttachedForksError:
326 h.flash(_('Cannot delete %s it still contains attached forks')
327 % repo_name, category='warning')
328
329 except Exception:
330 log.exception("Exception during deletion of repository")
331 h.flash(_('An error occurred during deletion of %s') % repo_name,
332 category='error')
333
334 return redirect(url('repos'))
335
336 @HasPermissionAllDecorator('hg.admin')
291 @HasPermissionAllDecorator('hg.admin')
337 def show(self, repo_name, format='html'):
292 def show(self, repo_name, format='html'):
338 """GET /repos/repo_name: Show a specific item"""
293 """GET /repos/repo_name: Show a specific item"""
339 # url('repo', repo_name=ID)
294 # url('repo', repo_name=ID)
340
295
341 @HasRepoPermissionAllDecorator('repository.admin')
296 @HasRepoPermissionAllDecorator('repository.admin')
342 def edit_fields(self, repo_name):
297 def edit_fields(self, repo_name):
343 """GET /repo_name/settings: Form to edit an existing item"""
298 """GET /repo_name/settings: Form to edit an existing item"""
344 c.repo_info = self._load_repo(repo_name)
299 c.repo_info = self._load_repo(repo_name)
345 c.repo_fields = RepositoryField.query()\
300 c.repo_fields = RepositoryField.query()\
346 .filter(RepositoryField.repository == c.repo_info).all()
301 .filter(RepositoryField.repository == c.repo_info).all()
347 c.active = 'fields'
302 c.active = 'fields'
348 if request.POST:
303 if request.POST:
349
304
350 return redirect(url('repo_edit_fields'))
305 return redirect(url('repo_edit_fields'))
351 return render('admin/repos/repo_edit.mako')
306 return render('admin/repos/repo_edit.mako')
352
307
353 @HasRepoPermissionAllDecorator('repository.admin')
308 @HasRepoPermissionAllDecorator('repository.admin')
354 @auth.CSRFRequired()
309 @auth.CSRFRequired()
355 def create_repo_field(self, repo_name):
310 def create_repo_field(self, repo_name):
356 try:
311 try:
357 form_result = RepoFieldForm()().to_python(dict(request.POST))
312 form_result = RepoFieldForm()().to_python(dict(request.POST))
358 RepoModel().add_repo_field(
313 RepoModel().add_repo_field(
359 repo_name, form_result['new_field_key'],
314 repo_name, form_result['new_field_key'],
360 field_type=form_result['new_field_type'],
315 field_type=form_result['new_field_type'],
361 field_value=form_result['new_field_value'],
316 field_value=form_result['new_field_value'],
362 field_label=form_result['new_field_label'],
317 field_label=form_result['new_field_label'],
363 field_desc=form_result['new_field_desc'])
318 field_desc=form_result['new_field_desc'])
364
319
365 Session().commit()
320 Session().commit()
366 except Exception as e:
321 except Exception as e:
367 log.exception("Exception creating field")
322 log.exception("Exception creating field")
368 msg = _('An error occurred during creation of field')
323 msg = _('An error occurred during creation of field')
369 if isinstance(e, formencode.Invalid):
324 if isinstance(e, formencode.Invalid):
370 msg += ". " + e.msg
325 msg += ". " + e.msg
371 h.flash(msg, category='error')
326 h.flash(msg, category='error')
372 return redirect(url('edit_repo_fields', repo_name=repo_name))
327 return redirect(url('edit_repo_fields', repo_name=repo_name))
373
328
374 @HasRepoPermissionAllDecorator('repository.admin')
329 @HasRepoPermissionAllDecorator('repository.admin')
375 @auth.CSRFRequired()
330 @auth.CSRFRequired()
376 def delete_repo_field(self, repo_name, field_id):
331 def delete_repo_field(self, repo_name, field_id):
377 field = RepositoryField.get_or_404(field_id)
332 field = RepositoryField.get_or_404(field_id)
378 try:
333 try:
379 RepoModel().delete_repo_field(repo_name, field.field_key)
334 RepoModel().delete_repo_field(repo_name, field.field_key)
380 Session().commit()
335 Session().commit()
381 except Exception as e:
336 except Exception as e:
382 log.exception("Exception during removal of field")
337 log.exception("Exception during removal of field")
383 msg = _('An error occurred during removal of field')
338 msg = _('An error occurred during removal of field')
384 h.flash(msg, category='error')
339 h.flash(msg, category='error')
385 return redirect(url('edit_repo_fields', repo_name=repo_name))
340 return redirect(url('edit_repo_fields', repo_name=repo_name))
386
341
387 @HasRepoPermissionAllDecorator('repository.admin')
388 def edit_advanced(self, repo_name):
389 """GET /repo_name/settings: Form to edit an existing item"""
390 c.repo_info = self._load_repo(repo_name)
391 c.default_user_id = User.get_default_user().user_id
392 c.in_public_journal = UserFollowing.query()\
393 .filter(UserFollowing.user_id == c.default_user_id)\
394 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
395
396 c.active = 'advanced'
397 c.has_origin_repo_read_perm = False
398 if c.repo_info.fork:
399 c.has_origin_repo_read_perm = h.HasRepoPermissionAny(
400 'repository.write', 'repository.read', 'repository.admin')(
401 c.repo_info.fork.repo_name, 'repo set as fork page')
402
403 if request.POST:
404 return redirect(url('repo_edit_advanced'))
405 return render('admin/repos/repo_edit.mako')
406
407 @HasRepoPermissionAllDecorator('repository.admin')
408 @auth.CSRFRequired()
409 def edit_advanced_journal(self, repo_name):
410 """
411 Set's this repository to be visible in public journal,
412 in other words assing default user to follow this repo
413
414 :param repo_name:
415 """
416
417 try:
418 repo_id = Repository.get_by_repo_name(repo_name).repo_id
419 user_id = User.get_default_user().user_id
420 self.scm_model.toggle_following_repo(repo_id, user_id)
421 h.flash(_('Updated repository visibility in public journal'),
422 category='success')
423 Session().commit()
424 except Exception:
425 h.flash(_('An error occurred during setting this'
426 ' repository in public journal'),
427 category='error')
428
429 return redirect(url('edit_repo_advanced', repo_name=repo_name))
430
431 @HasRepoPermissionAllDecorator('repository.admin')
432 @auth.CSRFRequired()
433 def edit_advanced_fork(self, repo_name):
434 """
435 Mark given repository as a fork of another
436
437 :param repo_name:
438 """
439
440 new_fork_id = request.POST.get('id_fork_of')
441 try:
442
443 if new_fork_id and not new_fork_id.isdigit():
444 log.error('Given fork id %s is not an INT', new_fork_id)
445
446 fork_id = safe_int(new_fork_id)
447 repo = ScmModel().mark_as_fork(repo_name, fork_id,
448 c.rhodecode_user.username)
449 fork = repo.fork.repo_name if repo.fork else _('Nothing')
450 Session().commit()
451 h.flash(_('Marked repo %s as fork of %s') % (repo_name, fork),
452 category='success')
453 except RepositoryError as e:
454 log.exception("Repository Error occurred")
455 h.flash(str(e), category='error')
456 except Exception as e:
457 log.exception("Exception while editing fork")
458 h.flash(_('An error occurred during this operation'),
459 category='error')
460
461 return redirect(url('edit_repo_advanced', repo_name=repo_name))
462
463 @HasRepoPermissionAllDecorator('repository.admin')
464 @auth.CSRFRequired()
465 def edit_advanced_locking(self, repo_name):
466 """
467 Unlock repository when it is locked !
468
469 :param repo_name:
470 """
471 try:
472 repo = Repository.get_by_repo_name(repo_name)
473 if request.POST.get('set_lock'):
474 Repository.lock(repo, c.rhodecode_user.user_id,
475 lock_reason=Repository.LOCK_WEB)
476 h.flash(_('Locked repository'), category='success')
477 elif request.POST.get('set_unlock'):
478 Repository.unlock(repo)
479 h.flash(_('Unlocked repository'), category='success')
480 except Exception as e:
481 log.exception("Exception during unlocking")
482 h.flash(_('An error occurred during unlocking'),
483 category='error')
484 return redirect(url('edit_repo_advanced', repo_name=repo_name))
485
486 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
342 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
487 @auth.CSRFRequired()
343 @auth.CSRFRequired()
488 def toggle_locking(self, repo_name):
344 def toggle_locking(self, repo_name):
489 """
345 """
490 Toggle locking of repository by simple GET call to url
346 Toggle locking of repository by simple GET call to url
491
347
492 :param repo_name:
348 :param repo_name:
493 """
349 """
494
350
495 try:
351 try:
496 repo = Repository.get_by_repo_name(repo_name)
352 repo = Repository.get_by_repo_name(repo_name)
497
353
498 if repo.enable_locking:
354 if repo.enable_locking:
499 if repo.locked[0]:
355 if repo.locked[0]:
500 Repository.unlock(repo)
356 Repository.unlock(repo)
501 action = _('Unlocked')
357 action = _('Unlocked')
502 else:
358 else:
503 Repository.lock(repo, c.rhodecode_user.user_id,
359 Repository.lock(repo, c.rhodecode_user.user_id,
504 lock_reason=Repository.LOCK_WEB)
360 lock_reason=Repository.LOCK_WEB)
505 action = _('Locked')
361 action = _('Locked')
506
362
507 h.flash(_('Repository has been %s') % action,
363 h.flash(_('Repository has been %s') % action,
508 category='success')
364 category='success')
509 except Exception:
365 except Exception:
510 log.exception("Exception during unlocking")
366 log.exception("Exception during unlocking")
511 h.flash(_('An error occurred during unlocking'),
367 h.flash(_('An error occurred during unlocking'),
512 category='error')
368 category='error')
513 return redirect(url('summary_home', repo_name=repo_name))
369 return redirect(url('summary_home', repo_name=repo_name))
514
370
515 @HasRepoPermissionAllDecorator('repository.admin')
371 @HasRepoPermissionAllDecorator('repository.admin')
516 @auth.CSRFRequired()
372 @auth.CSRFRequired()
517 def edit_remote(self, repo_name):
373 def edit_remote(self, repo_name):
518 """PUT /{repo_name}/settings/remote: edit the repo remote."""
374 """PUT /{repo_name}/settings/remote: edit the repo remote."""
519 try:
375 try:
520 ScmModel().pull_changes(repo_name, c.rhodecode_user.username)
376 ScmModel().pull_changes(repo_name, c.rhodecode_user.username)
521 h.flash(_('Pulled from remote location'), category='success')
377 h.flash(_('Pulled from remote location'), category='success')
522 except Exception:
378 except Exception:
523 log.exception("Exception during pull from remote")
379 log.exception("Exception during pull from remote")
524 h.flash(_('An error occurred during pull from remote location'),
380 h.flash(_('An error occurred during pull from remote location'),
525 category='error')
381 category='error')
526 return redirect(url('edit_repo_remote', repo_name=c.repo_name))
382 return redirect(url('edit_repo_remote', repo_name=c.repo_name))
527
383
528 @HasRepoPermissionAllDecorator('repository.admin')
384 @HasRepoPermissionAllDecorator('repository.admin')
529 def edit_remote_form(self, repo_name):
385 def edit_remote_form(self, repo_name):
530 """GET /repo_name/settings: Form to edit an existing item"""
386 """GET /repo_name/settings: Form to edit an existing item"""
531 c.repo_info = self._load_repo(repo_name)
387 c.repo_info = self._load_repo(repo_name)
532 c.active = 'remote'
388 c.active = 'remote'
533
389
534 return render('admin/repos/repo_edit.mako')
390 return render('admin/repos/repo_edit.mako')
535
391
536 @HasRepoPermissionAllDecorator('repository.admin')
392 @HasRepoPermissionAllDecorator('repository.admin')
537 @auth.CSRFRequired()
393 @auth.CSRFRequired()
538 def edit_statistics(self, repo_name):
394 def edit_statistics(self, repo_name):
539 """PUT /{repo_name}/settings/statistics: reset the repo statistics."""
395 """PUT /{repo_name}/settings/statistics: reset the repo statistics."""
540 try:
396 try:
541 RepoModel().delete_stats(repo_name)
397 RepoModel().delete_stats(repo_name)
542 Session().commit()
398 Session().commit()
543 except Exception as e:
399 except Exception as e:
544 log.error(traceback.format_exc())
400 log.error(traceback.format_exc())
545 h.flash(_('An error occurred during deletion of repository stats'),
401 h.flash(_('An error occurred during deletion of repository stats'),
546 category='error')
402 category='error')
547 return redirect(url('edit_repo_statistics', repo_name=c.repo_name))
403 return redirect(url('edit_repo_statistics', repo_name=c.repo_name))
548
404
549 @HasRepoPermissionAllDecorator('repository.admin')
405 @HasRepoPermissionAllDecorator('repository.admin')
550 def edit_statistics_form(self, repo_name):
406 def edit_statistics_form(self, repo_name):
551 """GET /repo_name/settings: Form to edit an existing item"""
407 """GET /repo_name/settings: Form to edit an existing item"""
552 c.repo_info = self._load_repo(repo_name)
408 c.repo_info = self._load_repo(repo_name)
553 repo = c.repo_info.scm_instance()
409 repo = c.repo_info.scm_instance()
554
410
555 if c.repo_info.stats:
411 if c.repo_info.stats:
556 # this is on what revision we ended up so we add +1 for count
412 # this is on what revision we ended up so we add +1 for count
557 last_rev = c.repo_info.stats.stat_on_revision + 1
413 last_rev = c.repo_info.stats.stat_on_revision + 1
558 else:
414 else:
559 last_rev = 0
415 last_rev = 0
560 c.stats_revision = last_rev
416 c.stats_revision = last_rev
561
417
562 c.repo_last_rev = repo.count()
418 c.repo_last_rev = repo.count()
563
419
564 if last_rev == 0 or c.repo_last_rev == 0:
420 if last_rev == 0 or c.repo_last_rev == 0:
565 c.stats_percentage = 0
421 c.stats_percentage = 0
566 else:
422 else:
567 c.stats_percentage = '%.2f' % ((float((last_rev)) / c.repo_last_rev) * 100)
423 c.stats_percentage = '%.2f' % ((float((last_rev)) / c.repo_last_rev) * 100)
568
424
569 c.active = 'statistics'
425 c.active = 'statistics'
570
426
571 return render('admin/repos/repo_edit.mako')
427 return render('admin/repos/repo_edit.mako')
572
428
573 @HasRepoPermissionAllDecorator('repository.admin')
429 @HasRepoPermissionAllDecorator('repository.admin')
574 @auth.CSRFRequired()
430 @auth.CSRFRequired()
575 def repo_issuetracker_test(self, repo_name):
431 def repo_issuetracker_test(self, repo_name):
576 if request.is_xhr:
432 if request.is_xhr:
577 return h.urlify_commit_message(
433 return h.urlify_commit_message(
578 request.POST.get('test_text', ''),
434 request.POST.get('test_text', ''),
579 repo_name)
435 repo_name)
580 else:
436 else:
581 raise HTTPBadRequest()
437 raise HTTPBadRequest()
582
438
583 @HasRepoPermissionAllDecorator('repository.admin')
439 @HasRepoPermissionAllDecorator('repository.admin')
584 @auth.CSRFRequired()
440 @auth.CSRFRequired()
585 def repo_issuetracker_delete(self, repo_name):
441 def repo_issuetracker_delete(self, repo_name):
586 uid = request.POST.get('uid')
442 uid = request.POST.get('uid')
587 repo_settings = IssueTrackerSettingsModel(repo=repo_name)
443 repo_settings = IssueTrackerSettingsModel(repo=repo_name)
588 try:
444 try:
589 repo_settings.delete_entries(uid)
445 repo_settings.delete_entries(uid)
590 except Exception:
446 except Exception:
591 h.flash(_('Error occurred during deleting issue tracker entry'),
447 h.flash(_('Error occurred during deleting issue tracker entry'),
592 category='error')
448 category='error')
593 else:
449 else:
594 h.flash(_('Removed issue tracker entry'), category='success')
450 h.flash(_('Removed issue tracker entry'), category='success')
595 return redirect(url('repo_settings_issuetracker',
451 return redirect(url('repo_settings_issuetracker',
596 repo_name=repo_name))
452 repo_name=repo_name))
597
453
598 def _update_patterns(self, form, repo_settings):
454 def _update_patterns(self, form, repo_settings):
599 for uid in form['delete_patterns']:
455 for uid in form['delete_patterns']:
600 repo_settings.delete_entries(uid)
456 repo_settings.delete_entries(uid)
601
457
602 for pattern in form['patterns']:
458 for pattern in form['patterns']:
603 for setting, value, type_ in pattern:
459 for setting, value, type_ in pattern:
604 sett = repo_settings.create_or_update_setting(
460 sett = repo_settings.create_or_update_setting(
605 setting, value, type_)
461 setting, value, type_)
606 Session().add(sett)
462 Session().add(sett)
607
463
608 Session().commit()
464 Session().commit()
609
465
610 @HasRepoPermissionAllDecorator('repository.admin')
466 @HasRepoPermissionAllDecorator('repository.admin')
611 @auth.CSRFRequired()
467 @auth.CSRFRequired()
612 def repo_issuetracker_save(self, repo_name):
468 def repo_issuetracker_save(self, repo_name):
613 # Save inheritance
469 # Save inheritance
614 repo_settings = IssueTrackerSettingsModel(repo=repo_name)
470 repo_settings = IssueTrackerSettingsModel(repo=repo_name)
615 inherited = (request.POST.get('inherit_global_issuetracker')
471 inherited = (request.POST.get('inherit_global_issuetracker')
616 == "inherited")
472 == "inherited")
617 repo_settings.inherit_global_settings = inherited
473 repo_settings.inherit_global_settings = inherited
618 Session().commit()
474 Session().commit()
619
475
620 form = IssueTrackerPatternsForm()().to_python(request.POST)
476 form = IssueTrackerPatternsForm()().to_python(request.POST)
621 if form:
477 if form:
622 self._update_patterns(form, repo_settings)
478 self._update_patterns(form, repo_settings)
623
479
624 h.flash(_('Updated issue tracker entries'), category='success')
480 h.flash(_('Updated issue tracker entries'), category='success')
625 return redirect(url('repo_settings_issuetracker',
481 return redirect(url('repo_settings_issuetracker',
626 repo_name=repo_name))
482 repo_name=repo_name))
627
483
628 @HasRepoPermissionAllDecorator('repository.admin')
484 @HasRepoPermissionAllDecorator('repository.admin')
629 def repo_issuetracker(self, repo_name):
485 def repo_issuetracker(self, repo_name):
630 """GET /admin/settings/issue-tracker: All items in the collection"""
486 """GET /admin/settings/issue-tracker: All items in the collection"""
631 c.active = 'issuetracker'
487 c.active = 'issuetracker'
632 c.data = 'data'
488 c.data = 'data'
633 c.repo_info = self._load_repo(repo_name)
489 c.repo_info = self._load_repo(repo_name)
634
490
635 repo = Repository.get_by_repo_name(repo_name)
491 repo = Repository.get_by_repo_name(repo_name)
636 c.settings_model = IssueTrackerSettingsModel(repo=repo)
492 c.settings_model = IssueTrackerSettingsModel(repo=repo)
637 c.global_patterns = c.settings_model.get_global_settings()
493 c.global_patterns = c.settings_model.get_global_settings()
638 c.repo_patterns = c.settings_model.get_repo_settings()
494 c.repo_patterns = c.settings_model.get_repo_settings()
639
495
640 return render('admin/repos/repo_edit.mako')
496 return render('admin/repos/repo_edit.mako')
641
497
642 @HasRepoPermissionAllDecorator('repository.admin')
498 @HasRepoPermissionAllDecorator('repository.admin')
643 def repo_settings_vcs(self, repo_name):
499 def repo_settings_vcs(self, repo_name):
644 """GET /{repo_name}/settings/vcs/: All items in the collection"""
500 """GET /{repo_name}/settings/vcs/: All items in the collection"""
645
501
646 model = VcsSettingsModel(repo=repo_name)
502 model = VcsSettingsModel(repo=repo_name)
647
503
648 c.active = 'vcs'
504 c.active = 'vcs'
649 c.global_svn_branch_patterns = model.get_global_svn_branch_patterns()
505 c.global_svn_branch_patterns = model.get_global_svn_branch_patterns()
650 c.global_svn_tag_patterns = model.get_global_svn_tag_patterns()
506 c.global_svn_tag_patterns = model.get_global_svn_tag_patterns()
651 c.svn_branch_patterns = model.get_repo_svn_branch_patterns()
507 c.svn_branch_patterns = model.get_repo_svn_branch_patterns()
652 c.svn_tag_patterns = model.get_repo_svn_tag_patterns()
508 c.svn_tag_patterns = model.get_repo_svn_tag_patterns()
653 c.repo_info = self._load_repo(repo_name)
509 c.repo_info = self._load_repo(repo_name)
654 defaults = self._vcs_form_defaults(repo_name)
510 defaults = self._vcs_form_defaults(repo_name)
655 c.inherit_global_settings = defaults['inherit_global_settings']
511 c.inherit_global_settings = defaults['inherit_global_settings']
656 c.labs_active = str2bool(
512 c.labs_active = str2bool(
657 rhodecode.CONFIG.get('labs_settings_active', 'true'))
513 rhodecode.CONFIG.get('labs_settings_active', 'true'))
658
514
659 return htmlfill.render(
515 return htmlfill.render(
660 render('admin/repos/repo_edit.mako'),
516 render('admin/repos/repo_edit.mako'),
661 defaults=defaults,
517 defaults=defaults,
662 encoding="UTF-8",
518 encoding="UTF-8",
663 force_defaults=False)
519 force_defaults=False)
664
520
665 @HasRepoPermissionAllDecorator('repository.admin')
521 @HasRepoPermissionAllDecorator('repository.admin')
666 @auth.CSRFRequired()
522 @auth.CSRFRequired()
667 def repo_settings_vcs_update(self, repo_name):
523 def repo_settings_vcs_update(self, repo_name):
668 """POST /{repo_name}/settings/vcs/: All items in the collection"""
524 """POST /{repo_name}/settings/vcs/: All items in the collection"""
669 c.active = 'vcs'
525 c.active = 'vcs'
670
526
671 model = VcsSettingsModel(repo=repo_name)
527 model = VcsSettingsModel(repo=repo_name)
672 c.global_svn_branch_patterns = model.get_global_svn_branch_patterns()
528 c.global_svn_branch_patterns = model.get_global_svn_branch_patterns()
673 c.global_svn_tag_patterns = model.get_global_svn_tag_patterns()
529 c.global_svn_tag_patterns = model.get_global_svn_tag_patterns()
674 c.svn_branch_patterns = model.get_repo_svn_branch_patterns()
530 c.svn_branch_patterns = model.get_repo_svn_branch_patterns()
675 c.svn_tag_patterns = model.get_repo_svn_tag_patterns()
531 c.svn_tag_patterns = model.get_repo_svn_tag_patterns()
676 c.repo_info = self._load_repo(repo_name)
532 c.repo_info = self._load_repo(repo_name)
677 defaults = self._vcs_form_defaults(repo_name)
533 defaults = self._vcs_form_defaults(repo_name)
678 c.inherit_global_settings = defaults['inherit_global_settings']
534 c.inherit_global_settings = defaults['inherit_global_settings']
679
535
680 application_form = RepoVcsSettingsForm(repo_name)()
536 application_form = RepoVcsSettingsForm(repo_name)()
681 try:
537 try:
682 form_result = application_form.to_python(dict(request.POST))
538 form_result = application_form.to_python(dict(request.POST))
683 except formencode.Invalid as errors:
539 except formencode.Invalid as errors:
684 h.flash(
540 h.flash(
685 _("Some form inputs contain invalid data."),
541 _("Some form inputs contain invalid data."),
686 category='error')
542 category='error')
687 return htmlfill.render(
543 return htmlfill.render(
688 render('admin/repos/repo_edit.mako'),
544 render('admin/repos/repo_edit.mako'),
689 defaults=errors.value,
545 defaults=errors.value,
690 errors=errors.error_dict or {},
546 errors=errors.error_dict or {},
691 prefix_error=False,
547 prefix_error=False,
692 encoding="UTF-8",
548 encoding="UTF-8",
693 force_defaults=False
549 force_defaults=False
694 )
550 )
695
551
696 try:
552 try:
697 inherit_global_settings = form_result['inherit_global_settings']
553 inherit_global_settings = form_result['inherit_global_settings']
698 model.create_or_update_repo_settings(
554 model.create_or_update_repo_settings(
699 form_result, inherit_global_settings=inherit_global_settings)
555 form_result, inherit_global_settings=inherit_global_settings)
700 except Exception:
556 except Exception:
701 log.exception("Exception while updating settings")
557 log.exception("Exception while updating settings")
702 h.flash(
558 h.flash(
703 _('Error occurred during updating repository VCS settings'),
559 _('Error occurred during updating repository VCS settings'),
704 category='error')
560 category='error')
705 else:
561 else:
706 Session().commit()
562 Session().commit()
707 h.flash(_('Updated VCS settings'), category='success')
563 h.flash(_('Updated VCS settings'), category='success')
708 return redirect(url('repo_vcs_settings', repo_name=repo_name))
564 return redirect(url('repo_vcs_settings', repo_name=repo_name))
709
565
710 return htmlfill.render(
566 return htmlfill.render(
711 render('admin/repos/repo_edit.mako'),
567 render('admin/repos/repo_edit.mako'),
712 defaults=self._vcs_form_defaults(repo_name),
568 defaults=self._vcs_form_defaults(repo_name),
713 encoding="UTF-8",
569 encoding="UTF-8",
714 force_defaults=False)
570 force_defaults=False)
715
571
716 @HasRepoPermissionAllDecorator('repository.admin')
572 @HasRepoPermissionAllDecorator('repository.admin')
717 @auth.CSRFRequired()
573 @auth.CSRFRequired()
718 @jsonify
574 @jsonify
719 def repo_delete_svn_pattern(self, repo_name):
575 def repo_delete_svn_pattern(self, repo_name):
720 if not request.is_xhr:
576 if not request.is_xhr:
721 return False
577 return False
722
578
723 delete_pattern_id = request.POST.get('delete_svn_pattern')
579 delete_pattern_id = request.POST.get('delete_svn_pattern')
724 model = VcsSettingsModel(repo=repo_name)
580 model = VcsSettingsModel(repo=repo_name)
725 try:
581 try:
726 model.delete_repo_svn_pattern(delete_pattern_id)
582 model.delete_repo_svn_pattern(delete_pattern_id)
727 except SettingNotFound:
583 except SettingNotFound:
728 raise HTTPBadRequest()
584 raise HTTPBadRequest()
729
585
730 Session().commit()
586 Session().commit()
731 return True
587 return True
732
588
733 def _vcs_form_defaults(self, repo_name):
589 def _vcs_form_defaults(self, repo_name):
734 model = VcsSettingsModel(repo=repo_name)
590 model = VcsSettingsModel(repo=repo_name)
735 global_defaults = model.get_global_settings()
591 global_defaults = model.get_global_settings()
736
592
737 repo_defaults = {}
593 repo_defaults = {}
738 repo_defaults.update(global_defaults)
594 repo_defaults.update(global_defaults)
739 repo_defaults.update(model.get_repo_settings())
595 repo_defaults.update(model.get_repo_settings())
740
596
741 global_defaults = {
597 global_defaults = {
742 '{}_inherited'.format(k): global_defaults[k]
598 '{}_inherited'.format(k): global_defaults[k]
743 for k in global_defaults}
599 for k in global_defaults}
744
600
745 defaults = {
601 defaults = {
746 'inherit_global_settings': model.inherit_global_settings
602 'inherit_global_settings': model.inherit_global_settings
747 }
603 }
748 defaults.update(global_defaults)
604 defaults.update(global_defaults)
749 defaults.update(repo_defaults)
605 defaults.update(repo_defaults)
750 defaults.update({
606 defaults.update({
751 'new_svn_branch': '',
607 'new_svn_branch': '',
752 'new_svn_tag': '',
608 'new_svn_tag': '',
753 })
609 })
754 return defaults
610 return defaults
@@ -1,183 +1,184 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, Repository
25 from rhodecode.model.db import User, UserLog, Repository
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 'user.push': {},
36 'user.push': {},
37 'user.pull': {},
37 'user.pull': {},
38
38
39 'repo.add': {},
39 'repo.create': {},
40 'repo.edit': {},
40 'repo.edit': {},
41 'repo.edit.permissions': {},
41 'repo.edit.permissions': {},
42 'repo.delete': {},
42 'repo.commit.strip': {},
43 'repo.commit.strip': {},
43 'repo.archive.download': {},
44 'repo.archive.download': {},
44 }
45 }
45
46
46
47
47 class UserWrap(object):
48 class UserWrap(object):
48 """
49 """
49 Fake object used to imitate AuthUser
50 Fake object used to imitate AuthUser
50 """
51 """
51
52
52 def __init__(self, user_id=None, username=None, ip_addr=None):
53 def __init__(self, user_id=None, username=None, ip_addr=None):
53 self.user_id = user_id
54 self.user_id = user_id
54 self.username = username
55 self.username = username
55 self.ip_addr = ip_addr
56 self.ip_addr = ip_addr
56
57
57
58
58 class RepoWrap(object):
59 class RepoWrap(object):
59 """
60 """
60 Fake object used to imitate RepoObject that audit logger requires
61 Fake object used to imitate RepoObject that audit logger requires
61 """
62 """
62
63
63 def __init__(self, repo_id=None, repo_name=None):
64 def __init__(self, repo_id=None, repo_name=None):
64 self.repo_id = repo_id
65 self.repo_id = repo_id
65 self.repo_name = repo_name
66 self.repo_name = repo_name
66
67
67
68
68 def _store_log(action_name, action_data, user_id, username, user_data,
69 def _store_log(action_name, action_data, user_id, username, user_data,
69 ip_address, repository_id, repository_name):
70 ip_address, repository_id, repository_name):
70 user_log = UserLog()
71 user_log = UserLog()
71 user_log.version = UserLog.VERSION_2
72 user_log.version = UserLog.VERSION_2
72
73
73 user_log.action = action_name
74 user_log.action = action_name
74 user_log.action_data = action_data
75 user_log.action_data = action_data
75
76
76 user_log.user_ip = ip_address
77 user_log.user_ip = ip_address
77
78
78 user_log.user_id = user_id
79 user_log.user_id = user_id
79 user_log.username = username
80 user_log.username = username
80 user_log.user_data = user_data
81 user_log.user_data = user_data
81
82
82 user_log.repository_id = repository_id
83 user_log.repository_id = repository_id
83 user_log.repository_name = repository_name
84 user_log.repository_name = repository_name
84
85
85 user_log.action_date = datetime.datetime.now()
86 user_log.action_date = datetime.datetime.now()
86
87
87 log.info('AUDIT: Logging action: `%s` by user:id:%s[%s] ip:%s',
88 log.info('AUDIT: Logging action: `%s` by user:id:%s[%s] ip:%s',
88 action_name, user_id, username, ip_address)
89 action_name, user_id, username, ip_address)
89
90
90 return user_log
91 return user_log
91
92
92
93
93 def store(
94 def store(
94 action, user, action_data=None, user_data=None, ip_addr=None,
95 action, user, action_data=None, user_data=None, ip_addr=None,
95 repo=None, sa_session=None, commit=False):
96 repo=None, sa_session=None, commit=False):
96 """
97 """
97 Audit logger for various actions made by users, typically this results in a call such::
98 Audit logger for various actions made by users, typically this results in a call such::
98
99
99 from rhodecode.lib import audit_logger
100 from rhodecode.lib import audit_logger
100
101
101 audit_logger.store(
102 audit_logger.store(
102 action='repo.edit', user=self._rhodecode_user)
103 action='repo.edit', user=self._rhodecode_user)
103 audit_logger.store(
104 audit_logger.store(
104 action='repo.delete',
105 action='repo.delete', action_data={'repo_data': repo_data},
105 user=audit_logger.UserWrap(username='itried-login', ip_addr='8.8.8.8'))
106 user=audit_logger.UserWrap(username='itried-login', ip_addr='8.8.8.8'))
106
107
107 # repo action
108 # repo action
108 audit_logger.store(
109 audit_logger.store(
109 action='repo.delete',
110 action='repo.delete',
110 user=audit_logger.UserWrap(username='itried-login', ip_addr='8.8.8.8'),
111 user=audit_logger.UserWrap(username='itried-login', ip_addr='8.8.8.8'),
111 repo=audit_logger.RepoWrap(repo_name='some-repo'))
112 repo=audit_logger.RepoWrap(repo_name='some-repo'))
112
113
113 # repo action, when we know and have the repository object already
114 # repo action, when we know and have the repository object already
114 audit_logger.store(
115 audit_logger.store(
115 action='repo.delete',
116 action='repo.delete',
116 user=audit_logger.UserWrap(username='itried-login', ip_addr='8.8.8.8'),
117 user=audit_logger.UserWrap(username='itried-login', ip_addr='8.8.8.8'),
117 repo=repo_object)
118 repo=repo_object)
118
119
119 # without an user ?
120 # without an user ?
120 audit_logger.store(
121 audit_logger.store(
121 action='user.login.failure',
122 action='user.login.failure',
122 user=audit_logger.UserWrap(
123 user=audit_logger.UserWrap(
123 username=self.request.params.get('username'),
124 username=self.request.params.get('username'),
124 ip_addr=self.request.remote_addr))
125 ip_addr=self.request.remote_addr))
125
126
126 """
127 """
127 from rhodecode.lib.utils2 import safe_unicode
128 from rhodecode.lib.utils2 import safe_unicode
128 from rhodecode.lib.auth import AuthUser
129 from rhodecode.lib.auth import AuthUser
129
130
130 if action not in ACTIONS:
131 if action not in ACTIONS:
131 raise ValueError('Action `{}` not in valid actions'.format(action))
132 raise ValueError('Action `{}` not in valid actions'.format(action))
132
133
133 if not sa_session:
134 if not sa_session:
134 sa_session = meta.Session()
135 sa_session = meta.Session()
135
136
136 try:
137 try:
137 username = getattr(user, 'username', None)
138 username = getattr(user, 'username', None)
138 if not username:
139 if not username:
139 pass
140 pass
140
141
141 user_id = getattr(user, 'user_id', None)
142 user_id = getattr(user, 'user_id', None)
142 if not user_id:
143 if not user_id:
143 # maybe we have username ? Try to figure user_id from username
144 # maybe we have username ? Try to figure user_id from username
144 if username:
145 if username:
145 user_id = getattr(
146 user_id = getattr(
146 User.get_by_username(username), 'user_id', None)
147 User.get_by_username(username), 'user_id', None)
147
148
148 ip_addr = ip_addr or getattr(user, 'ip_addr', None)
149 ip_addr = ip_addr or getattr(user, 'ip_addr', None)
149 if not ip_addr:
150 if not ip_addr:
150 pass
151 pass
151
152
152 if not user_data:
153 if not user_data:
153 # try to get this from the auth user
154 # try to get this from the auth user
154 if isinstance(user, AuthUser):
155 if isinstance(user, AuthUser):
155 user_data = {
156 user_data = {
156 'username': user.username,
157 'username': user.username,
157 'email': user.email,
158 'email': user.email,
158 }
159 }
159
160
160 repository_name = getattr(repo, 'repo_name', None)
161 repository_name = getattr(repo, 'repo_name', None)
161 repository_id = getattr(repo, 'repo_id', None)
162 repository_id = getattr(repo, 'repo_id', None)
162 if not repository_id:
163 if not repository_id:
163 # maybe we have repo_name ? Try to figure repo_id from repo_name
164 # maybe we have repo_name ? Try to figure repo_id from repo_name
164 if repository_name:
165 if repository_name:
165 repository_id = getattr(
166 repository_id = getattr(
166 Repository.get_by_repo_name(repository_name), 'repo_id', None)
167 Repository.get_by_repo_name(repository_name), 'repo_id', None)
167
168
168 user_log = _store_log(
169 user_log = _store_log(
169 action_name=safe_unicode(action),
170 action_name=safe_unicode(action),
170 action_data=action_data or {},
171 action_data=action_data or {},
171 user_id=user_id,
172 user_id=user_id,
172 username=username,
173 username=username,
173 user_data=user_data or {},
174 user_data=user_data or {},
174 ip_address=safe_unicode(ip_addr),
175 ip_address=safe_unicode(ip_addr),
175 repository_id=repository_id,
176 repository_id=repository_id,
176 repository_name=repository_name
177 repository_name=repository_name
177 )
178 )
178 sa_session.add(user_log)
179 sa_session.add(user_log)
179 if commit:
180 if commit:
180 sa_session.commit()
181 sa_session.commit()
181
182
182 except Exception:
183 except Exception:
183 log.exception('AUDIT: failed to store audit log')
184 log.exception('AUDIT: failed to store audit log')
@@ -1,121 +1,127 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('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']);
28 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']);
29 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']);
30 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']);
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']);
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']);
32 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']);
33 pyroutes.register('pullrequest', '/%(repo_name)s/pull-request/new', ['repo_name']);
33 pyroutes.register('pullrequest', '/%(repo_name)s/pull-request/new', ['repo_name']);
34 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']);
35 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']);
36 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']);
37 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']);
38 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']);
39 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']);
40 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']);
41 pyroutes.register('changelog_home', '/%(repo_name)s/changelog', ['repo_name']);
41 pyroutes.register('changelog_home', '/%(repo_name)s/changelog', ['repo_name']);
42 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']);
43 pyroutes.register('changelog_elements', '/%(repo_name)s/changelog_details', ['repo_name']);
43 pyroutes.register('changelog_elements', '/%(repo_name)s/changelog_details', ['repo_name']);
44 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']);
45 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']);
46 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']);
47 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']);
48 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']);
49 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']);
50 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']);
51 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']);
52 pyroutes.register('summary_home_slash', '/%(repo_name)s/', ['repo_name']);
52 pyroutes.register('summary_home_slash', '/%(repo_name)s/', ['repo_name']);
53 pyroutes.register('summary_home', '/%(repo_name)s', ['repo_name']);
53 pyroutes.register('summary_home', '/%(repo_name)s', ['repo_name']);
54 pyroutes.register('favicon', '/favicon.ico', []);
54 pyroutes.register('favicon', '/favicon.ico', []);
55 pyroutes.register('robots', '/robots.txt', []);
55 pyroutes.register('robots', '/robots.txt', []);
56 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
56 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
57 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
57 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
58 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
58 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
59 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
59 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
60 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
60 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
61 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']);
62 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']);
63 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']);
64 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']);
65 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']);
66 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']);
67 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']);
68 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']);
69 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']);
70 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']);
71 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']);
72 pyroutes.register('ops_ping', '_admin/ops/ping', []);
72 pyroutes.register('ops_ping', '_admin/ops/ping', []);
73 pyroutes.register('admin_settings_open_source', '_admin/settings/open_source', []);
73 pyroutes.register('admin_settings_open_source', '_admin/settings/open_source', []);
74 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', []);
75 pyroutes.register('admin_settings_system', '_admin/settings/system', []);
75 pyroutes.register('admin_settings_system', '_admin/settings/system', []);
76 pyroutes.register('admin_settings_system_update', '_admin/settings/system/updates', []);
76 pyroutes.register('admin_settings_system_update', '_admin/settings/system/updates', []);
77 pyroutes.register('admin_settings_sessions', '_admin/settings/sessions', []);
77 pyroutes.register('admin_settings_sessions', '_admin/settings/sessions', []);
78 pyroutes.register('admin_settings_sessions_cleanup', '_admin/settings/sessions/cleanup', []);
78 pyroutes.register('admin_settings_sessions_cleanup', '_admin/settings/sessions/cleanup', []);
79 pyroutes.register('users', '_admin/users', []);
79 pyroutes.register('users', '_admin/users', []);
80 pyroutes.register('users_data', '_admin/users_data', []);
80 pyroutes.register('users_data', '_admin/users_data', []);
81 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']);
82 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']);
83 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']);
84 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']);
85 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']);
86 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']);
87 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
87 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
88 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
88 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
89 pyroutes.register('channelstream_proxy', '/_channelstream', []);
89 pyroutes.register('channelstream_proxy', '/_channelstream', []);
90 pyroutes.register('login', '/_admin/login', []);
90 pyroutes.register('login', '/_admin/login', []);
91 pyroutes.register('logout', '/_admin/logout', []);
91 pyroutes.register('logout', '/_admin/logout', []);
92 pyroutes.register('register', '/_admin/register', []);
92 pyroutes.register('register', '/_admin/register', []);
93 pyroutes.register('reset_password', '/_admin/password_reset', []);
93 pyroutes.register('reset_password', '/_admin/password_reset', []);
94 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
94 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
95 pyroutes.register('user_autocomplete_data', '/_users', []);
95 pyroutes.register('user_autocomplete_data', '/_users', []);
96 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
96 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
97 pyroutes.register('repo_list_data', '/_repos', []);
97 pyroutes.register('repo_list_data', '/_repos', []);
98 pyroutes.register('goto_switcher_data', '/_goto_data', []);
98 pyroutes.register('goto_switcher_data', '/_goto_data', []);
99 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
99 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
100 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
100 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
101 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
101 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
102 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
102 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
103 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
104 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
105 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
106 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
107 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
108 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
103 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
109 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
104 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
110 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
105 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
111 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
106 pyroutes.register('repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
112 pyroutes.register('repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
107 pyroutes.register('repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
113 pyroutes.register('repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
108 pyroutes.register('strip', '/%(repo_name)s/settings/strip', ['repo_name']);
114 pyroutes.register('strip', '/%(repo_name)s/settings/strip', ['repo_name']);
109 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
115 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
110 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
116 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
111 pyroutes.register('search', '/_admin/search', []);
117 pyroutes.register('search', '/_admin/search', []);
112 pyroutes.register('search_repo', '/%(repo_name)s/search', ['repo_name']);
118 pyroutes.register('search_repo', '/%(repo_name)s/search', ['repo_name']);
113 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
119 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
114 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
120 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
115 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
121 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
116 pyroutes.register('my_account_password_update', '/_admin/my_account/password', []);
122 pyroutes.register('my_account_password_update', '/_admin/my_account/password', []);
117 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
123 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
118 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
124 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
119 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
125 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
120 pyroutes.register('apiv2', '/_admin/api', []);
126 pyroutes.register('apiv2', '/_admin/api', []);
121 }
127 }
@@ -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.route_path('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.route_path('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,211 +1,210 b''
1 <%namespace name="base" file="/base/base.mako"/>
1 <%namespace name="base" file="/base/base.mako"/>
2
2
3 <%
3 <%
4 elems = [
4 elems = [
5 (_('Owner'), lambda:base.gravatar_with_user(c.repo_info.user.email), '', ''),
5 (_('Owner'), lambda:base.gravatar_with_user(c.repo_info.user.email), '', ''),
6 (_('Created on'), h.format_date(c.repo_info.created_on), '', ''),
6 (_('Created on'), h.format_date(c.repo_info.created_on), '', ''),
7 (_('Updated on'), h.format_date(c.repo_info.updated_on), '', ''),
7 (_('Updated on'), h.format_date(c.repo_info.updated_on), '', ''),
8 (_('Cached Commit id'), lambda: h.link_to(c.repo_info.changeset_cache.get('short_id'), h.url('changeset_home',repo_name=c.repo_name,revision=c.repo_info.changeset_cache.get('raw_id'))), '', ''),
8 (_('Cached Commit id'), lambda: h.link_to(c.repo_info.changeset_cache.get('short_id'), h.url('changeset_home',repo_name=c.repo_name,revision=c.repo_info.changeset_cache.get('raw_id'))), '', ''),
9 ]
9 ]
10 %>
10 %>
11
11
12 <div class="panel panel-default">
12 <div class="panel panel-default">
13 <div class="panel-heading">
13 <div class="panel-heading" id="advanced-info" >
14 <h3 class="panel-title">${_('Repository: %s') % c.repo_info.repo_name}</h3>
14 <h3 class="panel-title">${_('Repository: %s') % c.repo_info.repo_name} <a class="permalink" href="#advanced-info"> ΒΆ</a></h3>
15 </div>
15 </div>
16 <div class="panel-body">
16 <div class="panel-body">
17 ${base.dt_info_panel(elems)}
17 ${base.dt_info_panel(elems)}
18 </div>
18 </div>
19 </div>
19 </div>
20
20
21
21
22 <div class="panel panel-default">
22 <div class="panel panel-default">
23 <div class="panel-heading">
23 <div class="panel-heading" id="advanced-fork">
24 <h3 class="panel-title">${_('Fork Reference')}</h3>
24 <h3 class="panel-title">${_('Fork Reference')} <a class="permalink" href="#advanced-fork"> ΒΆ</a></h3>
25 </div>
25 </div>
26 <div class="panel-body">
26 <div class="panel-body">
27 ${h.secure_form(url('edit_repo_advanced_fork', repo_name=c.repo_info.repo_name), method='put')}
27 ${h.secure_form(h.route_path('edit_repo_advanced_fork', repo_name=c.repo_info.repo_name), method='POST')}
28
28
29 % if c.repo_info.fork:
29 % if c.repo_info.fork:
30 <div class="panel-body-title-text">${h.literal(_('This repository is a fork of %(repo_link)s') % {'repo_link': h.link_to_if(c.has_origin_repo_read_perm,c.repo_info.fork.repo_name, h.url('summary_home', repo_name=c.repo_info.fork.repo_name))})}
30 <div class="panel-body-title-text">${h.literal(_('This repository is a fork of %(repo_link)s') % {'repo_link': h.link_to_if(c.has_origin_repo_read_perm,c.repo_info.fork.repo_name, h.url('summary_home', repo_name=c.repo_info.fork.repo_name))})}
31 | <button class="btn btn-link btn-danger" type="submit">Remove fork reference</button></div>
31 | <button class="btn btn-link btn-danger" type="submit">Remove fork reference</button></div>
32 % endif
32 % endif
33
33
34 <div class="field">
34 <div class="field">
35 ${h.hidden('id_fork_of')}
35 ${h.hidden('id_fork_of')}
36 ${h.submit('set_as_fork_%s' % c.repo_info.repo_name,_('Set'),class_="btn btn-small",)}
36 ${h.submit('set_as_fork_%s' % c.repo_info.repo_name,_('Set'),class_="btn btn-small",)}
37 </div>
37 </div>
38 <div class="field">
38 <div class="field">
39 <span class="help-block">${_('Manually set this repository as a fork of another from the list')}</span>
39 <span class="help-block">${_('Manually set this repository as a fork of another from the list')}</span>
40 </div>
40 </div>
41 ${h.end_form()}
41 ${h.end_form()}
42 </div>
42 </div>
43 </div>
43 </div>
44
44
45
45
46 <div class="panel panel-default">
46 <div class="panel panel-default">
47 <div class="panel-heading">
47 <div class="panel-heading" id="advanced-journal">
48 <h3 class="panel-title">${_('Public Journal Visibility')}</h3>
48 <h3 class="panel-title">${_('Public Journal Visibility')} <a class="permalink" href="#advanced-journal"> ΒΆ</a></h3>
49 </div>
49 </div>
50 <div class="panel-body">
50 <div class="panel-body">
51 ${h.secure_form(url('edit_repo_advanced_journal', repo_name=c.repo_info.repo_name), method='put')}
51 ${h.secure_form(h.route_path('edit_repo_advanced_journal', repo_name=c.repo_info.repo_name), method='POST')}
52 <div class="field">
52 <div class="field">
53 %if c.in_public_journal:
53 %if c.in_public_journal:
54 <button class="btn btn-small" type="submit">
54 <button class="btn btn-small" type="submit">
55 <i class="icon-minus"></i>
56 ${_('Remove from Public Journal')}
55 ${_('Remove from Public Journal')}
57 </button>
56 </button>
58 %else:
57 %else:
59 <button class="btn btn-small" type="submit">
58 <button class="btn btn-small" type="submit">
60 ${_('Add to Public Journal')}
59 ${_('Add to Public Journal')}
61 </button>
60 </button>
62 %endif
61 %endif
63 </div>
62 </div>
64 <div class="field" >
63 <div class="field" >
65 <span class="help-block">${_('All actions made on this repository will be visible to everyone following the public journal.')}</span>
64 <span class="help-block">${_('All actions made on this repository will be visible to everyone following the public journal.')}</span>
66 </div>
65 </div>
67 ${h.end_form()}
66 ${h.end_form()}
68 </div>
67 </div>
69 </div>
68 </div>
70
69
71
70
72 <div class="panel panel-default">
71 <div class="panel panel-default">
73 <div class="panel-heading">
72 <div class="panel-heading" id="advanced-locking">
74 <h3 class="panel-title">${_('Locking state')}</h3>
73 <h3 class="panel-title">${_('Locking state')} <a class="permalink" href="#advanced-locking"> ΒΆ</a></h3>
75 </div>
74 </div>
76 <div class="panel-body">
75 <div class="panel-body">
77 ${h.secure_form(url('edit_repo_advanced_locking', repo_name=c.repo_info.repo_name), method='put')}
76 ${h.secure_form(h.route_path('edit_repo_advanced_locking', repo_name=c.repo_info.repo_name), method='POST')}
78
77
79 %if c.repo_info.locked[0]:
78 %if c.repo_info.locked[0]:
80 <div class="panel-body-title-text">${'Locked by %s on %s. Lock reason: %s' % (h.person_by_id(c.repo_info.locked[0]),
79 <div class="panel-body-title-text">${'Locked by %s on %s. Lock reason: %s' % (h.person_by_id(c.repo_info.locked[0]),
81 h.format_date(h. time_to_datetime(c.repo_info.locked[1])), c.repo_info.locked[2])}</div>
80 h.format_date(h. time_to_datetime(c.repo_info.locked[1])), c.repo_info.locked[2])}</div>
82 %else:
81 %else:
83 <div class="panel-body-title-text">${_('This Repository is not currently locked.')}</div>
82 <div class="panel-body-title-text">${_('This Repository is not currently locked.')}</div>
84 %endif
83 %endif
85
84
86 <div class="field" >
85 <div class="field" >
87 %if c.repo_info.locked[0]:
86 %if c.repo_info.locked[0]:
88 ${h.hidden('set_unlock', '1')}
87 ${h.hidden('set_unlock', '1')}
89 <button class="btn btn-small" type="submit"
88 <button class="btn btn-small" type="submit"
90 onclick="return confirm('${_('Confirm to unlock repository.')}');">
89 onclick="return confirm('${_('Confirm to unlock repository.')}');">
91 <i class="icon-unlock"></i>
90 <i class="icon-unlock"></i>
92 ${_('Unlock repository')}
91 ${_('Unlock repository')}
93 </button>
92 </button>
94 %else:
93 %else:
95 ${h.hidden('set_lock', '1')}
94 ${h.hidden('set_lock', '1')}
96 <button class="btn btn-small" type="submit"
95 <button class="btn btn-small" type="submit"
97 onclick="return confirm('${_('Confirm to lock repository.')}');">
96 onclick="return confirm('${_('Confirm to lock repository.')}');">
98 <i class="icon-lock"></i>
97 <i class="icon-lock"></i>
99 ${_('Lock Repository')}
98 ${_('Lock Repository')}
100 </button>
99 </button>
101 %endif
100 %endif
102 </div>
101 </div>
103 <div class="field" >
102 <div class="field" >
104 <span class="help-block">
103 <span class="help-block">
105 ${_('Force repository locking. This only works when anonymous access is disabled. Pulling from the repository locks the repository to that user until the same user pushes to that repository again.')}
104 ${_('Force repository locking. This only works when anonymous access is disabled. Pulling from the repository locks the repository to that user until the same user pushes to that repository again.')}
106 </span>
105 </span>
107 </div>
106 </div>
108 ${h.end_form()}
107 ${h.end_form()}
109 </div>
108 </div>
110 </div>
109 </div>
111
110
112 <div class="panel panel-danger">
111 <div class="panel panel-danger">
113 <div class="panel-heading">
112 <div class="panel-heading" id="advanced-delete">
114 <h3 class="panel-title">${_('Delete repository')}</h3>
113 <h3 class="panel-title">${_('Delete repository')} <a class="permalink" href="#advanced-delete"> ΒΆ</a></h3>
115 </div>
114 </div>
116 <div class="panel-body">
115 <div class="panel-body">
117 ${h.secure_form(url('repo', repo_name=c.repo_name),method='delete')}
116 ${h.secure_form(h.route_path('edit_repo_advanced_delete', repo_name=c.repo_name), method='POST')}
118 <table class="display">
117 <table class="display">
119 <tr>
118 <tr>
120 <td>
119 <td>
121 ${ungettext('This repository has %s fork.', 'This repository has %s forks.', c.repo_info. forks.count()) % c.repo_info.forks.count()}
120 ${_ungettext('This repository has %s fork.', 'This repository has %s forks.', c.repo_info.forks.count()) % c.repo_info.forks.count()}
122 </td>
121 </td>
123 <td>
122 <td>
124 %if c.repo_info.forks.count():
123 %if c.repo_info.forks.count():
125 <input type="radio" name="forks" value="detach_forks" checked="checked"/> <label for="forks">${_('Detach forks')}</label>
124 <input type="radio" name="forks" value="detach_forks" checked="checked"/> <label for="forks">${_('Detach forks')}</label>
126 %endif
125 %endif
127 </td>
126 </td>
128 <td>
127 <td>
129 %if c.repo_info.forks.count():
128 %if c.repo_info.forks.count():
130 <input type="radio" name="forks" value="delete_forks"/> <label for="forks">${_('Delete forks')}</label>
129 <input type="radio" name="forks" value="delete_forks"/> <label for="forks">${_('Delete forks')}</label>
131 %endif
130 %endif
132 </td>
131 </td>
133 </tr>
132 </tr>
134 </table>
133 </table>
135 <div style="margin: 0 0 20px 0" class="fake-space"></div>
134 <div style="margin: 0 0 20px 0" class="fake-space"></div>
136
135
137 <div class="field">
136 <div class="field">
138 <button class="btn btn-small btn-danger" type="submit"
137 <button class="btn btn-small btn-danger" type="submit"
139 onclick="return confirm('${_('Confirm to delete this repository: %s') % c.repo_name}');">
138 onclick="return confirm('${_('Confirm to delete this repository: %s') % c.repo_name}');">
140 <i class="icon-remove-sign"></i>
139 <i class="icon-remove-sign"></i>
141 ${_('Delete This Repository')}
140 ${_('Delete This Repository')}
142 </button>
141 </button>
143 </div>
142 </div>
144 <div class="field">
143 <div class="field">
145 <span class="help-block">
144 <span class="help-block">
146 ${_('This repository will be renamed in a special way in order to make it inaccessible to RhodeCode Enterprise and its VCS systems. If you need to fully delete it from the file system, please do it manually, or with rhodecode-cleanup-repos command.')}
145 ${_('This repository will be renamed in a special way in order to make it inaccessible to RhodeCode Enterprise and its VCS systems. If you need to fully delete it from the file system, please do it manually, or with rhodecode-cleanup-repos command available in rhodecode-tools.')}
147 </span>
146 </span>
148 </div>
147 </div>
149
148
150 ${h.end_form()}
149 ${h.end_form()}
151 </div>
150 </div>
152 </div>
151 </div>
153
152
154
153
155 <script>
154 <script>
156
155
157 var currentRepoId = ${c.repo_info.repo_id};
156 var currentRepoId = ${c.repo_info.repo_id};
158
157
159 var repoTypeFilter = function(data) {
158 var repoTypeFilter = function(data) {
160 var results = [];
159 var results = [];
161
160
162 if (!data.results[0]) {
161 if (!data.results[0]) {
163 return data
162 return data
164 }
163 }
165
164
166 $.each(data.results[0].children, function() {
165 $.each(data.results[0].children, function() {
167 // filter out the SAME repo, it cannot be used as fork of itself
166 // filter out the SAME repo, it cannot be used as fork of itself
168 if (this.obj.repo_id != currentRepoId) {
167 if (this.obj.repo_id != currentRepoId) {
169 this.id = this.obj.repo_id;
168 this.id = this.obj.repo_id;
170 results.push(this)
169 results.push(this)
171 }
170 }
172 });
171 });
173 data.results[0].children = results;
172 data.results[0].children = results;
174 return data;
173 return data;
175 };
174 };
176
175
177 $("#id_fork_of").select2({
176 $("#id_fork_of").select2({
178 cachedDataSource: {},
177 cachedDataSource: {},
179 minimumInputLength: 2,
178 minimumInputLength: 2,
180 placeholder: "${_('Change repository') if c.repo_info.fork else _('Pick repository')}",
179 placeholder: "${_('Change repository') if c.repo_info.fork else _('Pick repository')}",
181 dropdownAutoWidth: true,
180 dropdownAutoWidth: true,
182 containerCssClass: "drop-menu",
181 containerCssClass: "drop-menu",
183 dropdownCssClass: "drop-menu-dropdown",
182 dropdownCssClass: "drop-menu-dropdown",
184 formatResult: formatResult,
183 formatResult: formatResult,
185 query: $.debounce(250, function(query){
184 query: $.debounce(250, function(query){
186 self = this;
185 self = this;
187 var cacheKey = query.term;
186 var cacheKey = query.term;
188 var cachedData = self.cachedDataSource[cacheKey];
187 var cachedData = self.cachedDataSource[cacheKey];
189
188
190 if (cachedData) {
189 if (cachedData) {
191 query.callback({results: cachedData.results});
190 query.callback({results: cachedData.results});
192 } else {
191 } else {
193 $.ajax({
192 $.ajax({
194 url: pyroutes.url('repo_list_data'),
193 url: pyroutes.url('repo_list_data'),
195 data: {'query': query.term, repo_type: '${c.repo_info.repo_type}'},
194 data: {'query': query.term, repo_type: '${c.repo_info.repo_type}'},
196 dataType: 'json',
195 dataType: 'json',
197 type: 'GET',
196 type: 'GET',
198 success: function(data) {
197 success: function(data) {
199 data = repoTypeFilter(data);
198 data = repoTypeFilter(data);
200 self.cachedDataSource[cacheKey] = data;
199 self.cachedDataSource[cacheKey] = data;
201 query.callback({results: data.results});
200 query.callback({results: data.results});
202 },
201 },
203 error: function(data, textStatus, errorThrown) {
202 error: function(data, textStatus, errorThrown) {
204 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
203 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
205 }
204 }
206 })
205 })
207 }
206 }
208 })
207 })
209 });
208 });
210 </script>
209 </script>
211
210
@@ -1,317 +1,317 b''
1 ## DATA TABLE RE USABLE ELEMENTS
1 ## DATA TABLE RE USABLE ELEMENTS
2 ## usage:
2 ## usage:
3 ## <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
3 ## <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
4 <%namespace name="base" file="/base/base.mako"/>
4 <%namespace name="base" file="/base/base.mako"/>
5
5
6 ## REPOSITORY RENDERERS
6 ## REPOSITORY RENDERERS
7 <%def name="quick_menu(repo_name)">
7 <%def name="quick_menu(repo_name)">
8 <i class="pointer icon-more"></i>
8 <i class="pointer icon-more"></i>
9 <div class="menu_items_container hidden">
9 <div class="menu_items_container hidden">
10 <ul class="menu_items">
10 <ul class="menu_items">
11 <li>
11 <li>
12 <a title="${_('Summary')}" href="${h.url('summary_home',repo_name=repo_name)}">
12 <a title="${_('Summary')}" href="${h.url('summary_home',repo_name=repo_name)}">
13 <span>${_('Summary')}</span>
13 <span>${_('Summary')}</span>
14 </a>
14 </a>
15 </li>
15 </li>
16 <li>
16 <li>
17 <a title="${_('Changelog')}" href="${h.url('changelog_home',repo_name=repo_name)}">
17 <a title="${_('Changelog')}" href="${h.url('changelog_home',repo_name=repo_name)}">
18 <span>${_('Changelog')}</span>
18 <span>${_('Changelog')}</span>
19 </a>
19 </a>
20 </li>
20 </li>
21 <li>
21 <li>
22 <a title="${_('Files')}" href="${h.url('files_home',repo_name=repo_name)}">
22 <a title="${_('Files')}" href="${h.url('files_home',repo_name=repo_name)}">
23 <span>${_('Files')}</span>
23 <span>${_('Files')}</span>
24 </a>
24 </a>
25 </li>
25 </li>
26 <li>
26 <li>
27 <a title="${_('Fork')}" href="${h.url('repo_fork_home',repo_name=repo_name)}">
27 <a title="${_('Fork')}" href="${h.url('repo_fork_home',repo_name=repo_name)}">
28 <span>${_('Fork')}</span>
28 <span>${_('Fork')}</span>
29 </a>
29 </a>
30 </li>
30 </li>
31 </ul>
31 </ul>
32 </div>
32 </div>
33 </%def>
33 </%def>
34
34
35 <%def name="repo_name(name,rtype,rstate,private,fork_of,short_name=False,admin=False)">
35 <%def name="repo_name(name,rtype,rstate,private,fork_of,short_name=False,admin=False)">
36 <%
36 <%
37 def get_name(name,short_name=short_name):
37 def get_name(name,short_name=short_name):
38 if short_name:
38 if short_name:
39 return name.split('/')[-1]
39 return name.split('/')[-1]
40 else:
40 else:
41 return name
41 return name
42 %>
42 %>
43 <div class="${'repo_state_pending' if rstate == 'repo_state_pending' else ''} truncate">
43 <div class="${'repo_state_pending' if rstate == 'repo_state_pending' else ''} truncate">
44 ##NAME
44 ##NAME
45 <a href="${h.route_path('edit_repo',repo_name=name) if admin else h.url('summary_home',repo_name=name)}">
45 <a href="${h.route_path('edit_repo',repo_name=name) if admin else h.url('summary_home',repo_name=name)}">
46
46
47 ##TYPE OF REPO
47 ##TYPE OF REPO
48 %if h.is_hg(rtype):
48 %if h.is_hg(rtype):
49 <span title="${_('Mercurial repository')}"><i class="icon-hg"></i></span>
49 <span title="${_('Mercurial repository')}"><i class="icon-hg"></i></span>
50 %elif h.is_git(rtype):
50 %elif h.is_git(rtype):
51 <span title="${_('Git repository')}"><i class="icon-git"></i></span>
51 <span title="${_('Git repository')}"><i class="icon-git"></i></span>
52 %elif h.is_svn(rtype):
52 %elif h.is_svn(rtype):
53 <span title="${_('Subversion repository')}"><i class="icon-svn"></i></span>
53 <span title="${_('Subversion repository')}"><i class="icon-svn"></i></span>
54 %endif
54 %endif
55
55
56 ##PRIVATE/PUBLIC
56 ##PRIVATE/PUBLIC
57 %if private and c.visual.show_private_icon:
57 %if private and c.visual.show_private_icon:
58 <i class="icon-lock" title="${_('Private repository')}"></i>
58 <i class="icon-lock" title="${_('Private repository')}"></i>
59 %elif not private and c.visual.show_public_icon:
59 %elif not private and c.visual.show_public_icon:
60 <i class="icon-unlock-alt" title="${_('Public repository')}"></i>
60 <i class="icon-unlock-alt" title="${_('Public repository')}"></i>
61 %else:
61 %else:
62 <span></span>
62 <span></span>
63 %endif
63 %endif
64 ${get_name(name)}
64 ${get_name(name)}
65 </a>
65 </a>
66 %if fork_of:
66 %if fork_of:
67 <a href="${h.url('summary_home',repo_name=fork_of.repo_name)}"><i class="icon-code-fork"></i></a>
67 <a href="${h.url('summary_home',repo_name=fork_of.repo_name)}"><i class="icon-code-fork"></i></a>
68 %endif
68 %endif
69 %if rstate == 'repo_state_pending':
69 %if rstate == 'repo_state_pending':
70 <i class="icon-cogs" title="${_('Repository creating in progress...')}"></i>
70 <i class="icon-cogs" title="${_('Repository creating in progress...')}"></i>
71 %endif
71 %endif
72 </div>
72 </div>
73 </%def>
73 </%def>
74
74
75 <%def name="repo_desc(description)">
75 <%def name="repo_desc(description)">
76 <div class="truncate-wrap">${description}</div>
76 <div class="truncate-wrap">${description}</div>
77 </%def>
77 </%def>
78
78
79 <%def name="last_change(last_change)">
79 <%def name="last_change(last_change)">
80 ${h.age_component(last_change)}
80 ${h.age_component(last_change)}
81 </%def>
81 </%def>
82
82
83 <%def name="revision(name,rev,tip,author,last_msg)">
83 <%def name="revision(name,rev,tip,author,last_msg)">
84 <div>
84 <div>
85 %if rev >= 0:
85 %if rev >= 0:
86 <code><a title="${h.tooltip('%s:\n\n%s' % (author,last_msg))}" class="tooltip" href="${h.url('changeset_home',repo_name=name,revision=tip)}">${'r%s:%s' % (rev,h.short_id(tip))}</a></code>
86 <code><a title="${h.tooltip('%s:\n\n%s' % (author,last_msg))}" class="tooltip" href="${h.url('changeset_home',repo_name=name,revision=tip)}">${'r%s:%s' % (rev,h.short_id(tip))}</a></code>
87 %else:
87 %else:
88 ${_('No commits yet')}
88 ${_('No commits yet')}
89 %endif
89 %endif
90 </div>
90 </div>
91 </%def>
91 </%def>
92
92
93 <%def name="rss(name)">
93 <%def name="rss(name)">
94 %if c.rhodecode_user.username != h.DEFAULT_USER:
94 %if c.rhodecode_user.username != h.DEFAULT_USER:
95 <a title="${_('Subscribe to %s rss feed')% name}" href="${h.url('rss_feed_home',repo_name=name,auth_token=c.rhodecode_user.feed_token)}"><i class="icon-rss-sign"></i></a>
95 <a title="${_('Subscribe to %s rss feed')% name}" href="${h.url('rss_feed_home',repo_name=name,auth_token=c.rhodecode_user.feed_token)}"><i class="icon-rss-sign"></i></a>
96 %else:
96 %else:
97 <a title="${_('Subscribe to %s rss feed')% name}" href="${h.url('rss_feed_home',repo_name=name)}"><i class="icon-rss-sign"></i></a>
97 <a title="${_('Subscribe to %s rss feed')% name}" href="${h.url('rss_feed_home',repo_name=name)}"><i class="icon-rss-sign"></i></a>
98 %endif
98 %endif
99 </%def>
99 </%def>
100
100
101 <%def name="atom(name)">
101 <%def name="atom(name)">
102 %if c.rhodecode_user.username != h.DEFAULT_USER:
102 %if c.rhodecode_user.username != h.DEFAULT_USER:
103 <a title="${_('Subscribe to %s atom feed')% name}" href="${h.url('atom_feed_home',repo_name=name,auth_token=c.rhodecode_user.feed_token)}"><i class="icon-rss-sign"></i></a>
103 <a title="${_('Subscribe to %s atom feed')% name}" href="${h.url('atom_feed_home',repo_name=name,auth_token=c.rhodecode_user.feed_token)}"><i class="icon-rss-sign"></i></a>
104 %else:
104 %else:
105 <a title="${_('Subscribe to %s atom feed')% name}" href="${h.url('atom_feed_home',repo_name=name)}"><i class="icon-rss-sign"></i></a>
105 <a title="${_('Subscribe to %s atom feed')% name}" href="${h.url('atom_feed_home',repo_name=name)}"><i class="icon-rss-sign"></i></a>
106 %endif
106 %endif
107 </%def>
107 </%def>
108
108
109 <%def name="user_gravatar(email, size=16)">
109 <%def name="user_gravatar(email, size=16)">
110 <div class="rc-user tooltip" title="${h.author_string(email)}">
110 <div class="rc-user tooltip" title="${h.author_string(email)}">
111 ${base.gravatar(email, 16)}
111 ${base.gravatar(email, 16)}
112 </div>
112 </div>
113 </%def>
113 </%def>
114
114
115 <%def name="repo_actions(repo_name, super_user=True)">
115 <%def name="repo_actions(repo_name, super_user=True)">
116 <div>
116 <div>
117 <div class="grid_edit">
117 <div class="grid_edit">
118 <a href="${h.route_path('edit_repo',repo_name=repo_name)}" title="${_('Edit')}">
118 <a href="${h.route_path('edit_repo',repo_name=repo_name)}" title="${_('Edit')}">
119 <i class="icon-pencil"></i>Edit</a>
119 <i class="icon-pencil"></i>Edit</a>
120 </div>
120 </div>
121 <div class="grid_delete">
121 <div class="grid_delete">
122 ${h.secure_form(h.url('repo', repo_name=repo_name),method='delete')}
122 ${h.secure_form(h.route_path('edit_repo_advanced_delete', repo_name=repo_name), method='POST')}
123 ${h.submit('remove_%s' % repo_name,_('Delete'),class_="btn btn-link btn-danger",
123 ${h.submit('remove_%s' % repo_name,_('Delete'),class_="btn btn-link btn-danger",
124 onclick="return confirm('"+_('Confirm to delete this repository: %s') % repo_name+"');")}
124 onclick="return confirm('"+_('Confirm to delete this repository: %s') % repo_name+"');")}
125 ${h.end_form()}
125 ${h.end_form()}
126 </div>
126 </div>
127 </div>
127 </div>
128 </%def>
128 </%def>
129
129
130 <%def name="repo_state(repo_state)">
130 <%def name="repo_state(repo_state)">
131 <div>
131 <div>
132 %if repo_state == 'repo_state_pending':
132 %if repo_state == 'repo_state_pending':
133 <div class="tag tag4">${_('Creating')}</div>
133 <div class="tag tag4">${_('Creating')}</div>
134 %elif repo_state == 'repo_state_created':
134 %elif repo_state == 'repo_state_created':
135 <div class="tag tag1">${_('Created')}</div>
135 <div class="tag tag1">${_('Created')}</div>
136 %else:
136 %else:
137 <div class="tag alert2" title="${repo_state}">invalid</div>
137 <div class="tag alert2" title="${repo_state}">invalid</div>
138 %endif
138 %endif
139 </div>
139 </div>
140 </%def>
140 </%def>
141
141
142
142
143 ## REPO GROUP RENDERERS
143 ## REPO GROUP RENDERERS
144 <%def name="quick_repo_group_menu(repo_group_name)">
144 <%def name="quick_repo_group_menu(repo_group_name)">
145 <i class="pointer icon-more"></i>
145 <i class="pointer icon-more"></i>
146 <div class="menu_items_container hidden">
146 <div class="menu_items_container hidden">
147 <ul class="menu_items">
147 <ul class="menu_items">
148 <li>
148 <li>
149 <a href="${h.url('repo_group_home',group_name=repo_group_name)}">
149 <a href="${h.url('repo_group_home',group_name=repo_group_name)}">
150 <span class="icon">
150 <span class="icon">
151 <i class="icon-file-text"></i>
151 <i class="icon-file-text"></i>
152 </span>
152 </span>
153 <span>${_('Summary')}</span>
153 <span>${_('Summary')}</span>
154 </a>
154 </a>
155 </li>
155 </li>
156
156
157 </ul>
157 </ul>
158 </div>
158 </div>
159 </%def>
159 </%def>
160
160
161 <%def name="repo_group_name(repo_group_name, children_groups=None)">
161 <%def name="repo_group_name(repo_group_name, children_groups=None)">
162 <div>
162 <div>
163 <a href="${h.url('repo_group_home',group_name=repo_group_name)}">
163 <a href="${h.url('repo_group_home',group_name=repo_group_name)}">
164 <i class="icon-folder-close" title="${_('Repository group')}"></i>
164 <i class="icon-folder-close" title="${_('Repository group')}"></i>
165 %if children_groups:
165 %if children_groups:
166 ${h.literal(' &raquo; '.join(children_groups))}
166 ${h.literal(' &raquo; '.join(children_groups))}
167 %else:
167 %else:
168 ${repo_group_name}
168 ${repo_group_name}
169 %endif
169 %endif
170 </a>
170 </a>
171 </div>
171 </div>
172 </%def>
172 </%def>
173
173
174 <%def name="repo_group_desc(description)">
174 <%def name="repo_group_desc(description)">
175 <div class="truncate-wrap">${description}</div>
175 <div class="truncate-wrap">${description}</div>
176 </%def>
176 </%def>
177
177
178 <%def name="repo_group_actions(repo_group_id, repo_group_name, gr_count)">
178 <%def name="repo_group_actions(repo_group_id, repo_group_name, gr_count)">
179 <div class="grid_edit">
179 <div class="grid_edit">
180 <a href="${h.url('edit_repo_group',group_name=repo_group_name)}" title="${_('Edit')}">Edit</a>
180 <a href="${h.url('edit_repo_group',group_name=repo_group_name)}" title="${_('Edit')}">Edit</a>
181 </div>
181 </div>
182 <div class="grid_delete">
182 <div class="grid_delete">
183 ${h.secure_form(h.url('delete_repo_group', group_name=repo_group_name),method='delete')}
183 ${h.secure_form(h.url('delete_repo_group', group_name=repo_group_name),method='delete')}
184 ${h.submit('remove_%s' % repo_group_name,_('Delete'),class_="btn btn-link btn-danger",
184 ${h.submit('remove_%s' % repo_group_name,_('Delete'),class_="btn btn-link btn-danger",
185 onclick="return confirm('"+ungettext('Confirm to delete this group: %s with %s repository','Confirm to delete this group: %s with %s repositories',gr_count) % (repo_group_name, gr_count)+"');")}
185 onclick="return confirm('"+ungettext('Confirm to delete this group: %s with %s repository','Confirm to delete this group: %s with %s repositories',gr_count) % (repo_group_name, gr_count)+"');")}
186 ${h.end_form()}
186 ${h.end_form()}
187 </div>
187 </div>
188 </%def>
188 </%def>
189
189
190
190
191 <%def name="user_actions(user_id, username)">
191 <%def name="user_actions(user_id, username)">
192 <div class="grid_edit">
192 <div class="grid_edit">
193 <a href="${h.url('edit_user',user_id=user_id)}" title="${_('Edit')}">
193 <a href="${h.url('edit_user',user_id=user_id)}" title="${_('Edit')}">
194 <i class="icon-pencil"></i>Edit</a>
194 <i class="icon-pencil"></i>Edit</a>
195 </div>
195 </div>
196 <div class="grid_delete">
196 <div class="grid_delete">
197 ${h.secure_form(h.url('delete_user', user_id=user_id),method='delete')}
197 ${h.secure_form(h.url('delete_user', user_id=user_id),method='delete')}
198 ${h.submit('remove_',_('Delete'),id="remove_user_%s" % user_id, class_="btn btn-link btn-danger",
198 ${h.submit('remove_',_('Delete'),id="remove_user_%s" % user_id, class_="btn btn-link btn-danger",
199 onclick="return confirm('"+_('Confirm to delete this user: %s') % username+"');")}
199 onclick="return confirm('"+_('Confirm to delete this user: %s') % username+"');")}
200 ${h.end_form()}
200 ${h.end_form()}
201 </div>
201 </div>
202 </%def>
202 </%def>
203
203
204 <%def name="user_group_actions(user_group_id, user_group_name)">
204 <%def name="user_group_actions(user_group_id, user_group_name)">
205 <div class="grid_edit">
205 <div class="grid_edit">
206 <a href="${h.url('edit_users_group', user_group_id=user_group_id)}" title="${_('Edit')}">Edit</a>
206 <a href="${h.url('edit_users_group', user_group_id=user_group_id)}" title="${_('Edit')}">Edit</a>
207 </div>
207 </div>
208 <div class="grid_delete">
208 <div class="grid_delete">
209 ${h.secure_form(h.url('delete_users_group', user_group_id=user_group_id),method='delete')}
209 ${h.secure_form(h.url('delete_users_group', user_group_id=user_group_id),method='delete')}
210 ${h.submit('remove_',_('Delete'),id="remove_group_%s" % user_group_id, class_="btn btn-link btn-danger",
210 ${h.submit('remove_',_('Delete'),id="remove_group_%s" % user_group_id, class_="btn btn-link btn-danger",
211 onclick="return confirm('"+_('Confirm to delete this user group: %s') % user_group_name+"');")}
211 onclick="return confirm('"+_('Confirm to delete this user group: %s') % user_group_name+"');")}
212 ${h.end_form()}
212 ${h.end_form()}
213 </div>
213 </div>
214 </%def>
214 </%def>
215
215
216
216
217 <%def name="user_name(user_id, username)">
217 <%def name="user_name(user_id, username)">
218 ${h.link_to(h.person(username, 'username_or_name_or_email'), h.url('edit_user', user_id=user_id))}
218 ${h.link_to(h.person(username, 'username_or_name_or_email'), h.url('edit_user', user_id=user_id))}
219 </%def>
219 </%def>
220
220
221 <%def name="user_profile(username)">
221 <%def name="user_profile(username)">
222 ${base.gravatar_with_user(username, 16)}
222 ${base.gravatar_with_user(username, 16)}
223 </%def>
223 </%def>
224
224
225 <%def name="user_group_name(user_group_id, user_group_name)">
225 <%def name="user_group_name(user_group_id, user_group_name)">
226 <div>
226 <div>
227 <a href="${h.url('edit_users_group', user_group_id=user_group_id)}">
227 <a href="${h.url('edit_users_group', user_group_id=user_group_id)}">
228 <i class="icon-group" title="${_('User group')}"></i> ${user_group_name}</a>
228 <i class="icon-group" title="${_('User group')}"></i> ${user_group_name}</a>
229 </div>
229 </div>
230 </%def>
230 </%def>
231
231
232
232
233 ## GISTS
233 ## GISTS
234
234
235 <%def name="gist_gravatar(full_contact)">
235 <%def name="gist_gravatar(full_contact)">
236 <div class="gist_gravatar">
236 <div class="gist_gravatar">
237 ${base.gravatar(full_contact, 30)}
237 ${base.gravatar(full_contact, 30)}
238 </div>
238 </div>
239 </%def>
239 </%def>
240
240
241 <%def name="gist_access_id(gist_access_id, full_contact)">
241 <%def name="gist_access_id(gist_access_id, full_contact)">
242 <div>
242 <div>
243 <b>
243 <b>
244 <a href="${h.url('gist',gist_id=gist_access_id)}">gist: ${gist_access_id}</a>
244 <a href="${h.url('gist',gist_id=gist_access_id)}">gist: ${gist_access_id}</a>
245 </b>
245 </b>
246 </div>
246 </div>
247 </%def>
247 </%def>
248
248
249 <%def name="gist_author(full_contact, created_on, expires)">
249 <%def name="gist_author(full_contact, created_on, expires)">
250 ${base.gravatar_with_user(full_contact, 16)}
250 ${base.gravatar_with_user(full_contact, 16)}
251 </%def>
251 </%def>
252
252
253
253
254 <%def name="gist_created(created_on)">
254 <%def name="gist_created(created_on)">
255 <div class="created">
255 <div class="created">
256 ${h.age_component(created_on, time_is_local=True)}
256 ${h.age_component(created_on, time_is_local=True)}
257 </div>
257 </div>
258 </%def>
258 </%def>
259
259
260 <%def name="gist_expires(expires)">
260 <%def name="gist_expires(expires)">
261 <div class="created">
261 <div class="created">
262 %if expires == -1:
262 %if expires == -1:
263 ${_('never')}
263 ${_('never')}
264 %else:
264 %else:
265 ${h.age_component(h.time_to_utcdatetime(expires))}
265 ${h.age_component(h.time_to_utcdatetime(expires))}
266 %endif
266 %endif
267 </div>
267 </div>
268 </%def>
268 </%def>
269
269
270 <%def name="gist_type(gist_type)">
270 <%def name="gist_type(gist_type)">
271 %if gist_type != 'public':
271 %if gist_type != 'public':
272 <div class="tag">${_('Private')}</div>
272 <div class="tag">${_('Private')}</div>
273 %endif
273 %endif
274 </%def>
274 </%def>
275
275
276 <%def name="gist_description(gist_description)">
276 <%def name="gist_description(gist_description)">
277 ${gist_description}
277 ${gist_description}
278 </%def>
278 </%def>
279
279
280
280
281 ## PULL REQUESTS GRID RENDERERS
281 ## PULL REQUESTS GRID RENDERERS
282
282
283 <%def name="pullrequest_target_repo(repo_name)">
283 <%def name="pullrequest_target_repo(repo_name)">
284 <div class="truncate">
284 <div class="truncate">
285 ${h.link_to(repo_name,h.url('summary_home',repo_name=repo_name))}
285 ${h.link_to(repo_name,h.url('summary_home',repo_name=repo_name))}
286 </div>
286 </div>
287 </%def>
287 </%def>
288 <%def name="pullrequest_status(status)">
288 <%def name="pullrequest_status(status)">
289 <div class="${'flag_status %s' % status} pull-left"></div>
289 <div class="${'flag_status %s' % status} pull-left"></div>
290 </%def>
290 </%def>
291
291
292 <%def name="pullrequest_title(title, description)">
292 <%def name="pullrequest_title(title, description)">
293 ${title} <br/>
293 ${title} <br/>
294 ${h.shorter(description, 40)}
294 ${h.shorter(description, 40)}
295 </%def>
295 </%def>
296
296
297 <%def name="pullrequest_comments(comments_nr)">
297 <%def name="pullrequest_comments(comments_nr)">
298 <i class="icon-comment"></i> ${comments_nr}
298 <i class="icon-comment"></i> ${comments_nr}
299 </%def>
299 </%def>
300
300
301 <%def name="pullrequest_name(pull_request_id, target_repo_name, short=False)">
301 <%def name="pullrequest_name(pull_request_id, target_repo_name, short=False)">
302 <a href="${h.url('pullrequest_show',repo_name=target_repo_name,pull_request_id=pull_request_id)}">
302 <a href="${h.url('pullrequest_show',repo_name=target_repo_name,pull_request_id=pull_request_id)}">
303 % if short:
303 % if short:
304 #${pull_request_id}
304 #${pull_request_id}
305 % else:
305 % else:
306 ${_('Pull request #%(pr_number)s') % {'pr_number': pull_request_id,}}
306 ${_('Pull request #%(pr_number)s') % {'pr_number': pull_request_id,}}
307 % endif
307 % endif
308 </a>
308 </a>
309 </%def>
309 </%def>
310
310
311 <%def name="pullrequest_updated_on(updated_on)">
311 <%def name="pullrequest_updated_on(updated_on)">
312 ${h.age_component(h.time_to_utcdatetime(updated_on))}
312 ${h.age_component(h.time_to_utcdatetime(updated_on))}
313 </%def>
313 </%def>
314
314
315 <%def name="pullrequest_author(full_contact)">
315 <%def name="pullrequest_author(full_contact)">
316 ${base.gravatar_with_user(full_contact, 16)}
316 ${base.gravatar_with_user(full_contact, 16)}
317 </%def>
317 </%def>
@@ -1,1208 +1,1117 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 urllib
21 import urllib
22
22
23 import mock
23 import mock
24 import pytest
24 import pytest
25
25
26 from rhodecode.lib import auth
26 from rhodecode.lib import auth
27 from rhodecode.lib.utils2 import safe_str, str2bool, safe_unicode
27 from rhodecode.lib.utils2 import safe_str, str2bool, safe_unicode
28 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
28 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
29 from rhodecode.model.db import Repository, RepoGroup, UserRepoToPerm, User,\
29 from rhodecode.model.db import Repository, RepoGroup, UserRepoToPerm, User,\
30 Permission
30 Permission
31 from rhodecode.model.meta import Session
31 from rhodecode.model.meta import Session
32 from rhodecode.model.repo import RepoModel
32 from rhodecode.model.repo import RepoModel
33 from rhodecode.model.repo_group import RepoGroupModel
33 from rhodecode.model.repo_group import RepoGroupModel
34 from rhodecode.model.settings import SettingsModel, VcsSettingsModel
34 from rhodecode.model.settings import SettingsModel, VcsSettingsModel
35 from rhodecode.model.user import UserModel
35 from rhodecode.model.user import UserModel
36 from rhodecode.tests import (
36 from rhodecode.tests import (
37 login_user_session, url, assert_session_flash, TEST_USER_ADMIN_LOGIN,
37 login_user_session, url, assert_session_flash, TEST_USER_ADMIN_LOGIN,
38 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS, HG_REPO, GIT_REPO,
38 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS, HG_REPO, GIT_REPO,
39 logout_user_session)
39 logout_user_session)
40 from rhodecode.tests.fixture import Fixture, error_function
40 from rhodecode.tests.fixture import Fixture, error_function
41 from rhodecode.tests.utils import AssertResponse, repo_on_filesystem
41 from rhodecode.tests.utils import AssertResponse, repo_on_filesystem
42
42
43 fixture = Fixture()
43 fixture = Fixture()
44
44
45
45
46 @pytest.mark.usefixtures("app")
46 @pytest.mark.usefixtures("app")
47 class TestAdminRepos(object):
47 class TestAdminRepos(object):
48
48
49 def test_index(self):
49 def test_index(self):
50 self.app.get(url('repos'))
50 self.app.get(url('repos'))
51
51
52 def test_create_page_restricted(self, autologin_user, backend):
52 def test_create_page_restricted(self, autologin_user, backend):
53 with mock.patch('rhodecode.BACKENDS', {'git': 'git'}):
53 with mock.patch('rhodecode.BACKENDS', {'git': 'git'}):
54 response = self.app.get(url('new_repo'), status=200)
54 response = self.app.get(url('new_repo'), status=200)
55 assert_response = AssertResponse(response)
55 assert_response = AssertResponse(response)
56 element = assert_response.get_element('#repo_type')
56 element = assert_response.get_element('#repo_type')
57 assert element.text_content() == '\ngit\n'
57 assert element.text_content() == '\ngit\n'
58
58
59 def test_create_page_non_restricted(self, autologin_user, backend):
59 def test_create_page_non_restricted(self, autologin_user, backend):
60 response = self.app.get(url('new_repo'), status=200)
60 response = self.app.get(url('new_repo'), status=200)
61 assert_response = AssertResponse(response)
61 assert_response = AssertResponse(response)
62 assert_response.element_contains('#repo_type', 'git')
62 assert_response.element_contains('#repo_type', 'git')
63 assert_response.element_contains('#repo_type', 'svn')
63 assert_response.element_contains('#repo_type', 'svn')
64 assert_response.element_contains('#repo_type', 'hg')
64 assert_response.element_contains('#repo_type', 'hg')
65
65
66 @pytest.mark.parametrize("suffix",
66 @pytest.mark.parametrize("suffix",
67 [u'', u'xxa'], ids=['', 'non-ascii'])
67 [u'', u'xxa'], ids=['', 'non-ascii'])
68 def test_create(self, autologin_user, backend, suffix, csrf_token):
68 def test_create(self, autologin_user, backend, suffix, csrf_token):
69 repo_name_unicode = backend.new_repo_name(suffix=suffix)
69 repo_name_unicode = backend.new_repo_name(suffix=suffix)
70 repo_name = repo_name_unicode.encode('utf8')
70 repo_name = repo_name_unicode.encode('utf8')
71 description_unicode = u'description for newly created repo' + suffix
71 description_unicode = u'description for newly created repo' + suffix
72 description = description_unicode.encode('utf8')
72 description = description_unicode.encode('utf8')
73 response = self.app.post(
73 response = self.app.post(
74 url('repos'),
74 url('repos'),
75 fixture._get_repo_create_params(
75 fixture._get_repo_create_params(
76 repo_private=False,
76 repo_private=False,
77 repo_name=repo_name,
77 repo_name=repo_name,
78 repo_type=backend.alias,
78 repo_type=backend.alias,
79 repo_description=description,
79 repo_description=description,
80 csrf_token=csrf_token),
80 csrf_token=csrf_token),
81 status=302)
81 status=302)
82
82
83 self.assert_repository_is_created_correctly(
83 self.assert_repository_is_created_correctly(
84 repo_name, description, backend)
84 repo_name, description, backend)
85
85
86 def test_create_numeric(self, autologin_user, backend, csrf_token):
86 def test_create_numeric(self, autologin_user, backend, csrf_token):
87 numeric_repo = '1234'
87 numeric_repo = '1234'
88 repo_name = numeric_repo
88 repo_name = numeric_repo
89 description = 'description for newly created repo' + numeric_repo
89 description = 'description for newly created repo' + numeric_repo
90 self.app.post(
90 self.app.post(
91 url('repos'),
91 url('repos'),
92 fixture._get_repo_create_params(
92 fixture._get_repo_create_params(
93 repo_private=False,
93 repo_private=False,
94 repo_name=repo_name,
94 repo_name=repo_name,
95 repo_type=backend.alias,
95 repo_type=backend.alias,
96 repo_description=description,
96 repo_description=description,
97 csrf_token=csrf_token))
97 csrf_token=csrf_token))
98
98
99 self.assert_repository_is_created_correctly(
99 self.assert_repository_is_created_correctly(
100 repo_name, description, backend)
100 repo_name, description, backend)
101
101
102 @pytest.mark.parametrize("suffix", [u'', u'Δ…Δ‡Δ™'], ids=['', 'non-ascii'])
102 @pytest.mark.parametrize("suffix", [u'', u'Δ…Δ‡Δ™'], ids=['', 'non-ascii'])
103 def test_create_in_group(
103 def test_create_in_group(
104 self, autologin_user, backend, suffix, csrf_token):
104 self, autologin_user, backend, suffix, csrf_token):
105 # create GROUP
105 # create GROUP
106 group_name = 'sometest_%s' % backend.alias
106 group_name = 'sometest_%s' % backend.alias
107 gr = RepoGroupModel().create(group_name=group_name,
107 gr = RepoGroupModel().create(group_name=group_name,
108 group_description='test',
108 group_description='test',
109 owner=TEST_USER_ADMIN_LOGIN)
109 owner=TEST_USER_ADMIN_LOGIN)
110 Session().commit()
110 Session().commit()
111
111
112 repo_name = u'ingroup' + suffix
112 repo_name = u'ingroup' + suffix
113 repo_name_full = RepoGroup.url_sep().join(
113 repo_name_full = RepoGroup.url_sep().join(
114 [group_name, repo_name])
114 [group_name, repo_name])
115 description = u'description for newly created repo'
115 description = u'description for newly created repo'
116 self.app.post(
116 self.app.post(
117 url('repos'),
117 url('repos'),
118 fixture._get_repo_create_params(
118 fixture._get_repo_create_params(
119 repo_private=False,
119 repo_private=False,
120 repo_name=safe_str(repo_name),
120 repo_name=safe_str(repo_name),
121 repo_type=backend.alias,
121 repo_type=backend.alias,
122 repo_description=description,
122 repo_description=description,
123 repo_group=gr.group_id,
123 repo_group=gr.group_id,
124 csrf_token=csrf_token))
124 csrf_token=csrf_token))
125
125
126 # TODO: johbo: Cleanup work to fixture
126 # TODO: johbo: Cleanup work to fixture
127 try:
127 try:
128 self.assert_repository_is_created_correctly(
128 self.assert_repository_is_created_correctly(
129 repo_name_full, description, backend)
129 repo_name_full, description, backend)
130
130
131 new_repo = RepoModel().get_by_repo_name(repo_name_full)
131 new_repo = RepoModel().get_by_repo_name(repo_name_full)
132 inherited_perms = UserRepoToPerm.query().filter(
132 inherited_perms = UserRepoToPerm.query().filter(
133 UserRepoToPerm.repository_id == new_repo.repo_id).all()
133 UserRepoToPerm.repository_id == new_repo.repo_id).all()
134 assert len(inherited_perms) == 1
134 assert len(inherited_perms) == 1
135 finally:
135 finally:
136 RepoModel().delete(repo_name_full)
136 RepoModel().delete(repo_name_full)
137 RepoGroupModel().delete(group_name)
137 RepoGroupModel().delete(group_name)
138 Session().commit()
138 Session().commit()
139
139
140 def test_create_in_group_numeric(
140 def test_create_in_group_numeric(
141 self, autologin_user, backend, csrf_token):
141 self, autologin_user, backend, csrf_token):
142 # create GROUP
142 # create GROUP
143 group_name = 'sometest_%s' % backend.alias
143 group_name = 'sometest_%s' % backend.alias
144 gr = RepoGroupModel().create(group_name=group_name,
144 gr = RepoGroupModel().create(group_name=group_name,
145 group_description='test',
145 group_description='test',
146 owner=TEST_USER_ADMIN_LOGIN)
146 owner=TEST_USER_ADMIN_LOGIN)
147 Session().commit()
147 Session().commit()
148
148
149 repo_name = '12345'
149 repo_name = '12345'
150 repo_name_full = RepoGroup.url_sep().join([group_name, repo_name])
150 repo_name_full = RepoGroup.url_sep().join([group_name, repo_name])
151 description = 'description for newly created repo'
151 description = 'description for newly created repo'
152 self.app.post(
152 self.app.post(
153 url('repos'),
153 url('repos'),
154 fixture._get_repo_create_params(
154 fixture._get_repo_create_params(
155 repo_private=False,
155 repo_private=False,
156 repo_name=repo_name,
156 repo_name=repo_name,
157 repo_type=backend.alias,
157 repo_type=backend.alias,
158 repo_description=description,
158 repo_description=description,
159 repo_group=gr.group_id,
159 repo_group=gr.group_id,
160 csrf_token=csrf_token))
160 csrf_token=csrf_token))
161
161
162 # TODO: johbo: Cleanup work to fixture
162 # TODO: johbo: Cleanup work to fixture
163 try:
163 try:
164 self.assert_repository_is_created_correctly(
164 self.assert_repository_is_created_correctly(
165 repo_name_full, description, backend)
165 repo_name_full, description, backend)
166
166
167 new_repo = RepoModel().get_by_repo_name(repo_name_full)
167 new_repo = RepoModel().get_by_repo_name(repo_name_full)
168 inherited_perms = UserRepoToPerm.query()\
168 inherited_perms = UserRepoToPerm.query()\
169 .filter(UserRepoToPerm.repository_id == new_repo.repo_id).all()
169 .filter(UserRepoToPerm.repository_id == new_repo.repo_id).all()
170 assert len(inherited_perms) == 1
170 assert len(inherited_perms) == 1
171 finally:
171 finally:
172 RepoModel().delete(repo_name_full)
172 RepoModel().delete(repo_name_full)
173 RepoGroupModel().delete(group_name)
173 RepoGroupModel().delete(group_name)
174 Session().commit()
174 Session().commit()
175
175
176 def test_create_in_group_without_needed_permissions(self, backend):
176 def test_create_in_group_without_needed_permissions(self, backend):
177 session = login_user_session(
177 session = login_user_session(
178 self.app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
178 self.app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
179 csrf_token = auth.get_csrf_token(session)
179 csrf_token = auth.get_csrf_token(session)
180 # revoke
180 # revoke
181 user_model = UserModel()
181 user_model = UserModel()
182 # disable fork and create on default user
182 # disable fork and create on default user
183 user_model.revoke_perm(User.DEFAULT_USER, 'hg.create.repository')
183 user_model.revoke_perm(User.DEFAULT_USER, 'hg.create.repository')
184 user_model.grant_perm(User.DEFAULT_USER, 'hg.create.none')
184 user_model.grant_perm(User.DEFAULT_USER, 'hg.create.none')
185 user_model.revoke_perm(User.DEFAULT_USER, 'hg.fork.repository')
185 user_model.revoke_perm(User.DEFAULT_USER, 'hg.fork.repository')
186 user_model.grant_perm(User.DEFAULT_USER, 'hg.fork.none')
186 user_model.grant_perm(User.DEFAULT_USER, 'hg.fork.none')
187
187
188 # disable on regular user
188 # disable on regular user
189 user_model.revoke_perm(TEST_USER_REGULAR_LOGIN, 'hg.create.repository')
189 user_model.revoke_perm(TEST_USER_REGULAR_LOGIN, 'hg.create.repository')
190 user_model.grant_perm(TEST_USER_REGULAR_LOGIN, 'hg.create.none')
190 user_model.grant_perm(TEST_USER_REGULAR_LOGIN, 'hg.create.none')
191 user_model.revoke_perm(TEST_USER_REGULAR_LOGIN, 'hg.fork.repository')
191 user_model.revoke_perm(TEST_USER_REGULAR_LOGIN, 'hg.fork.repository')
192 user_model.grant_perm(TEST_USER_REGULAR_LOGIN, 'hg.fork.none')
192 user_model.grant_perm(TEST_USER_REGULAR_LOGIN, 'hg.fork.none')
193 Session().commit()
193 Session().commit()
194
194
195 # create GROUP
195 # create GROUP
196 group_name = 'reg_sometest_%s' % backend.alias
196 group_name = 'reg_sometest_%s' % backend.alias
197 gr = RepoGroupModel().create(group_name=group_name,
197 gr = RepoGroupModel().create(group_name=group_name,
198 group_description='test',
198 group_description='test',
199 owner=TEST_USER_ADMIN_LOGIN)
199 owner=TEST_USER_ADMIN_LOGIN)
200 Session().commit()
200 Session().commit()
201
201
202 group_name_allowed = 'reg_sometest_allowed_%s' % backend.alias
202 group_name_allowed = 'reg_sometest_allowed_%s' % backend.alias
203 gr_allowed = RepoGroupModel().create(
203 gr_allowed = RepoGroupModel().create(
204 group_name=group_name_allowed,
204 group_name=group_name_allowed,
205 group_description='test',
205 group_description='test',
206 owner=TEST_USER_REGULAR_LOGIN)
206 owner=TEST_USER_REGULAR_LOGIN)
207 Session().commit()
207 Session().commit()
208
208
209 repo_name = 'ingroup'
209 repo_name = 'ingroup'
210 description = 'description for newly created repo'
210 description = 'description for newly created repo'
211 response = self.app.post(
211 response = self.app.post(
212 url('repos'),
212 url('repos'),
213 fixture._get_repo_create_params(
213 fixture._get_repo_create_params(
214 repo_private=False,
214 repo_private=False,
215 repo_name=repo_name,
215 repo_name=repo_name,
216 repo_type=backend.alias,
216 repo_type=backend.alias,
217 repo_description=description,
217 repo_description=description,
218 repo_group=gr.group_id,
218 repo_group=gr.group_id,
219 csrf_token=csrf_token))
219 csrf_token=csrf_token))
220
220
221 response.mustcontain('Invalid value')
221 response.mustcontain('Invalid value')
222
222
223 # user is allowed to create in this group
223 # user is allowed to create in this group
224 repo_name = 'ingroup'
224 repo_name = 'ingroup'
225 repo_name_full = RepoGroup.url_sep().join(
225 repo_name_full = RepoGroup.url_sep().join(
226 [group_name_allowed, repo_name])
226 [group_name_allowed, repo_name])
227 description = 'description for newly created repo'
227 description = 'description for newly created repo'
228 response = self.app.post(
228 response = self.app.post(
229 url('repos'),
229 url('repos'),
230 fixture._get_repo_create_params(
230 fixture._get_repo_create_params(
231 repo_private=False,
231 repo_private=False,
232 repo_name=repo_name,
232 repo_name=repo_name,
233 repo_type=backend.alias,
233 repo_type=backend.alias,
234 repo_description=description,
234 repo_description=description,
235 repo_group=gr_allowed.group_id,
235 repo_group=gr_allowed.group_id,
236 csrf_token=csrf_token))
236 csrf_token=csrf_token))
237
237
238 # TODO: johbo: Cleanup in pytest fixture
238 # TODO: johbo: Cleanup in pytest fixture
239 try:
239 try:
240 self.assert_repository_is_created_correctly(
240 self.assert_repository_is_created_correctly(
241 repo_name_full, description, backend)
241 repo_name_full, description, backend)
242
242
243 new_repo = RepoModel().get_by_repo_name(repo_name_full)
243 new_repo = RepoModel().get_by_repo_name(repo_name_full)
244 inherited_perms = UserRepoToPerm.query().filter(
244 inherited_perms = UserRepoToPerm.query().filter(
245 UserRepoToPerm.repository_id == new_repo.repo_id).all()
245 UserRepoToPerm.repository_id == new_repo.repo_id).all()
246 assert len(inherited_perms) == 1
246 assert len(inherited_perms) == 1
247
247
248 assert repo_on_filesystem(repo_name_full)
248 assert repo_on_filesystem(repo_name_full)
249 finally:
249 finally:
250 RepoModel().delete(repo_name_full)
250 RepoModel().delete(repo_name_full)
251 RepoGroupModel().delete(group_name)
251 RepoGroupModel().delete(group_name)
252 RepoGroupModel().delete(group_name_allowed)
252 RepoGroupModel().delete(group_name_allowed)
253 Session().commit()
253 Session().commit()
254
254
255 def test_create_in_group_inherit_permissions(self, autologin_user, backend,
255 def test_create_in_group_inherit_permissions(self, autologin_user, backend,
256 csrf_token):
256 csrf_token):
257 # create GROUP
257 # create GROUP
258 group_name = 'sometest_%s' % backend.alias
258 group_name = 'sometest_%s' % backend.alias
259 gr = RepoGroupModel().create(group_name=group_name,
259 gr = RepoGroupModel().create(group_name=group_name,
260 group_description='test',
260 group_description='test',
261 owner=TEST_USER_ADMIN_LOGIN)
261 owner=TEST_USER_ADMIN_LOGIN)
262 perm = Permission.get_by_key('repository.write')
262 perm = Permission.get_by_key('repository.write')
263 RepoGroupModel().grant_user_permission(
263 RepoGroupModel().grant_user_permission(
264 gr, TEST_USER_REGULAR_LOGIN, perm)
264 gr, TEST_USER_REGULAR_LOGIN, perm)
265
265
266 # add repo permissions
266 # add repo permissions
267 Session().commit()
267 Session().commit()
268
268
269 repo_name = 'ingroup_inherited_%s' % backend.alias
269 repo_name = 'ingroup_inherited_%s' % backend.alias
270 repo_name_full = RepoGroup.url_sep().join([group_name, repo_name])
270 repo_name_full = RepoGroup.url_sep().join([group_name, repo_name])
271 description = 'description for newly created repo'
271 description = 'description for newly created repo'
272 self.app.post(
272 self.app.post(
273 url('repos'),
273 url('repos'),
274 fixture._get_repo_create_params(
274 fixture._get_repo_create_params(
275 repo_private=False,
275 repo_private=False,
276 repo_name=repo_name,
276 repo_name=repo_name,
277 repo_type=backend.alias,
277 repo_type=backend.alias,
278 repo_description=description,
278 repo_description=description,
279 repo_group=gr.group_id,
279 repo_group=gr.group_id,
280 repo_copy_permissions=True,
280 repo_copy_permissions=True,
281 csrf_token=csrf_token))
281 csrf_token=csrf_token))
282
282
283 # TODO: johbo: Cleanup to pytest fixture
283 # TODO: johbo: Cleanup to pytest fixture
284 try:
284 try:
285 self.assert_repository_is_created_correctly(
285 self.assert_repository_is_created_correctly(
286 repo_name_full, description, backend)
286 repo_name_full, description, backend)
287 except Exception:
287 except Exception:
288 RepoGroupModel().delete(group_name)
288 RepoGroupModel().delete(group_name)
289 Session().commit()
289 Session().commit()
290 raise
290 raise
291
291
292 # check if inherited permissions are applied
292 # check if inherited permissions are applied
293 new_repo = RepoModel().get_by_repo_name(repo_name_full)
293 new_repo = RepoModel().get_by_repo_name(repo_name_full)
294 inherited_perms = UserRepoToPerm.query().filter(
294 inherited_perms = UserRepoToPerm.query().filter(
295 UserRepoToPerm.repository_id == new_repo.repo_id).all()
295 UserRepoToPerm.repository_id == new_repo.repo_id).all()
296 assert len(inherited_perms) == 2
296 assert len(inherited_perms) == 2
297
297
298 assert TEST_USER_REGULAR_LOGIN in [
298 assert TEST_USER_REGULAR_LOGIN in [
299 x.user.username for x in inherited_perms]
299 x.user.username for x in inherited_perms]
300 assert 'repository.write' in [
300 assert 'repository.write' in [
301 x.permission.permission_name for x in inherited_perms]
301 x.permission.permission_name for x in inherited_perms]
302
302
303 RepoModel().delete(repo_name_full)
303 RepoModel().delete(repo_name_full)
304 RepoGroupModel().delete(group_name)
304 RepoGroupModel().delete(group_name)
305 Session().commit()
305 Session().commit()
306
306
307 @pytest.mark.xfail_backends(
307 @pytest.mark.xfail_backends(
308 "git", "hg", reason="Missing reposerver support")
308 "git", "hg", reason="Missing reposerver support")
309 def test_create_with_clone_uri(self, autologin_user, backend, reposerver,
309 def test_create_with_clone_uri(self, autologin_user, backend, reposerver,
310 csrf_token):
310 csrf_token):
311 source_repo = backend.create_repo(number_of_commits=2)
311 source_repo = backend.create_repo(number_of_commits=2)
312 source_repo_name = source_repo.repo_name
312 source_repo_name = source_repo.repo_name
313 reposerver.serve(source_repo.scm_instance())
313 reposerver.serve(source_repo.scm_instance())
314
314
315 repo_name = backend.new_repo_name()
315 repo_name = backend.new_repo_name()
316 response = self.app.post(
316 response = self.app.post(
317 url('repos'),
317 url('repos'),
318 fixture._get_repo_create_params(
318 fixture._get_repo_create_params(
319 repo_private=False,
319 repo_private=False,
320 repo_name=repo_name,
320 repo_name=repo_name,
321 repo_type=backend.alias,
321 repo_type=backend.alias,
322 repo_description='',
322 repo_description='',
323 clone_uri=reposerver.url,
323 clone_uri=reposerver.url,
324 csrf_token=csrf_token),
324 csrf_token=csrf_token),
325 status=302)
325 status=302)
326
326
327 # Should be redirected to the creating page
327 # Should be redirected to the creating page
328 response.mustcontain('repo_creating')
328 response.mustcontain('repo_creating')
329
329
330 # Expecting that both repositories have same history
330 # Expecting that both repositories have same history
331 source_repo = RepoModel().get_by_repo_name(source_repo_name)
331 source_repo = RepoModel().get_by_repo_name(source_repo_name)
332 source_vcs = source_repo.scm_instance()
332 source_vcs = source_repo.scm_instance()
333 repo = RepoModel().get_by_repo_name(repo_name)
333 repo = RepoModel().get_by_repo_name(repo_name)
334 repo_vcs = repo.scm_instance()
334 repo_vcs = repo.scm_instance()
335 assert source_vcs[0].message == repo_vcs[0].message
335 assert source_vcs[0].message == repo_vcs[0].message
336 assert source_vcs.count() == repo_vcs.count()
336 assert source_vcs.count() == repo_vcs.count()
337 assert source_vcs.commit_ids == repo_vcs.commit_ids
337 assert source_vcs.commit_ids == repo_vcs.commit_ids
338
338
339 @pytest.mark.xfail_backends("svn", reason="Depends on import support")
339 @pytest.mark.xfail_backends("svn", reason="Depends on import support")
340 def test_create_remote_repo_wrong_clone_uri(self, autologin_user, backend,
340 def test_create_remote_repo_wrong_clone_uri(self, autologin_user, backend,
341 csrf_token):
341 csrf_token):
342 repo_name = backend.new_repo_name()
342 repo_name = backend.new_repo_name()
343 description = 'description for newly created repo'
343 description = 'description for newly created repo'
344 response = self.app.post(
344 response = self.app.post(
345 url('repos'),
345 url('repos'),
346 fixture._get_repo_create_params(
346 fixture._get_repo_create_params(
347 repo_private=False,
347 repo_private=False,
348 repo_name=repo_name,
348 repo_name=repo_name,
349 repo_type=backend.alias,
349 repo_type=backend.alias,
350 repo_description=description,
350 repo_description=description,
351 clone_uri='http://repo.invalid/repo',
351 clone_uri='http://repo.invalid/repo',
352 csrf_token=csrf_token))
352 csrf_token=csrf_token))
353 response.mustcontain('invalid clone url')
353 response.mustcontain('invalid clone url')
354
354
355 @pytest.mark.xfail_backends("svn", reason="Depends on import support")
355 @pytest.mark.xfail_backends("svn", reason="Depends on import support")
356 def test_create_remote_repo_wrong_clone_uri_hg_svn(
356 def test_create_remote_repo_wrong_clone_uri_hg_svn(
357 self, autologin_user, backend, csrf_token):
357 self, autologin_user, backend, csrf_token):
358 repo_name = backend.new_repo_name()
358 repo_name = backend.new_repo_name()
359 description = 'description for newly created repo'
359 description = 'description for newly created repo'
360 response = self.app.post(
360 response = self.app.post(
361 url('repos'),
361 url('repos'),
362 fixture._get_repo_create_params(
362 fixture._get_repo_create_params(
363 repo_private=False,
363 repo_private=False,
364 repo_name=repo_name,
364 repo_name=repo_name,
365 repo_type=backend.alias,
365 repo_type=backend.alias,
366 repo_description=description,
366 repo_description=description,
367 clone_uri='svn+http://svn.invalid/repo',
367 clone_uri='svn+http://svn.invalid/repo',
368 csrf_token=csrf_token))
368 csrf_token=csrf_token))
369 response.mustcontain('invalid clone url')
369 response.mustcontain('invalid clone url')
370
370
371 def test_create_with_git_suffix(
371 def test_create_with_git_suffix(
372 self, autologin_user, backend, csrf_token):
372 self, autologin_user, backend, csrf_token):
373 repo_name = backend.new_repo_name() + ".git"
373 repo_name = backend.new_repo_name() + ".git"
374 description = 'description for newly created repo'
374 description = 'description for newly created repo'
375 response = self.app.post(
375 response = self.app.post(
376 url('repos'),
376 url('repos'),
377 fixture._get_repo_create_params(
377 fixture._get_repo_create_params(
378 repo_private=False,
378 repo_private=False,
379 repo_name=repo_name,
379 repo_name=repo_name,
380 repo_type=backend.alias,
380 repo_type=backend.alias,
381 repo_description=description,
381 repo_description=description,
382 csrf_token=csrf_token))
382 csrf_token=csrf_token))
383 response.mustcontain('Repository name cannot end with .git')
383 response.mustcontain('Repository name cannot end with .git')
384
384
385 @pytest.mark.parametrize("suffix", [u'', u'Δ…Δ™Ε‚'], ids=['', 'non-ascii'])
386 def test_delete(self, autologin_user, backend, suffix, csrf_token):
387 repo = backend.create_repo(name_suffix=suffix)
388 repo_name = repo.repo_name
389
390 response = self.app.post(url('repo', repo_name=repo_name),
391 params={'_method': 'delete',
392 'csrf_token': csrf_token})
393 assert_session_flash(response, 'Deleted repository %s' % (repo_name))
394 response.follow()
395
396 # check if repo was deleted from db
397 assert RepoModel().get_by_repo_name(repo_name) is None
398 assert not repo_on_filesystem(repo_name)
399
400 def test_show(self, autologin_user, backend):
385 def test_show(self, autologin_user, backend):
401 self.app.get(url('repo', repo_name=backend.repo_name))
386 self.app.get(url('repo', repo_name=backend.repo_name))
402
387
403 def test_default_user_cannot_access_private_repo_in_a_group(
388 def test_default_user_cannot_access_private_repo_in_a_group(
404 self, autologin_user, user_util, backend, csrf_token):
389 self, autologin_user, user_util, backend, csrf_token):
405
390
406 group = user_util.create_repo_group()
391 group = user_util.create_repo_group()
407
392
408 repo = backend.create_repo(
393 repo = backend.create_repo(
409 repo_private=True, repo_group=group, repo_copy_permissions=True)
394 repo_private=True, repo_group=group, repo_copy_permissions=True)
410
395
411 permissions = _get_permission_for_user(
396 permissions = _get_permission_for_user(
412 user='default', repo=repo.repo_name)
397 user='default', repo=repo.repo_name)
413 assert len(permissions) == 1
398 assert len(permissions) == 1
414 assert permissions[0].permission.permission_name == 'repository.none'
399 assert permissions[0].permission.permission_name == 'repository.none'
415 assert permissions[0].repository.private is True
400 assert permissions[0].repository.private is True
416
401
417 def test_set_repo_fork_has_no_self_id(self, autologin_user, backend):
418 repo = backend.repo
419 response = self.app.get(
420 url('edit_repo_advanced', repo_name=backend.repo_name))
421 opt = """<option value="%s">vcs_test_git</option>""" % repo.repo_id
422 response.mustcontain(no=[opt])
423
424 def test_set_fork_of_target_repo(
425 self, autologin_user, backend, csrf_token):
426 target_repo = 'target_%s' % backend.alias
427 fixture.create_repo(target_repo, repo_type=backend.alias)
428 repo2 = Repository.get_by_repo_name(target_repo)
429 response = self.app.post(
430 url('edit_repo_advanced_fork', repo_name=backend.repo_name),
431 params={'id_fork_of': repo2.repo_id, '_method': 'put',
432 'csrf_token': csrf_token})
433 repo = Repository.get_by_repo_name(backend.repo_name)
434 repo2 = Repository.get_by_repo_name(target_repo)
435 assert_session_flash(
436 response,
437 'Marked repo %s as fork of %s' % (repo.repo_name, repo2.repo_name))
438
439 assert repo.fork == repo2
440 response = response.follow()
441 # check if given repo is selected
442
443 opt = 'This repository is a fork of <a href="%s">%s</a>' % (
444 url('summary_home', repo_name=repo2.repo_name), repo2.repo_name)
445
446 response.mustcontain(opt)
447
448 fixture.destroy_repo(target_repo, forks='detach')
449
450 @pytest.mark.backends("hg", "git")
451 def test_set_fork_of_other_type_repo(self, autologin_user, backend,
452 csrf_token):
453 TARGET_REPO_MAP = {
454 'git': {
455 'type': 'hg',
456 'repo_name': HG_REPO},
457 'hg': {
458 'type': 'git',
459 'repo_name': GIT_REPO},
460 }
461 target_repo = TARGET_REPO_MAP[backend.alias]
462
463 repo2 = Repository.get_by_repo_name(target_repo['repo_name'])
464 response = self.app.post(
465 url('edit_repo_advanced_fork', repo_name=backend.repo_name),
466 params={'id_fork_of': repo2.repo_id, '_method': 'put',
467 'csrf_token': csrf_token})
468 assert_session_flash(
469 response,
470 'Cannot set repository as fork of repository with other type')
471
472 def test_set_fork_of_none(self, autologin_user, backend, csrf_token):
473 # mark it as None
474 response = self.app.post(
475 url('edit_repo_advanced_fork', repo_name=backend.repo_name),
476 params={'id_fork_of': None, '_method': 'put',
477 'csrf_token': csrf_token})
478 assert_session_flash(
479 response,
480 'Marked repo %s as fork of %s'
481 % (backend.repo_name, "Nothing"))
482 assert backend.repo.fork is None
483
484 def test_set_fork_of_same_repo(self, autologin_user, backend, csrf_token):
485 repo = Repository.get_by_repo_name(backend.repo_name)
486 response = self.app.post(
487 url('edit_repo_advanced_fork', repo_name=backend.repo_name),
488 params={'id_fork_of': repo.repo_id, '_method': 'put',
489 'csrf_token': csrf_token})
490 assert_session_flash(
491 response, 'An error occurred during this operation')
492
493 def test_create_on_top_level_without_permissions(self, backend):
402 def test_create_on_top_level_without_permissions(self, backend):
494 session = login_user_session(
403 session = login_user_session(
495 self.app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
404 self.app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
496 csrf_token = auth.get_csrf_token(session)
405 csrf_token = auth.get_csrf_token(session)
497
406
498 # revoke
407 # revoke
499 user_model = UserModel()
408 user_model = UserModel()
500 # disable fork and create on default user
409 # disable fork and create on default user
501 user_model.revoke_perm(User.DEFAULT_USER, 'hg.create.repository')
410 user_model.revoke_perm(User.DEFAULT_USER, 'hg.create.repository')
502 user_model.grant_perm(User.DEFAULT_USER, 'hg.create.none')
411 user_model.grant_perm(User.DEFAULT_USER, 'hg.create.none')
503 user_model.revoke_perm(User.DEFAULT_USER, 'hg.fork.repository')
412 user_model.revoke_perm(User.DEFAULT_USER, 'hg.fork.repository')
504 user_model.grant_perm(User.DEFAULT_USER, 'hg.fork.none')
413 user_model.grant_perm(User.DEFAULT_USER, 'hg.fork.none')
505
414
506 # disable on regular user
415 # disable on regular user
507 user_model.revoke_perm(TEST_USER_REGULAR_LOGIN, 'hg.create.repository')
416 user_model.revoke_perm(TEST_USER_REGULAR_LOGIN, 'hg.create.repository')
508 user_model.grant_perm(TEST_USER_REGULAR_LOGIN, 'hg.create.none')
417 user_model.grant_perm(TEST_USER_REGULAR_LOGIN, 'hg.create.none')
509 user_model.revoke_perm(TEST_USER_REGULAR_LOGIN, 'hg.fork.repository')
418 user_model.revoke_perm(TEST_USER_REGULAR_LOGIN, 'hg.fork.repository')
510 user_model.grant_perm(TEST_USER_REGULAR_LOGIN, 'hg.fork.none')
419 user_model.grant_perm(TEST_USER_REGULAR_LOGIN, 'hg.fork.none')
511 Session().commit()
420 Session().commit()
512
421
513 repo_name = backend.new_repo_name()
422 repo_name = backend.new_repo_name()
514 description = 'description for newly created repo'
423 description = 'description for newly created repo'
515 response = self.app.post(
424 response = self.app.post(
516 url('repos'),
425 url('repos'),
517 fixture._get_repo_create_params(
426 fixture._get_repo_create_params(
518 repo_private=False,
427 repo_private=False,
519 repo_name=repo_name,
428 repo_name=repo_name,
520 repo_type=backend.alias,
429 repo_type=backend.alias,
521 repo_description=description,
430 repo_description=description,
522 csrf_token=csrf_token))
431 csrf_token=csrf_token))
523
432
524 response.mustcontain(
433 response.mustcontain(
525 u"You do not have the permission to store repositories in "
434 u"You do not have the permission to store repositories in "
526 u"the root location.")
435 u"the root location.")
527
436
528 @mock.patch.object(RepoModel, '_create_filesystem_repo', error_function)
437 @mock.patch.object(RepoModel, '_create_filesystem_repo', error_function)
529 def test_create_repo_when_filesystem_op_fails(
438 def test_create_repo_when_filesystem_op_fails(
530 self, autologin_user, backend, csrf_token):
439 self, autologin_user, backend, csrf_token):
531 repo_name = backend.new_repo_name()
440 repo_name = backend.new_repo_name()
532 description = 'description for newly created repo'
441 description = 'description for newly created repo'
533
442
534 response = self.app.post(
443 response = self.app.post(
535 url('repos'),
444 url('repos'),
536 fixture._get_repo_create_params(
445 fixture._get_repo_create_params(
537 repo_private=False,
446 repo_private=False,
538 repo_name=repo_name,
447 repo_name=repo_name,
539 repo_type=backend.alias,
448 repo_type=backend.alias,
540 repo_description=description,
449 repo_description=description,
541 csrf_token=csrf_token))
450 csrf_token=csrf_token))
542
451
543 assert_session_flash(
452 assert_session_flash(
544 response, 'Error creating repository %s' % repo_name)
453 response, 'Error creating repository %s' % repo_name)
545 # repo must not be in db
454 # repo must not be in db
546 assert backend.repo is None
455 assert backend.repo is None
547 # repo must not be in filesystem !
456 # repo must not be in filesystem !
548 assert not repo_on_filesystem(repo_name)
457 assert not repo_on_filesystem(repo_name)
549
458
550 def assert_repository_is_created_correctly(
459 def assert_repository_is_created_correctly(
551 self, repo_name, description, backend):
460 self, repo_name, description, backend):
552 repo_name_utf8 = safe_str(repo_name)
461 repo_name_utf8 = safe_str(repo_name)
553
462
554 # run the check page that triggers the flash message
463 # run the check page that triggers the flash message
555 response = self.app.get(url('repo_check_home', repo_name=repo_name))
464 response = self.app.get(url('repo_check_home', repo_name=repo_name))
556 assert response.json == {u'result': True}
465 assert response.json == {u'result': True}
557
466
558 flash_msg = u'Created repository <a href="/{}">{}</a>'.format(
467 flash_msg = u'Created repository <a href="/{}">{}</a>'.format(
559 urllib.quote(repo_name_utf8), repo_name)
468 urllib.quote(repo_name_utf8), repo_name)
560 assert_session_flash(response, flash_msg)
469 assert_session_flash(response, flash_msg)
561
470
562 # test if the repo was created in the database
471 # test if the repo was created in the database
563 new_repo = RepoModel().get_by_repo_name(repo_name)
472 new_repo = RepoModel().get_by_repo_name(repo_name)
564
473
565 assert new_repo.repo_name == repo_name
474 assert new_repo.repo_name == repo_name
566 assert new_repo.description == description
475 assert new_repo.description == description
567
476
568 # test if the repository is visible in the list ?
477 # test if the repository is visible in the list ?
569 response = self.app.get(url('summary_home', repo_name=repo_name))
478 response = self.app.get(url('summary_home', repo_name=repo_name))
570 response.mustcontain(repo_name)
479 response.mustcontain(repo_name)
571 response.mustcontain(backend.alias)
480 response.mustcontain(backend.alias)
572
481
573 assert repo_on_filesystem(repo_name)
482 assert repo_on_filesystem(repo_name)
574
483
575
484
576 @pytest.mark.usefixtures("app")
485 @pytest.mark.usefixtures("app")
577 class TestVcsSettings(object):
486 class TestVcsSettings(object):
578 FORM_DATA = {
487 FORM_DATA = {
579 'inherit_global_settings': False,
488 'inherit_global_settings': False,
580 'hooks_changegroup_repo_size': False,
489 'hooks_changegroup_repo_size': False,
581 'hooks_changegroup_push_logger': False,
490 'hooks_changegroup_push_logger': False,
582 'hooks_outgoing_pull_logger': False,
491 'hooks_outgoing_pull_logger': False,
583 'extensions_largefiles': False,
492 'extensions_largefiles': False,
584 'extensions_evolve': False,
493 'extensions_evolve': False,
585 'phases_publish': 'False',
494 'phases_publish': 'False',
586 'rhodecode_pr_merge_enabled': False,
495 'rhodecode_pr_merge_enabled': False,
587 'rhodecode_use_outdated_comments': False,
496 'rhodecode_use_outdated_comments': False,
588 'new_svn_branch': '',
497 'new_svn_branch': '',
589 'new_svn_tag': ''
498 'new_svn_tag': ''
590 }
499 }
591
500
592 @pytest.mark.skip_backends('svn')
501 @pytest.mark.skip_backends('svn')
593 def test_global_settings_initial_values(self, autologin_user, backend):
502 def test_global_settings_initial_values(self, autologin_user, backend):
594 repo_name = backend.repo_name
503 repo_name = backend.repo_name
595 response = self.app.get(url('repo_vcs_settings', repo_name=repo_name))
504 response = self.app.get(url('repo_vcs_settings', repo_name=repo_name))
596
505
597 expected_settings = (
506 expected_settings = (
598 'rhodecode_use_outdated_comments', 'rhodecode_pr_merge_enabled',
507 'rhodecode_use_outdated_comments', 'rhodecode_pr_merge_enabled',
599 'hooks_changegroup_repo_size', 'hooks_changegroup_push_logger',
508 'hooks_changegroup_repo_size', 'hooks_changegroup_push_logger',
600 'hooks_outgoing_pull_logger'
509 'hooks_outgoing_pull_logger'
601 )
510 )
602 for setting in expected_settings:
511 for setting in expected_settings:
603 self.assert_repo_value_equals_global_value(response, setting)
512 self.assert_repo_value_equals_global_value(response, setting)
604
513
605 def test_show_settings_requires_repo_admin_permission(
514 def test_show_settings_requires_repo_admin_permission(
606 self, backend, user_util, settings_util):
515 self, backend, user_util, settings_util):
607 repo = backend.create_repo()
516 repo = backend.create_repo()
608 repo_name = repo.repo_name
517 repo_name = repo.repo_name
609 user = UserModel().get_by_username(TEST_USER_REGULAR_LOGIN)
518 user = UserModel().get_by_username(TEST_USER_REGULAR_LOGIN)
610 user_util.grant_user_permission_to_repo(repo, user, 'repository.admin')
519 user_util.grant_user_permission_to_repo(repo, user, 'repository.admin')
611 login_user_session(
520 login_user_session(
612 self.app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
521 self.app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
613 self.app.get(url('repo_vcs_settings', repo_name=repo_name), status=200)
522 self.app.get(url('repo_vcs_settings', repo_name=repo_name), status=200)
614
523
615 def test_inherit_global_settings_flag_is_true_by_default(
524 def test_inherit_global_settings_flag_is_true_by_default(
616 self, autologin_user, backend):
525 self, autologin_user, backend):
617 repo_name = backend.repo_name
526 repo_name = backend.repo_name
618 response = self.app.get(url('repo_vcs_settings', repo_name=repo_name))
527 response = self.app.get(url('repo_vcs_settings', repo_name=repo_name))
619
528
620 assert_response = AssertResponse(response)
529 assert_response = AssertResponse(response)
621 element = assert_response.get_element('#inherit_global_settings')
530 element = assert_response.get_element('#inherit_global_settings')
622 assert element.checked
531 assert element.checked
623
532
624 @pytest.mark.parametrize('checked_value', [True, False])
533 @pytest.mark.parametrize('checked_value', [True, False])
625 def test_inherit_global_settings_value(
534 def test_inherit_global_settings_value(
626 self, autologin_user, backend, checked_value, settings_util):
535 self, autologin_user, backend, checked_value, settings_util):
627 repo = backend.create_repo()
536 repo = backend.create_repo()
628 repo_name = repo.repo_name
537 repo_name = repo.repo_name
629 settings_util.create_repo_rhodecode_setting(
538 settings_util.create_repo_rhodecode_setting(
630 repo, 'inherit_vcs_settings', checked_value, 'bool')
539 repo, 'inherit_vcs_settings', checked_value, 'bool')
631 response = self.app.get(url('repo_vcs_settings', repo_name=repo_name))
540 response = self.app.get(url('repo_vcs_settings', repo_name=repo_name))
632
541
633 assert_response = AssertResponse(response)
542 assert_response = AssertResponse(response)
634 element = assert_response.get_element('#inherit_global_settings')
543 element = assert_response.get_element('#inherit_global_settings')
635 assert element.checked == checked_value
544 assert element.checked == checked_value
636
545
637 @pytest.mark.skip_backends('svn')
546 @pytest.mark.skip_backends('svn')
638 def test_hooks_settings_are_created(
547 def test_hooks_settings_are_created(
639 self, autologin_user, backend, csrf_token):
548 self, autologin_user, backend, csrf_token):
640 repo_name = backend.repo_name
549 repo_name = backend.repo_name
641 data = self.FORM_DATA.copy()
550 data = self.FORM_DATA.copy()
642 data['csrf_token'] = csrf_token
551 data['csrf_token'] = csrf_token
643 self.app.post(
552 self.app.post(
644 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
553 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
645 settings = SettingsModel(repo=repo_name)
554 settings = SettingsModel(repo=repo_name)
646 try:
555 try:
647 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
556 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
648 ui = settings.get_ui_by_section_and_key(section, key)
557 ui = settings.get_ui_by_section_and_key(section, key)
649 assert ui.ui_active is False
558 assert ui.ui_active is False
650 finally:
559 finally:
651 self._cleanup_repo_settings(settings)
560 self._cleanup_repo_settings(settings)
652
561
653 def test_hooks_settings_are_not_created_for_svn(
562 def test_hooks_settings_are_not_created_for_svn(
654 self, autologin_user, backend_svn, csrf_token):
563 self, autologin_user, backend_svn, csrf_token):
655 repo_name = backend_svn.repo_name
564 repo_name = backend_svn.repo_name
656 data = self.FORM_DATA.copy()
565 data = self.FORM_DATA.copy()
657 data['csrf_token'] = csrf_token
566 data['csrf_token'] = csrf_token
658 self.app.post(
567 self.app.post(
659 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
568 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
660 settings = SettingsModel(repo=repo_name)
569 settings = SettingsModel(repo=repo_name)
661 try:
570 try:
662 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
571 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
663 ui = settings.get_ui_by_section_and_key(section, key)
572 ui = settings.get_ui_by_section_and_key(section, key)
664 assert ui is None
573 assert ui is None
665 finally:
574 finally:
666 self._cleanup_repo_settings(settings)
575 self._cleanup_repo_settings(settings)
667
576
668 @pytest.mark.skip_backends('svn')
577 @pytest.mark.skip_backends('svn')
669 def test_hooks_settings_are_updated(
578 def test_hooks_settings_are_updated(
670 self, autologin_user, backend, csrf_token):
579 self, autologin_user, backend, csrf_token):
671 repo_name = backend.repo_name
580 repo_name = backend.repo_name
672 settings = SettingsModel(repo=repo_name)
581 settings = SettingsModel(repo=repo_name)
673 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
582 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
674 settings.create_ui_section_value(section, '', key=key, active=True)
583 settings.create_ui_section_value(section, '', key=key, active=True)
675
584
676 data = self.FORM_DATA.copy()
585 data = self.FORM_DATA.copy()
677 data['csrf_token'] = csrf_token
586 data['csrf_token'] = csrf_token
678 self.app.post(
587 self.app.post(
679 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
588 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
680 try:
589 try:
681 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
590 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
682 ui = settings.get_ui_by_section_and_key(section, key)
591 ui = settings.get_ui_by_section_and_key(section, key)
683 assert ui.ui_active is False
592 assert ui.ui_active is False
684 finally:
593 finally:
685 self._cleanup_repo_settings(settings)
594 self._cleanup_repo_settings(settings)
686
595
687 def test_hooks_settings_are_not_updated_for_svn(
596 def test_hooks_settings_are_not_updated_for_svn(
688 self, autologin_user, backend_svn, csrf_token):
597 self, autologin_user, backend_svn, csrf_token):
689 repo_name = backend_svn.repo_name
598 repo_name = backend_svn.repo_name
690 settings = SettingsModel(repo=repo_name)
599 settings = SettingsModel(repo=repo_name)
691 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
600 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
692 settings.create_ui_section_value(section, '', key=key, active=True)
601 settings.create_ui_section_value(section, '', key=key, active=True)
693
602
694 data = self.FORM_DATA.copy()
603 data = self.FORM_DATA.copy()
695 data['csrf_token'] = csrf_token
604 data['csrf_token'] = csrf_token
696 self.app.post(
605 self.app.post(
697 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
606 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
698 try:
607 try:
699 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
608 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
700 ui = settings.get_ui_by_section_and_key(section, key)
609 ui = settings.get_ui_by_section_and_key(section, key)
701 assert ui.ui_active is True
610 assert ui.ui_active is True
702 finally:
611 finally:
703 self._cleanup_repo_settings(settings)
612 self._cleanup_repo_settings(settings)
704
613
705 @pytest.mark.skip_backends('svn')
614 @pytest.mark.skip_backends('svn')
706 def test_pr_settings_are_created(
615 def test_pr_settings_are_created(
707 self, autologin_user, backend, csrf_token):
616 self, autologin_user, backend, csrf_token):
708 repo_name = backend.repo_name
617 repo_name = backend.repo_name
709 data = self.FORM_DATA.copy()
618 data = self.FORM_DATA.copy()
710 data['csrf_token'] = csrf_token
619 data['csrf_token'] = csrf_token
711 self.app.post(
620 self.app.post(
712 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
621 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
713 settings = SettingsModel(repo=repo_name)
622 settings = SettingsModel(repo=repo_name)
714 try:
623 try:
715 for name in VcsSettingsModel.GENERAL_SETTINGS:
624 for name in VcsSettingsModel.GENERAL_SETTINGS:
716 setting = settings.get_setting_by_name(name)
625 setting = settings.get_setting_by_name(name)
717 assert setting.app_settings_value is False
626 assert setting.app_settings_value is False
718 finally:
627 finally:
719 self._cleanup_repo_settings(settings)
628 self._cleanup_repo_settings(settings)
720
629
721 def test_pr_settings_are_not_created_for_svn(
630 def test_pr_settings_are_not_created_for_svn(
722 self, autologin_user, backend_svn, csrf_token):
631 self, autologin_user, backend_svn, csrf_token):
723 repo_name = backend_svn.repo_name
632 repo_name = backend_svn.repo_name
724 data = self.FORM_DATA.copy()
633 data = self.FORM_DATA.copy()
725 data['csrf_token'] = csrf_token
634 data['csrf_token'] = csrf_token
726 self.app.post(
635 self.app.post(
727 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
636 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
728 settings = SettingsModel(repo=repo_name)
637 settings = SettingsModel(repo=repo_name)
729 try:
638 try:
730 for name in VcsSettingsModel.GENERAL_SETTINGS:
639 for name in VcsSettingsModel.GENERAL_SETTINGS:
731 setting = settings.get_setting_by_name(name)
640 setting = settings.get_setting_by_name(name)
732 assert setting is None
641 assert setting is None
733 finally:
642 finally:
734 self._cleanup_repo_settings(settings)
643 self._cleanup_repo_settings(settings)
735
644
736 def test_pr_settings_creation_requires_repo_admin_permission(
645 def test_pr_settings_creation_requires_repo_admin_permission(
737 self, backend, user_util, settings_util, csrf_token):
646 self, backend, user_util, settings_util, csrf_token):
738 repo = backend.create_repo()
647 repo = backend.create_repo()
739 repo_name = repo.repo_name
648 repo_name = repo.repo_name
740
649
741 logout_user_session(self.app, csrf_token)
650 logout_user_session(self.app, csrf_token)
742 session = login_user_session(
651 session = login_user_session(
743 self.app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
652 self.app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
744 new_csrf_token = auth.get_csrf_token(session)
653 new_csrf_token = auth.get_csrf_token(session)
745
654
746 user = UserModel().get_by_username(TEST_USER_REGULAR_LOGIN)
655 user = UserModel().get_by_username(TEST_USER_REGULAR_LOGIN)
747 repo = Repository.get_by_repo_name(repo_name)
656 repo = Repository.get_by_repo_name(repo_name)
748 user_util.grant_user_permission_to_repo(repo, user, 'repository.admin')
657 user_util.grant_user_permission_to_repo(repo, user, 'repository.admin')
749 data = self.FORM_DATA.copy()
658 data = self.FORM_DATA.copy()
750 data['csrf_token'] = new_csrf_token
659 data['csrf_token'] = new_csrf_token
751 settings = SettingsModel(repo=repo_name)
660 settings = SettingsModel(repo=repo_name)
752
661
753 try:
662 try:
754 self.app.post(
663 self.app.post(
755 url('repo_vcs_settings', repo_name=repo_name), data,
664 url('repo_vcs_settings', repo_name=repo_name), data,
756 status=302)
665 status=302)
757 finally:
666 finally:
758 self._cleanup_repo_settings(settings)
667 self._cleanup_repo_settings(settings)
759
668
760 @pytest.mark.skip_backends('svn')
669 @pytest.mark.skip_backends('svn')
761 def test_pr_settings_are_updated(
670 def test_pr_settings_are_updated(
762 self, autologin_user, backend, csrf_token):
671 self, autologin_user, backend, csrf_token):
763 repo_name = backend.repo_name
672 repo_name = backend.repo_name
764 settings = SettingsModel(repo=repo_name)
673 settings = SettingsModel(repo=repo_name)
765 for name in VcsSettingsModel.GENERAL_SETTINGS:
674 for name in VcsSettingsModel.GENERAL_SETTINGS:
766 settings.create_or_update_setting(name, True, 'bool')
675 settings.create_or_update_setting(name, True, 'bool')
767
676
768 data = self.FORM_DATA.copy()
677 data = self.FORM_DATA.copy()
769 data['csrf_token'] = csrf_token
678 data['csrf_token'] = csrf_token
770 self.app.post(
679 self.app.post(
771 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
680 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
772 try:
681 try:
773 for name in VcsSettingsModel.GENERAL_SETTINGS:
682 for name in VcsSettingsModel.GENERAL_SETTINGS:
774 setting = settings.get_setting_by_name(name)
683 setting = settings.get_setting_by_name(name)
775 assert setting.app_settings_value is False
684 assert setting.app_settings_value is False
776 finally:
685 finally:
777 self._cleanup_repo_settings(settings)
686 self._cleanup_repo_settings(settings)
778
687
779 def test_pr_settings_are_not_updated_for_svn(
688 def test_pr_settings_are_not_updated_for_svn(
780 self, autologin_user, backend_svn, csrf_token):
689 self, autologin_user, backend_svn, csrf_token):
781 repo_name = backend_svn.repo_name
690 repo_name = backend_svn.repo_name
782 settings = SettingsModel(repo=repo_name)
691 settings = SettingsModel(repo=repo_name)
783 for name in VcsSettingsModel.GENERAL_SETTINGS:
692 for name in VcsSettingsModel.GENERAL_SETTINGS:
784 settings.create_or_update_setting(name, True, 'bool')
693 settings.create_or_update_setting(name, True, 'bool')
785
694
786 data = self.FORM_DATA.copy()
695 data = self.FORM_DATA.copy()
787 data['csrf_token'] = csrf_token
696 data['csrf_token'] = csrf_token
788 self.app.post(
697 self.app.post(
789 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
698 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
790 try:
699 try:
791 for name in VcsSettingsModel.GENERAL_SETTINGS:
700 for name in VcsSettingsModel.GENERAL_SETTINGS:
792 setting = settings.get_setting_by_name(name)
701 setting = settings.get_setting_by_name(name)
793 assert setting.app_settings_value is True
702 assert setting.app_settings_value is True
794 finally:
703 finally:
795 self._cleanup_repo_settings(settings)
704 self._cleanup_repo_settings(settings)
796
705
797 def test_svn_settings_are_created(
706 def test_svn_settings_are_created(
798 self, autologin_user, backend_svn, csrf_token, settings_util):
707 self, autologin_user, backend_svn, csrf_token, settings_util):
799 repo_name = backend_svn.repo_name
708 repo_name = backend_svn.repo_name
800 data = self.FORM_DATA.copy()
709 data = self.FORM_DATA.copy()
801 data['new_svn_tag'] = 'svn-tag'
710 data['new_svn_tag'] = 'svn-tag'
802 data['new_svn_branch'] = 'svn-branch'
711 data['new_svn_branch'] = 'svn-branch'
803 data['csrf_token'] = csrf_token
712 data['csrf_token'] = csrf_token
804
713
805 # Create few global settings to make sure that uniqueness validators
714 # Create few global settings to make sure that uniqueness validators
806 # are not triggered
715 # are not triggered
807 settings_util.create_rhodecode_ui(
716 settings_util.create_rhodecode_ui(
808 VcsSettingsModel.SVN_BRANCH_SECTION, 'svn-branch')
717 VcsSettingsModel.SVN_BRANCH_SECTION, 'svn-branch')
809 settings_util.create_rhodecode_ui(
718 settings_util.create_rhodecode_ui(
810 VcsSettingsModel.SVN_TAG_SECTION, 'svn-tag')
719 VcsSettingsModel.SVN_TAG_SECTION, 'svn-tag')
811
720
812 self.app.post(
721 self.app.post(
813 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
722 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
814 settings = SettingsModel(repo=repo_name)
723 settings = SettingsModel(repo=repo_name)
815 try:
724 try:
816 svn_branches = settings.get_ui_by_section(
725 svn_branches = settings.get_ui_by_section(
817 VcsSettingsModel.SVN_BRANCH_SECTION)
726 VcsSettingsModel.SVN_BRANCH_SECTION)
818 svn_branch_names = [b.ui_value for b in svn_branches]
727 svn_branch_names = [b.ui_value for b in svn_branches]
819 svn_tags = settings.get_ui_by_section(
728 svn_tags = settings.get_ui_by_section(
820 VcsSettingsModel.SVN_TAG_SECTION)
729 VcsSettingsModel.SVN_TAG_SECTION)
821 svn_tag_names = [b.ui_value for b in svn_tags]
730 svn_tag_names = [b.ui_value for b in svn_tags]
822 assert 'svn-branch' in svn_branch_names
731 assert 'svn-branch' in svn_branch_names
823 assert 'svn-tag' in svn_tag_names
732 assert 'svn-tag' in svn_tag_names
824 finally:
733 finally:
825 self._cleanup_repo_settings(settings)
734 self._cleanup_repo_settings(settings)
826
735
827 def test_svn_settings_are_unique(
736 def test_svn_settings_are_unique(
828 self, autologin_user, backend_svn, csrf_token, settings_util):
737 self, autologin_user, backend_svn, csrf_token, settings_util):
829 repo = backend_svn.repo
738 repo = backend_svn.repo
830 repo_name = repo.repo_name
739 repo_name = repo.repo_name
831 data = self.FORM_DATA.copy()
740 data = self.FORM_DATA.copy()
832 data['new_svn_tag'] = 'test_tag'
741 data['new_svn_tag'] = 'test_tag'
833 data['new_svn_branch'] = 'test_branch'
742 data['new_svn_branch'] = 'test_branch'
834 data['csrf_token'] = csrf_token
743 data['csrf_token'] = csrf_token
835 settings_util.create_repo_rhodecode_ui(
744 settings_util.create_repo_rhodecode_ui(
836 repo, VcsSettingsModel.SVN_BRANCH_SECTION, 'test_branch')
745 repo, VcsSettingsModel.SVN_BRANCH_SECTION, 'test_branch')
837 settings_util.create_repo_rhodecode_ui(
746 settings_util.create_repo_rhodecode_ui(
838 repo, VcsSettingsModel.SVN_TAG_SECTION, 'test_tag')
747 repo, VcsSettingsModel.SVN_TAG_SECTION, 'test_tag')
839
748
840 response = self.app.post(
749 response = self.app.post(
841 url('repo_vcs_settings', repo_name=repo_name), data, status=200)
750 url('repo_vcs_settings', repo_name=repo_name), data, status=200)
842 response.mustcontain('Pattern already exists')
751 response.mustcontain('Pattern already exists')
843
752
844 def test_svn_settings_with_empty_values_are_not_created(
753 def test_svn_settings_with_empty_values_are_not_created(
845 self, autologin_user, backend_svn, csrf_token):
754 self, autologin_user, backend_svn, csrf_token):
846 repo_name = backend_svn.repo_name
755 repo_name = backend_svn.repo_name
847 data = self.FORM_DATA.copy()
756 data = self.FORM_DATA.copy()
848 data['csrf_token'] = csrf_token
757 data['csrf_token'] = csrf_token
849 self.app.post(
758 self.app.post(
850 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
759 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
851 settings = SettingsModel(repo=repo_name)
760 settings = SettingsModel(repo=repo_name)
852 try:
761 try:
853 svn_branches = settings.get_ui_by_section(
762 svn_branches = settings.get_ui_by_section(
854 VcsSettingsModel.SVN_BRANCH_SECTION)
763 VcsSettingsModel.SVN_BRANCH_SECTION)
855 svn_tags = settings.get_ui_by_section(
764 svn_tags = settings.get_ui_by_section(
856 VcsSettingsModel.SVN_TAG_SECTION)
765 VcsSettingsModel.SVN_TAG_SECTION)
857 assert len(svn_branches) == 0
766 assert len(svn_branches) == 0
858 assert len(svn_tags) == 0
767 assert len(svn_tags) == 0
859 finally:
768 finally:
860 self._cleanup_repo_settings(settings)
769 self._cleanup_repo_settings(settings)
861
770
862 def test_svn_settings_are_shown_for_svn_repository(
771 def test_svn_settings_are_shown_for_svn_repository(
863 self, autologin_user, backend_svn, csrf_token):
772 self, autologin_user, backend_svn, csrf_token):
864 repo_name = backend_svn.repo_name
773 repo_name = backend_svn.repo_name
865 response = self.app.get(
774 response = self.app.get(
866 url('repo_vcs_settings', repo_name=repo_name), status=200)
775 url('repo_vcs_settings', repo_name=repo_name), status=200)
867 response.mustcontain('Subversion Settings')
776 response.mustcontain('Subversion Settings')
868
777
869 @pytest.mark.skip_backends('svn')
778 @pytest.mark.skip_backends('svn')
870 def test_svn_settings_are_not_created_for_not_svn_repository(
779 def test_svn_settings_are_not_created_for_not_svn_repository(
871 self, autologin_user, backend, csrf_token):
780 self, autologin_user, backend, csrf_token):
872 repo_name = backend.repo_name
781 repo_name = backend.repo_name
873 data = self.FORM_DATA.copy()
782 data = self.FORM_DATA.copy()
874 data['csrf_token'] = csrf_token
783 data['csrf_token'] = csrf_token
875 self.app.post(
784 self.app.post(
876 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
785 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
877 settings = SettingsModel(repo=repo_name)
786 settings = SettingsModel(repo=repo_name)
878 try:
787 try:
879 svn_branches = settings.get_ui_by_section(
788 svn_branches = settings.get_ui_by_section(
880 VcsSettingsModel.SVN_BRANCH_SECTION)
789 VcsSettingsModel.SVN_BRANCH_SECTION)
881 svn_tags = settings.get_ui_by_section(
790 svn_tags = settings.get_ui_by_section(
882 VcsSettingsModel.SVN_TAG_SECTION)
791 VcsSettingsModel.SVN_TAG_SECTION)
883 assert len(svn_branches) == 0
792 assert len(svn_branches) == 0
884 assert len(svn_tags) == 0
793 assert len(svn_tags) == 0
885 finally:
794 finally:
886 self._cleanup_repo_settings(settings)
795 self._cleanup_repo_settings(settings)
887
796
888 @pytest.mark.skip_backends('svn')
797 @pytest.mark.skip_backends('svn')
889 def test_svn_settings_are_shown_only_for_svn_repository(
798 def test_svn_settings_are_shown_only_for_svn_repository(
890 self, autologin_user, backend, csrf_token):
799 self, autologin_user, backend, csrf_token):
891 repo_name = backend.repo_name
800 repo_name = backend.repo_name
892 response = self.app.get(
801 response = self.app.get(
893 url('repo_vcs_settings', repo_name=repo_name), status=200)
802 url('repo_vcs_settings', repo_name=repo_name), status=200)
894 response.mustcontain(no='Subversion Settings')
803 response.mustcontain(no='Subversion Settings')
895
804
896 def test_hg_settings_are_created(
805 def test_hg_settings_are_created(
897 self, autologin_user, backend_hg, csrf_token):
806 self, autologin_user, backend_hg, csrf_token):
898 repo_name = backend_hg.repo_name
807 repo_name = backend_hg.repo_name
899 data = self.FORM_DATA.copy()
808 data = self.FORM_DATA.copy()
900 data['new_svn_tag'] = 'svn-tag'
809 data['new_svn_tag'] = 'svn-tag'
901 data['new_svn_branch'] = 'svn-branch'
810 data['new_svn_branch'] = 'svn-branch'
902 data['csrf_token'] = csrf_token
811 data['csrf_token'] = csrf_token
903 self.app.post(
812 self.app.post(
904 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
813 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
905 settings = SettingsModel(repo=repo_name)
814 settings = SettingsModel(repo=repo_name)
906 try:
815 try:
907 largefiles_ui = settings.get_ui_by_section_and_key(
816 largefiles_ui = settings.get_ui_by_section_and_key(
908 'extensions', 'largefiles')
817 'extensions', 'largefiles')
909 assert largefiles_ui.ui_active is False
818 assert largefiles_ui.ui_active is False
910 phases_ui = settings.get_ui_by_section_and_key(
819 phases_ui = settings.get_ui_by_section_and_key(
911 'phases', 'publish')
820 'phases', 'publish')
912 assert str2bool(phases_ui.ui_value) is False
821 assert str2bool(phases_ui.ui_value) is False
913 finally:
822 finally:
914 self._cleanup_repo_settings(settings)
823 self._cleanup_repo_settings(settings)
915
824
916 def test_hg_settings_are_updated(
825 def test_hg_settings_are_updated(
917 self, autologin_user, backend_hg, csrf_token):
826 self, autologin_user, backend_hg, csrf_token):
918 repo_name = backend_hg.repo_name
827 repo_name = backend_hg.repo_name
919 settings = SettingsModel(repo=repo_name)
828 settings = SettingsModel(repo=repo_name)
920 settings.create_ui_section_value(
829 settings.create_ui_section_value(
921 'extensions', '', key='largefiles', active=True)
830 'extensions', '', key='largefiles', active=True)
922 settings.create_ui_section_value(
831 settings.create_ui_section_value(
923 'phases', '1', key='publish', active=True)
832 'phases', '1', key='publish', active=True)
924
833
925 data = self.FORM_DATA.copy()
834 data = self.FORM_DATA.copy()
926 data['csrf_token'] = csrf_token
835 data['csrf_token'] = csrf_token
927 self.app.post(
836 self.app.post(
928 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
837 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
929 try:
838 try:
930 largefiles_ui = settings.get_ui_by_section_and_key(
839 largefiles_ui = settings.get_ui_by_section_and_key(
931 'extensions', 'largefiles')
840 'extensions', 'largefiles')
932 assert largefiles_ui.ui_active is False
841 assert largefiles_ui.ui_active is False
933 phases_ui = settings.get_ui_by_section_and_key(
842 phases_ui = settings.get_ui_by_section_and_key(
934 'phases', 'publish')
843 'phases', 'publish')
935 assert str2bool(phases_ui.ui_value) is False
844 assert str2bool(phases_ui.ui_value) is False
936 finally:
845 finally:
937 self._cleanup_repo_settings(settings)
846 self._cleanup_repo_settings(settings)
938
847
939 def test_hg_settings_are_shown_for_hg_repository(
848 def test_hg_settings_are_shown_for_hg_repository(
940 self, autologin_user, backend_hg, csrf_token):
849 self, autologin_user, backend_hg, csrf_token):
941 repo_name = backend_hg.repo_name
850 repo_name = backend_hg.repo_name
942 response = self.app.get(
851 response = self.app.get(
943 url('repo_vcs_settings', repo_name=repo_name), status=200)
852 url('repo_vcs_settings', repo_name=repo_name), status=200)
944 response.mustcontain('Mercurial Settings')
853 response.mustcontain('Mercurial Settings')
945
854
946 @pytest.mark.skip_backends('hg')
855 @pytest.mark.skip_backends('hg')
947 def test_hg_settings_are_created_only_for_hg_repository(
856 def test_hg_settings_are_created_only_for_hg_repository(
948 self, autologin_user, backend, csrf_token):
857 self, autologin_user, backend, csrf_token):
949 repo_name = backend.repo_name
858 repo_name = backend.repo_name
950 data = self.FORM_DATA.copy()
859 data = self.FORM_DATA.copy()
951 data['csrf_token'] = csrf_token
860 data['csrf_token'] = csrf_token
952 self.app.post(
861 self.app.post(
953 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
862 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
954 settings = SettingsModel(repo=repo_name)
863 settings = SettingsModel(repo=repo_name)
955 try:
864 try:
956 largefiles_ui = settings.get_ui_by_section_and_key(
865 largefiles_ui = settings.get_ui_by_section_and_key(
957 'extensions', 'largefiles')
866 'extensions', 'largefiles')
958 assert largefiles_ui is None
867 assert largefiles_ui is None
959 phases_ui = settings.get_ui_by_section_and_key(
868 phases_ui = settings.get_ui_by_section_and_key(
960 'phases', 'publish')
869 'phases', 'publish')
961 assert phases_ui is None
870 assert phases_ui is None
962 finally:
871 finally:
963 self._cleanup_repo_settings(settings)
872 self._cleanup_repo_settings(settings)
964
873
965 @pytest.mark.skip_backends('hg')
874 @pytest.mark.skip_backends('hg')
966 def test_hg_settings_are_shown_only_for_hg_repository(
875 def test_hg_settings_are_shown_only_for_hg_repository(
967 self, autologin_user, backend, csrf_token):
876 self, autologin_user, backend, csrf_token):
968 repo_name = backend.repo_name
877 repo_name = backend.repo_name
969 response = self.app.get(
878 response = self.app.get(
970 url('repo_vcs_settings', repo_name=repo_name), status=200)
879 url('repo_vcs_settings', repo_name=repo_name), status=200)
971 response.mustcontain(no='Mercurial Settings')
880 response.mustcontain(no='Mercurial Settings')
972
881
973 @pytest.mark.skip_backends('hg')
882 @pytest.mark.skip_backends('hg')
974 def test_hg_settings_are_updated_only_for_hg_repository(
883 def test_hg_settings_are_updated_only_for_hg_repository(
975 self, autologin_user, backend, csrf_token):
884 self, autologin_user, backend, csrf_token):
976 repo_name = backend.repo_name
885 repo_name = backend.repo_name
977 settings = SettingsModel(repo=repo_name)
886 settings = SettingsModel(repo=repo_name)
978 settings.create_ui_section_value(
887 settings.create_ui_section_value(
979 'extensions', '', key='largefiles', active=True)
888 'extensions', '', key='largefiles', active=True)
980 settings.create_ui_section_value(
889 settings.create_ui_section_value(
981 'phases', '1', key='publish', active=True)
890 'phases', '1', key='publish', active=True)
982
891
983 data = self.FORM_DATA.copy()
892 data = self.FORM_DATA.copy()
984 data['csrf_token'] = csrf_token
893 data['csrf_token'] = csrf_token
985 self.app.post(
894 self.app.post(
986 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
895 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
987 try:
896 try:
988 largefiles_ui = settings.get_ui_by_section_and_key(
897 largefiles_ui = settings.get_ui_by_section_and_key(
989 'extensions', 'largefiles')
898 'extensions', 'largefiles')
990 assert largefiles_ui.ui_active is True
899 assert largefiles_ui.ui_active is True
991 phases_ui = settings.get_ui_by_section_and_key(
900 phases_ui = settings.get_ui_by_section_and_key(
992 'phases', 'publish')
901 'phases', 'publish')
993 assert phases_ui.ui_value == '1'
902 assert phases_ui.ui_value == '1'
994 finally:
903 finally:
995 self._cleanup_repo_settings(settings)
904 self._cleanup_repo_settings(settings)
996
905
997 def test_per_repo_svn_settings_are_displayed(
906 def test_per_repo_svn_settings_are_displayed(
998 self, autologin_user, backend_svn, settings_util):
907 self, autologin_user, backend_svn, settings_util):
999 repo = backend_svn.create_repo()
908 repo = backend_svn.create_repo()
1000 repo_name = repo.repo_name
909 repo_name = repo.repo_name
1001 branches = [
910 branches = [
1002 settings_util.create_repo_rhodecode_ui(
911 settings_util.create_repo_rhodecode_ui(
1003 repo, VcsSettingsModel.SVN_BRANCH_SECTION,
912 repo, VcsSettingsModel.SVN_BRANCH_SECTION,
1004 'branch_{}'.format(i))
913 'branch_{}'.format(i))
1005 for i in range(10)]
914 for i in range(10)]
1006 tags = [
915 tags = [
1007 settings_util.create_repo_rhodecode_ui(
916 settings_util.create_repo_rhodecode_ui(
1008 repo, VcsSettingsModel.SVN_TAG_SECTION, 'tag_{}'.format(i))
917 repo, VcsSettingsModel.SVN_TAG_SECTION, 'tag_{}'.format(i))
1009 for i in range(10)]
918 for i in range(10)]
1010
919
1011 response = self.app.get(
920 response = self.app.get(
1012 url('repo_vcs_settings', repo_name=repo_name), status=200)
921 url('repo_vcs_settings', repo_name=repo_name), status=200)
1013 assert_response = AssertResponse(response)
922 assert_response = AssertResponse(response)
1014 for branch in branches:
923 for branch in branches:
1015 css_selector = '[name=branch_value_{}]'.format(branch.ui_id)
924 css_selector = '[name=branch_value_{}]'.format(branch.ui_id)
1016 element = assert_response.get_element(css_selector)
925 element = assert_response.get_element(css_selector)
1017 assert element.value == branch.ui_value
926 assert element.value == branch.ui_value
1018 for tag in tags:
927 for tag in tags:
1019 css_selector = '[name=tag_ui_value_new_{}]'.format(tag.ui_id)
928 css_selector = '[name=tag_ui_value_new_{}]'.format(tag.ui_id)
1020 element = assert_response.get_element(css_selector)
929 element = assert_response.get_element(css_selector)
1021 assert element.value == tag.ui_value
930 assert element.value == tag.ui_value
1022
931
1023 def test_per_repo_hg_and_pr_settings_are_not_displayed_for_svn(
932 def test_per_repo_hg_and_pr_settings_are_not_displayed_for_svn(
1024 self, autologin_user, backend_svn, settings_util):
933 self, autologin_user, backend_svn, settings_util):
1025 repo = backend_svn.create_repo()
934 repo = backend_svn.create_repo()
1026 repo_name = repo.repo_name
935 repo_name = repo.repo_name
1027 response = self.app.get(
936 response = self.app.get(
1028 url('repo_vcs_settings', repo_name=repo_name), status=200)
937 url('repo_vcs_settings', repo_name=repo_name), status=200)
1029 response.mustcontain(no='<label>Hooks:</label>')
938 response.mustcontain(no='<label>Hooks:</label>')
1030 response.mustcontain(no='<label>Pull Request Settings:</label>')
939 response.mustcontain(no='<label>Pull Request Settings:</label>')
1031
940
1032 def test_inherit_global_settings_value_is_saved(
941 def test_inherit_global_settings_value_is_saved(
1033 self, autologin_user, backend, csrf_token):
942 self, autologin_user, backend, csrf_token):
1034 repo_name = backend.repo_name
943 repo_name = backend.repo_name
1035 data = self.FORM_DATA.copy()
944 data = self.FORM_DATA.copy()
1036 data['csrf_token'] = csrf_token
945 data['csrf_token'] = csrf_token
1037 data['inherit_global_settings'] = True
946 data['inherit_global_settings'] = True
1038 self.app.post(
947 self.app.post(
1039 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
948 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
1040
949
1041 settings = SettingsModel(repo=repo_name)
950 settings = SettingsModel(repo=repo_name)
1042 vcs_settings = VcsSettingsModel(repo=repo_name)
951 vcs_settings = VcsSettingsModel(repo=repo_name)
1043 try:
952 try:
1044 assert vcs_settings.inherit_global_settings is True
953 assert vcs_settings.inherit_global_settings is True
1045 finally:
954 finally:
1046 self._cleanup_repo_settings(settings)
955 self._cleanup_repo_settings(settings)
1047
956
1048 def test_repo_cache_is_invalidated_when_settings_are_updated(
957 def test_repo_cache_is_invalidated_when_settings_are_updated(
1049 self, autologin_user, backend, csrf_token):
958 self, autologin_user, backend, csrf_token):
1050 repo_name = backend.repo_name
959 repo_name = backend.repo_name
1051 data = self.FORM_DATA.copy()
960 data = self.FORM_DATA.copy()
1052 data['csrf_token'] = csrf_token
961 data['csrf_token'] = csrf_token
1053 data['inherit_global_settings'] = True
962 data['inherit_global_settings'] = True
1054 settings = SettingsModel(repo=repo_name)
963 settings = SettingsModel(repo=repo_name)
1055
964
1056 invalidation_patcher = mock.patch(
965 invalidation_patcher = mock.patch(
1057 'rhodecode.controllers.admin.repos.ScmModel.mark_for_invalidation')
966 'rhodecode.controllers.admin.repos.ScmModel.mark_for_invalidation')
1058 with invalidation_patcher as invalidation_mock:
967 with invalidation_patcher as invalidation_mock:
1059 self.app.post(
968 self.app.post(
1060 url('repo_vcs_settings', repo_name=repo_name), data,
969 url('repo_vcs_settings', repo_name=repo_name), data,
1061 status=302)
970 status=302)
1062 try:
971 try:
1063 invalidation_mock.assert_called_once_with(repo_name, delete=True)
972 invalidation_mock.assert_called_once_with(repo_name, delete=True)
1064 finally:
973 finally:
1065 self._cleanup_repo_settings(settings)
974 self._cleanup_repo_settings(settings)
1066
975
1067 def test_other_settings_not_saved_inherit_global_settings_is_true(
976 def test_other_settings_not_saved_inherit_global_settings_is_true(
1068 self, autologin_user, backend, csrf_token):
977 self, autologin_user, backend, csrf_token):
1069 repo_name = backend.repo_name
978 repo_name = backend.repo_name
1070 data = self.FORM_DATA.copy()
979 data = self.FORM_DATA.copy()
1071 data['csrf_token'] = csrf_token
980 data['csrf_token'] = csrf_token
1072 data['inherit_global_settings'] = True
981 data['inherit_global_settings'] = True
1073 self.app.post(
982 self.app.post(
1074 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
983 url('repo_vcs_settings', repo_name=repo_name), data, status=302)
1075
984
1076 settings = SettingsModel(repo=repo_name)
985 settings = SettingsModel(repo=repo_name)
1077 ui_settings = (
986 ui_settings = (
1078 VcsSettingsModel.HOOKS_SETTINGS + VcsSettingsModel.HG_SETTINGS)
987 VcsSettingsModel.HOOKS_SETTINGS + VcsSettingsModel.HG_SETTINGS)
1079
988
1080 vcs_settings = []
989 vcs_settings = []
1081 try:
990 try:
1082 for section, key in ui_settings:
991 for section, key in ui_settings:
1083 ui = settings.get_ui_by_section_and_key(section, key)
992 ui = settings.get_ui_by_section_and_key(section, key)
1084 if ui:
993 if ui:
1085 vcs_settings.append(ui)
994 vcs_settings.append(ui)
1086 vcs_settings.extend(settings.get_ui_by_section(
995 vcs_settings.extend(settings.get_ui_by_section(
1087 VcsSettingsModel.SVN_BRANCH_SECTION))
996 VcsSettingsModel.SVN_BRANCH_SECTION))
1088 vcs_settings.extend(settings.get_ui_by_section(
997 vcs_settings.extend(settings.get_ui_by_section(
1089 VcsSettingsModel.SVN_TAG_SECTION))
998 VcsSettingsModel.SVN_TAG_SECTION))
1090 for name in VcsSettingsModel.GENERAL_SETTINGS:
999 for name in VcsSettingsModel.GENERAL_SETTINGS:
1091 setting = settings.get_setting_by_name(name)
1000 setting = settings.get_setting_by_name(name)
1092 if setting:
1001 if setting:
1093 vcs_settings.append(setting)
1002 vcs_settings.append(setting)
1094 assert vcs_settings == []
1003 assert vcs_settings == []
1095 finally:
1004 finally:
1096 self._cleanup_repo_settings(settings)
1005 self._cleanup_repo_settings(settings)
1097
1006
1098 def test_delete_svn_branch_and_tag_patterns(
1007 def test_delete_svn_branch_and_tag_patterns(
1099 self, autologin_user, backend_svn, settings_util, csrf_token):
1008 self, autologin_user, backend_svn, settings_util, csrf_token):
1100 repo = backend_svn.create_repo()
1009 repo = backend_svn.create_repo()
1101 repo_name = repo.repo_name
1010 repo_name = repo.repo_name
1102 branch = settings_util.create_repo_rhodecode_ui(
1011 branch = settings_util.create_repo_rhodecode_ui(
1103 repo, VcsSettingsModel.SVN_BRANCH_SECTION, 'test_branch',
1012 repo, VcsSettingsModel.SVN_BRANCH_SECTION, 'test_branch',
1104 cleanup=False)
1013 cleanup=False)
1105 tag = settings_util.create_repo_rhodecode_ui(
1014 tag = settings_util.create_repo_rhodecode_ui(
1106 repo, VcsSettingsModel.SVN_TAG_SECTION, 'test_tag', cleanup=False)
1015 repo, VcsSettingsModel.SVN_TAG_SECTION, 'test_tag', cleanup=False)
1107 data = {
1016 data = {
1108 '_method': 'delete',
1017 '_method': 'delete',
1109 'csrf_token': csrf_token
1018 'csrf_token': csrf_token
1110 }
1019 }
1111 for id_ in (branch.ui_id, tag.ui_id):
1020 for id_ in (branch.ui_id, tag.ui_id):
1112 data['delete_svn_pattern'] = id_,
1021 data['delete_svn_pattern'] = id_,
1113 self.app.post(
1022 self.app.post(
1114 url('repo_vcs_settings', repo_name=repo_name), data,
1023 url('repo_vcs_settings', repo_name=repo_name), data,
1115 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200)
1024 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200)
1116 settings = VcsSettingsModel(repo=repo_name)
1025 settings = VcsSettingsModel(repo=repo_name)
1117 assert settings.get_repo_svn_branch_patterns() == []
1026 assert settings.get_repo_svn_branch_patterns() == []
1118
1027
1119 def test_delete_svn_branch_requires_repo_admin_permission(
1028 def test_delete_svn_branch_requires_repo_admin_permission(
1120 self, backend_svn, user_util, settings_util, csrf_token):
1029 self, backend_svn, user_util, settings_util, csrf_token):
1121 repo = backend_svn.create_repo()
1030 repo = backend_svn.create_repo()
1122 repo_name = repo.repo_name
1031 repo_name = repo.repo_name
1123
1032
1124 logout_user_session(self.app, csrf_token)
1033 logout_user_session(self.app, csrf_token)
1125 session = login_user_session(
1034 session = login_user_session(
1126 self.app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
1035 self.app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
1127 csrf_token = auth.get_csrf_token(session)
1036 csrf_token = auth.get_csrf_token(session)
1128
1037
1129 repo = Repository.get_by_repo_name(repo_name)
1038 repo = Repository.get_by_repo_name(repo_name)
1130 user = UserModel().get_by_username(TEST_USER_REGULAR_LOGIN)
1039 user = UserModel().get_by_username(TEST_USER_REGULAR_LOGIN)
1131 user_util.grant_user_permission_to_repo(repo, user, 'repository.admin')
1040 user_util.grant_user_permission_to_repo(repo, user, 'repository.admin')
1132 branch = settings_util.create_repo_rhodecode_ui(
1041 branch = settings_util.create_repo_rhodecode_ui(
1133 repo, VcsSettingsModel.SVN_BRANCH_SECTION, 'test_branch',
1042 repo, VcsSettingsModel.SVN_BRANCH_SECTION, 'test_branch',
1134 cleanup=False)
1043 cleanup=False)
1135 data = {
1044 data = {
1136 '_method': 'delete',
1045 '_method': 'delete',
1137 'csrf_token': csrf_token,
1046 'csrf_token': csrf_token,
1138 'delete_svn_pattern': branch.ui_id
1047 'delete_svn_pattern': branch.ui_id
1139 }
1048 }
1140 self.app.post(
1049 self.app.post(
1141 url('repo_vcs_settings', repo_name=repo_name), data,
1050 url('repo_vcs_settings', repo_name=repo_name), data,
1142 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200)
1051 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200)
1143
1052
1144 def test_delete_svn_branch_raises_400_when_not_found(
1053 def test_delete_svn_branch_raises_400_when_not_found(
1145 self, autologin_user, backend_svn, settings_util, csrf_token):
1054 self, autologin_user, backend_svn, settings_util, csrf_token):
1146 repo_name = backend_svn.repo_name
1055 repo_name = backend_svn.repo_name
1147 data = {
1056 data = {
1148 '_method': 'delete',
1057 '_method': 'delete',
1149 'delete_svn_pattern': 123,
1058 'delete_svn_pattern': 123,
1150 'csrf_token': csrf_token
1059 'csrf_token': csrf_token
1151 }
1060 }
1152 self.app.post(
1061 self.app.post(
1153 url('repo_vcs_settings', repo_name=repo_name), data,
1062 url('repo_vcs_settings', repo_name=repo_name), data,
1154 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=400)
1063 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=400)
1155
1064
1156 def test_delete_svn_branch_raises_400_when_no_id_specified(
1065 def test_delete_svn_branch_raises_400_when_no_id_specified(
1157 self, autologin_user, backend_svn, settings_util, csrf_token):
1066 self, autologin_user, backend_svn, settings_util, csrf_token):
1158 repo_name = backend_svn.repo_name
1067 repo_name = backend_svn.repo_name
1159 data = {
1068 data = {
1160 '_method': 'delete',
1069 '_method': 'delete',
1161 'csrf_token': csrf_token
1070 'csrf_token': csrf_token
1162 }
1071 }
1163 self.app.post(
1072 self.app.post(
1164 url('repo_vcs_settings', repo_name=repo_name), data,
1073 url('repo_vcs_settings', repo_name=repo_name), data,
1165 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=400)
1074 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=400)
1166
1075
1167 def _cleanup_repo_settings(self, settings_model):
1076 def _cleanup_repo_settings(self, settings_model):
1168 cleanup = []
1077 cleanup = []
1169 ui_settings = (
1078 ui_settings = (
1170 VcsSettingsModel.HOOKS_SETTINGS + VcsSettingsModel.HG_SETTINGS)
1079 VcsSettingsModel.HOOKS_SETTINGS + VcsSettingsModel.HG_SETTINGS)
1171
1080
1172 for section, key in ui_settings:
1081 for section, key in ui_settings:
1173 ui = settings_model.get_ui_by_section_and_key(section, key)
1082 ui = settings_model.get_ui_by_section_and_key(section, key)
1174 if ui:
1083 if ui:
1175 cleanup.append(ui)
1084 cleanup.append(ui)
1176
1085
1177 cleanup.extend(settings_model.get_ui_by_section(
1086 cleanup.extend(settings_model.get_ui_by_section(
1178 VcsSettingsModel.INHERIT_SETTINGS))
1087 VcsSettingsModel.INHERIT_SETTINGS))
1179 cleanup.extend(settings_model.get_ui_by_section(
1088 cleanup.extend(settings_model.get_ui_by_section(
1180 VcsSettingsModel.SVN_BRANCH_SECTION))
1089 VcsSettingsModel.SVN_BRANCH_SECTION))
1181 cleanup.extend(settings_model.get_ui_by_section(
1090 cleanup.extend(settings_model.get_ui_by_section(
1182 VcsSettingsModel.SVN_TAG_SECTION))
1091 VcsSettingsModel.SVN_TAG_SECTION))
1183
1092
1184 for name in VcsSettingsModel.GENERAL_SETTINGS:
1093 for name in VcsSettingsModel.GENERAL_SETTINGS:
1185 setting = settings_model.get_setting_by_name(name)
1094 setting = settings_model.get_setting_by_name(name)
1186 if setting:
1095 if setting:
1187 cleanup.append(setting)
1096 cleanup.append(setting)
1188
1097
1189 for object_ in cleanup:
1098 for object_ in cleanup:
1190 Session().delete(object_)
1099 Session().delete(object_)
1191 Session().commit()
1100 Session().commit()
1192
1101
1193 def assert_repo_value_equals_global_value(self, response, setting):
1102 def assert_repo_value_equals_global_value(self, response, setting):
1194 assert_response = AssertResponse(response)
1103 assert_response = AssertResponse(response)
1195 global_css_selector = '[name={}_inherited]'.format(setting)
1104 global_css_selector = '[name={}_inherited]'.format(setting)
1196 repo_css_selector = '[name={}]'.format(setting)
1105 repo_css_selector = '[name={}]'.format(setting)
1197 repo_element = assert_response.get_element(repo_css_selector)
1106 repo_element = assert_response.get_element(repo_css_selector)
1198 global_element = assert_response.get_element(global_css_selector)
1107 global_element = assert_response.get_element(global_css_selector)
1199 assert repo_element.value == global_element.value
1108 assert repo_element.value == global_element.value
1200
1109
1201
1110
1202 def _get_permission_for_user(user, repo):
1111 def _get_permission_for_user(user, repo):
1203 perm = UserRepoToPerm.query()\
1112 perm = UserRepoToPerm.query()\
1204 .filter(UserRepoToPerm.repository ==
1113 .filter(UserRepoToPerm.repository ==
1205 Repository.get_by_repo_name(repo))\
1114 Repository.get_by_repo_name(repo))\
1206 .filter(UserRepoToPerm.user == User.get_by_username(user))\
1115 .filter(UserRepoToPerm.user == User.get_by_username(user))\
1207 .all()
1116 .all()
1208 return perm
1117 return perm
@@ -1,274 +1,272 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 pytest
21 import pytest
22
22
23 from rhodecode.tests import *
23 from rhodecode.tests import *
24 from rhodecode.tests.fixture import Fixture
24 from rhodecode.tests.fixture import Fixture
25
25
26 from rhodecode.model.db import Repository
26 from rhodecode.model.db import Repository
27 from rhodecode.model.repo import RepoModel
27 from rhodecode.model.repo import RepoModel
28 from rhodecode.model.user import UserModel
28 from rhodecode.model.user import UserModel
29 from rhodecode.model.meta import Session
29 from rhodecode.model.meta import Session
30
30
31 fixture = Fixture()
31 fixture = Fixture()
32
32
33
33
34 class _BaseTest(TestController):
34 class _BaseTest(TestController):
35
35
36 REPO = None
36 REPO = None
37 REPO_TYPE = None
37 REPO_TYPE = None
38 NEW_REPO = None
38 NEW_REPO = None
39 REPO_FORK = None
39 REPO_FORK = None
40
40
41 @pytest.fixture(autouse=True)
41 @pytest.fixture(autouse=True)
42 def prepare(self, request, pylonsapp):
42 def prepare(self, request, pylonsapp):
43 self.username = u'forkuser'
43 self.username = u'forkuser'
44 self.password = u'qweqwe'
44 self.password = u'qweqwe'
45 self.u1 = fixture.create_user(self.username, password=self.password,
45 self.u1 = fixture.create_user(self.username, password=self.password,
46 email=u'fork_king@rhodecode.org')
46 email=u'fork_king@rhodecode.org')
47 Session().commit()
47 Session().commit()
48 self.u1id = self.u1.user_id
48 self.u1id = self.u1.user_id
49 request.addfinalizer(self.cleanup)
49 request.addfinalizer(self.cleanup)
50
50
51 def cleanup(self):
51 def cleanup(self):
52 u1 = UserModel().get(self.u1id)
52 u1 = UserModel().get(self.u1id)
53 Session().delete(u1)
53 Session().delete(u1)
54 Session().commit()
54 Session().commit()
55
55
56 def test_index(self):
56 def test_index(self):
57 self.log_user()
57 self.log_user()
58 repo_name = self.REPO
58 repo_name = self.REPO
59 response = self.app.get(url(controller='forks', action='forks',
59 response = self.app.get(url(controller='forks', action='forks',
60 repo_name=repo_name))
60 repo_name=repo_name))
61
61
62 response.mustcontain("""There are no forks yet""")
62 response.mustcontain("""There are no forks yet""")
63
63
64 def test_no_permissions_to_fork(self):
64 def test_no_permissions_to_fork(self):
65 usr = self.log_user(TEST_USER_REGULAR_LOGIN,
65 usr = self.log_user(TEST_USER_REGULAR_LOGIN,
66 TEST_USER_REGULAR_PASS)['user_id']
66 TEST_USER_REGULAR_PASS)['user_id']
67 user_model = UserModel()
67 user_model = UserModel()
68 user_model.revoke_perm(usr, 'hg.fork.repository')
68 user_model.revoke_perm(usr, 'hg.fork.repository')
69 user_model.grant_perm(usr, 'hg.fork.none')
69 user_model.grant_perm(usr, 'hg.fork.none')
70 u = UserModel().get(usr)
70 u = UserModel().get(usr)
71 u.inherit_default_permissions = False
71 u.inherit_default_permissions = False
72 Session().commit()
72 Session().commit()
73 # try create a fork
73 # try create a fork
74 repo_name = self.REPO
74 repo_name = self.REPO
75 self.app.post(
75 self.app.post(
76 url(controller='forks', action='fork_create', repo_name=repo_name),
76 url(controller='forks', action='fork_create', repo_name=repo_name),
77 {'csrf_token': self.csrf_token}, status=403)
77 {'csrf_token': self.csrf_token}, status=403)
78
78
79 def test_index_with_fork(self):
79 def test_index_with_fork(self):
80 self.log_user()
80 self.log_user()
81
81
82 # create a fork
82 # create a fork
83 fork_name = self.REPO_FORK
83 fork_name = self.REPO_FORK
84 description = 'fork of vcs test'
84 description = 'fork of vcs test'
85 repo_name = self.REPO
85 repo_name = self.REPO
86 source_repo = Repository.get_by_repo_name(repo_name)
86 source_repo = Repository.get_by_repo_name(repo_name)
87 creation_args = {
87 creation_args = {
88 'repo_name': fork_name,
88 'repo_name': fork_name,
89 'repo_group': '',
89 'repo_group': '',
90 'fork_parent_id': source_repo.repo_id,
90 'fork_parent_id': source_repo.repo_id,
91 'repo_type': self.REPO_TYPE,
91 'repo_type': self.REPO_TYPE,
92 'description': description,
92 'description': description,
93 'private': 'False',
93 'private': 'False',
94 'landing_rev': 'rev:tip',
94 'landing_rev': 'rev:tip',
95 'csrf_token': self.csrf_token,
95 'csrf_token': self.csrf_token,
96 }
96 }
97
97
98 self.app.post(url(controller='forks', action='fork_create',
98 self.app.post(url(controller='forks', action='fork_create',
99 repo_name=repo_name), creation_args)
99 repo_name=repo_name), creation_args)
100
100
101 response = self.app.get(url(controller='forks', action='forks',
101 response = self.app.get(url(controller='forks', action='forks',
102 repo_name=repo_name))
102 repo_name=repo_name))
103
103
104 response.mustcontain(
104 response.mustcontain(
105 """<a href="/%s">%s</a>""" % (fork_name, fork_name)
105 """<a href="/%s">%s</a>""" % (fork_name, fork_name)
106 )
106 )
107
107
108 # remove this fork
108 # remove this fork
109 response = self.app.post(
109 fixture.destroy_repo(fork_name)
110 url('repo', repo_name=fork_name),
111 params={'_method': 'delete', 'csrf_token': self.csrf_token})
112
110
113 def test_fork_create_into_group(self):
111 def test_fork_create_into_group(self):
114 self.log_user()
112 self.log_user()
115 group = fixture.create_repo_group('vc')
113 group = fixture.create_repo_group('vc')
116 group_id = group.group_id
114 group_id = group.group_id
117 fork_name = self.REPO_FORK
115 fork_name = self.REPO_FORK
118 fork_name_full = 'vc/%s' % fork_name
116 fork_name_full = 'vc/%s' % fork_name
119 description = 'fork of vcs test'
117 description = 'fork of vcs test'
120 repo_name = self.REPO
118 repo_name = self.REPO
121 source_repo = Repository.get_by_repo_name(repo_name)
119 source_repo = Repository.get_by_repo_name(repo_name)
122 creation_args = {
120 creation_args = {
123 'repo_name': fork_name,
121 'repo_name': fork_name,
124 'repo_group': group_id,
122 'repo_group': group_id,
125 'fork_parent_id': source_repo.repo_id,
123 'fork_parent_id': source_repo.repo_id,
126 'repo_type': self.REPO_TYPE,
124 'repo_type': self.REPO_TYPE,
127 'description': description,
125 'description': description,
128 'private': 'False',
126 'private': 'False',
129 'landing_rev': 'rev:tip',
127 'landing_rev': 'rev:tip',
130 'csrf_token': self.csrf_token,
128 'csrf_token': self.csrf_token,
131 }
129 }
132 self.app.post(url(controller='forks', action='fork_create',
130 self.app.post(url(controller='forks', action='fork_create',
133 repo_name=repo_name), creation_args)
131 repo_name=repo_name), creation_args)
134 repo = Repository.get_by_repo_name(fork_name_full)
132 repo = Repository.get_by_repo_name(fork_name_full)
135 assert repo.fork.repo_name == self.REPO
133 assert repo.fork.repo_name == self.REPO
136
134
137 # run the check page that triggers the flash message
135 # run the check page that triggers the flash message
138 response = self.app.get(url('repo_check_home', repo_name=fork_name_full))
136 response = self.app.get(url('repo_check_home', repo_name=fork_name_full))
139 # test if we have a message that fork is ok
137 # test if we have a message that fork is ok
140 assert_session_flash(response,
138 assert_session_flash(response,
141 'Forked repository %s as <a href="/%s">%s</a>'
139 'Forked repository %s as <a href="/%s">%s</a>'
142 % (repo_name, fork_name_full, fork_name_full))
140 % (repo_name, fork_name_full, fork_name_full))
143
141
144 # test if the fork was created in the database
142 # test if the fork was created in the database
145 fork_repo = Session().query(Repository)\
143 fork_repo = Session().query(Repository)\
146 .filter(Repository.repo_name == fork_name_full).one()
144 .filter(Repository.repo_name == fork_name_full).one()
147
145
148 assert fork_repo.repo_name == fork_name_full
146 assert fork_repo.repo_name == fork_name_full
149 assert fork_repo.fork.repo_name == repo_name
147 assert fork_repo.fork.repo_name == repo_name
150
148
151 # test if the repository is visible in the list ?
149 # test if the repository is visible in the list ?
152 response = self.app.get(url('summary_home', repo_name=fork_name_full))
150 response = self.app.get(url('summary_home', repo_name=fork_name_full))
153 response.mustcontain(fork_name_full)
151 response.mustcontain(fork_name_full)
154 response.mustcontain(self.REPO_TYPE)
152 response.mustcontain(self.REPO_TYPE)
155
153
156 response.mustcontain('Fork of')
154 response.mustcontain('Fork of')
157 response.mustcontain('<a href="/%s">%s</a>' % (repo_name, repo_name))
155 response.mustcontain('<a href="/%s">%s</a>' % (repo_name, repo_name))
158
156
159 fixture.destroy_repo(fork_name_full)
157 fixture.destroy_repo(fork_name_full)
160 fixture.destroy_repo_group(group_id)
158 fixture.destroy_repo_group(group_id)
161
159
162 def test_z_fork_create(self):
160 def test_z_fork_create(self):
163 self.log_user()
161 self.log_user()
164 fork_name = self.REPO_FORK
162 fork_name = self.REPO_FORK
165 description = 'fork of vcs test'
163 description = 'fork of vcs test'
166 repo_name = self.REPO
164 repo_name = self.REPO
167 source_repo = Repository.get_by_repo_name(repo_name)
165 source_repo = Repository.get_by_repo_name(repo_name)
168 creation_args = {
166 creation_args = {
169 'repo_name': fork_name,
167 'repo_name': fork_name,
170 'repo_group': '',
168 'repo_group': '',
171 'fork_parent_id': source_repo.repo_id,
169 'fork_parent_id': source_repo.repo_id,
172 'repo_type': self.REPO_TYPE,
170 'repo_type': self.REPO_TYPE,
173 'description': description,
171 'description': description,
174 'private': 'False',
172 'private': 'False',
175 'landing_rev': 'rev:tip',
173 'landing_rev': 'rev:tip',
176 'csrf_token': self.csrf_token,
174 'csrf_token': self.csrf_token,
177 }
175 }
178 self.app.post(url(controller='forks', action='fork_create',
176 self.app.post(url(controller='forks', action='fork_create',
179 repo_name=repo_name), creation_args)
177 repo_name=repo_name), creation_args)
180 repo = Repository.get_by_repo_name(self.REPO_FORK)
178 repo = Repository.get_by_repo_name(self.REPO_FORK)
181 assert repo.fork.repo_name == self.REPO
179 assert repo.fork.repo_name == self.REPO
182
180
183 # run the check page that triggers the flash message
181 # run the check page that triggers the flash message
184 response = self.app.get(url('repo_check_home', repo_name=fork_name))
182 response = self.app.get(url('repo_check_home', repo_name=fork_name))
185 # test if we have a message that fork is ok
183 # test if we have a message that fork is ok
186 assert_session_flash(response,
184 assert_session_flash(response,
187 'Forked repository %s as <a href="/%s">%s</a>'
185 'Forked repository %s as <a href="/%s">%s</a>'
188 % (repo_name, fork_name, fork_name))
186 % (repo_name, fork_name, fork_name))
189
187
190 # test if the fork was created in the database
188 # test if the fork was created in the database
191 fork_repo = Session().query(Repository)\
189 fork_repo = Session().query(Repository)\
192 .filter(Repository.repo_name == fork_name).one()
190 .filter(Repository.repo_name == fork_name).one()
193
191
194 assert fork_repo.repo_name == fork_name
192 assert fork_repo.repo_name == fork_name
195 assert fork_repo.fork.repo_name == repo_name
193 assert fork_repo.fork.repo_name == repo_name
196
194
197 # test if the repository is visible in the list ?
195 # test if the repository is visible in the list ?
198 response = self.app.get(url('summary_home', repo_name=fork_name))
196 response = self.app.get(url('summary_home', repo_name=fork_name))
199 response.mustcontain(fork_name)
197 response.mustcontain(fork_name)
200 response.mustcontain(self.REPO_TYPE)
198 response.mustcontain(self.REPO_TYPE)
201 response.mustcontain('Fork of')
199 response.mustcontain('Fork of')
202 response.mustcontain('<a href="/%s">%s</a>' % (repo_name, repo_name))
200 response.mustcontain('<a href="/%s">%s</a>' % (repo_name, repo_name))
203
201
204 def test_zz_fork_permission_page(self):
202 def test_zz_fork_permission_page(self):
205 usr = self.log_user(self.username, self.password)['user_id']
203 usr = self.log_user(self.username, self.password)['user_id']
206 repo_name = self.REPO
204 repo_name = self.REPO
207
205
208 forks = Repository.query()\
206 forks = Repository.query()\
209 .filter(Repository.repo_type == self.REPO_TYPE)\
207 .filter(Repository.repo_type == self.REPO_TYPE)\
210 .filter(Repository.fork_id != None).all()
208 .filter(Repository.fork_id != None).all()
211 assert 1 == len(forks)
209 assert 1 == len(forks)
212
210
213 # set read permissions for this
211 # set read permissions for this
214 RepoModel().grant_user_permission(repo=forks[0],
212 RepoModel().grant_user_permission(repo=forks[0],
215 user=usr,
213 user=usr,
216 perm='repository.read')
214 perm='repository.read')
217 Session().commit()
215 Session().commit()
218
216
219 response = self.app.get(url(controller='forks', action='forks',
217 response = self.app.get(url(controller='forks', action='forks',
220 repo_name=repo_name))
218 repo_name=repo_name))
221
219
222 response.mustcontain('fork of vcs test')
220 response.mustcontain('fork of vcs test')
223
221
224 def test_zzz_fork_permission_page(self):
222 def test_zzz_fork_permission_page(self):
225 usr = self.log_user(self.username, self.password)['user_id']
223 usr = self.log_user(self.username, self.password)['user_id']
226 repo_name = self.REPO
224 repo_name = self.REPO
227
225
228 forks = Repository.query()\
226 forks = Repository.query()\
229 .filter(Repository.repo_type == self.REPO_TYPE)\
227 .filter(Repository.repo_type == self.REPO_TYPE)\
230 .filter(Repository.fork_id != None).all()
228 .filter(Repository.fork_id != None).all()
231 assert 1 == len(forks)
229 assert 1 == len(forks)
232
230
233 # set none
231 # set none
234 RepoModel().grant_user_permission(repo=forks[0],
232 RepoModel().grant_user_permission(repo=forks[0],
235 user=usr, perm='repository.none')
233 user=usr, perm='repository.none')
236 Session().commit()
234 Session().commit()
237 # fork shouldn't be there
235 # fork shouldn't be there
238 response = self.app.get(url(controller='forks', action='forks',
236 response = self.app.get(url(controller='forks', action='forks',
239 repo_name=repo_name))
237 repo_name=repo_name))
240 response.mustcontain('There are no forks yet')
238 response.mustcontain('There are no forks yet')
241
239
242
240
243 class TestGIT(_BaseTest):
241 class TestGIT(_BaseTest):
244 REPO = GIT_REPO
242 REPO = GIT_REPO
245 NEW_REPO = NEW_GIT_REPO
243 NEW_REPO = NEW_GIT_REPO
246 REPO_TYPE = 'git'
244 REPO_TYPE = 'git'
247 REPO_FORK = GIT_FORK
245 REPO_FORK = GIT_FORK
248
246
249
247
250 class TestHG(_BaseTest):
248 class TestHG(_BaseTest):
251 REPO = HG_REPO
249 REPO = HG_REPO
252 NEW_REPO = NEW_HG_REPO
250 NEW_REPO = NEW_HG_REPO
253 REPO_TYPE = 'hg'
251 REPO_TYPE = 'hg'
254 REPO_FORK = HG_FORK
252 REPO_FORK = HG_FORK
255
253
256
254
257 @pytest.mark.usefixtures('app', 'autologin_user')
255 @pytest.mark.usefixtures('app', 'autologin_user')
258 @pytest.mark.skip_backends('git','hg')
256 @pytest.mark.skip_backends('git','hg')
259 class TestSVNFork:
257 class TestSVNFork:
260
258
261 def test_fork_redirects(self, backend):
259 def test_fork_redirects(self, backend):
262 denied_actions = ['fork','fork_create']
260 denied_actions = ['fork','fork_create']
263 for action in denied_actions:
261 for action in denied_actions:
264 response = self.app.get(url(
262 response = self.app.get(url(
265 controller='forks', action=action,
263 controller='forks', action=action,
266 repo_name=backend.repo_name))
264 repo_name=backend.repo_name))
267 assert response.status_int == 302
265 assert response.status_int == 302
268
266
269 # Not allowed, redirect to the summary
267 # Not allowed, redirect to the summary
270 redirected = response.follow()
268 redirected = response.follow()
271 summary_url = url('summary_home', repo_name=backend.repo_name)
269 summary_url = url('summary_home', repo_name=backend.repo_name)
272
270
273 # URL adds leading slash and path doesn't have it
271 # URL adds leading slash and path doesn't have it
274 assert redirected.req.path == summary_url
272 assert redirected.req.path == summary_url
General Comments 0
You need to be logged in to leave comments. Login now