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