Show More
@@ -0,0 +1,180 b'' | |||
|
1 | # -*- coding: utf-8 -*- | |
|
2 | ||
|
3 | # Copyright (C) 2016-2017 RhodeCode GmbH | |
|
4 | # | |
|
5 | # This program is free software: you can redistribute it and/or modify | |
|
6 | # it under the terms of the GNU Affero General Public License, version 3 | |
|
7 | # (only), as published by the Free Software Foundation. | |
|
8 | # | |
|
9 | # This program is distributed in the hope that it will be useful, | |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
|
12 | # GNU General Public License for more details. | |
|
13 | # | |
|
14 | # You should have received a copy of the GNU Affero General Public License | |
|
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
|
16 | # | |
|
17 | # This program is dual-licensed. If you wish to learn more about the | |
|
18 | # RhodeCode Enterprise Edition, including its added features, Support services, | |
|
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ | |
|
20 | ||
|
21 | import logging | |
|
22 | import formencode | |
|
23 | ||
|
24 | from pyramid.httpexceptions import HTTPFound, HTTPForbidden | |
|
25 | from pyramid.view import view_config | |
|
26 | from pyramid.renderers import render | |
|
27 | from pyramid.response import Response | |
|
28 | ||
|
29 | from rhodecode.apps._base import BaseAppView, DataGridAppView | |
|
30 | ||
|
31 | from rhodecode.lib.ext_json import json | |
|
32 | from rhodecode.lib.auth import ( | |
|
33 | LoginRequired, CSRFRequired, NotAnonymous, | |
|
34 | HasPermissionAny, HasRepoGroupPermissionAny) | |
|
35 | from rhodecode.lib import helpers as h | |
|
36 | from rhodecode.lib.utils import repo_name_slug | |
|
37 | from rhodecode.lib.utils2 import safe_int, safe_unicode | |
|
38 | from rhodecode.model.forms import RepoForm | |
|
39 | from rhodecode.model.repo import RepoModel | |
|
40 | from rhodecode.model.scm import RepoList, RepoGroupList, ScmModel | |
|
41 | from rhodecode.model.settings import SettingsModel | |
|
42 | from rhodecode.model.db import Repository, RepoGroup | |
|
43 | ||
|
44 | log = logging.getLogger(__name__) | |
|
45 | ||
|
46 | ||
|
47 | class AdminReposView(BaseAppView, DataGridAppView): | |
|
48 | ||
|
49 | def load_default_context(self): | |
|
50 | c = self._get_local_tmpl_context() | |
|
51 | self._register_global_c(c) | |
|
52 | return c | |
|
53 | ||
|
54 | def _load_form_data(self, c): | |
|
55 | acl_groups = RepoGroupList(RepoGroup.query().all(), | |
|
56 | perm_set=['group.write', 'group.admin']) | |
|
57 | c.repo_groups = RepoGroup.groups_choices(groups=acl_groups) | |
|
58 | c.repo_groups_choices = map(lambda k: safe_unicode(k[0]), c.repo_groups) | |
|
59 | c.landing_revs_choices, c.landing_revs = \ | |
|
60 | ScmModel().get_repo_landing_revs() | |
|
61 | c.personal_repo_group = self._rhodecode_user.personal_repo_group | |
|
62 | ||
|
63 | @LoginRequired() | |
|
64 | @NotAnonymous() | |
|
65 | @view_config( | |
|
66 | route_name='repos', request_method='GET', | |
|
67 | renderer='rhodecode:templates/admin/repos/repos.mako') | |
|
68 | def repository_list(self): | |
|
69 | c = self.load_default_context() | |
|
70 | ||
|
71 | repo_list = Repository.get_all_repos() | |
|
72 | c.repo_list = RepoList(repo_list, perm_set=['repository.admin']) | |
|
73 | repos_data = RepoModel().get_repos_as_dict( | |
|
74 | repo_list=c.repo_list, admin=True, super_user_actions=True) | |
|
75 | # json used to render the grid | |
|
76 | c.data = json.dumps(repos_data) | |
|
77 | ||
|
78 | return self._get_template_context(c) | |
|
79 | ||
|
80 | @LoginRequired() | |
|
81 | @NotAnonymous() | |
|
82 | # perms check inside | |
|
83 | @view_config( | |
|
84 | route_name='repo_new', request_method='GET', | |
|
85 | renderer='rhodecode:templates/admin/repos/repo_add.mako') | |
|
86 | def repository_new(self): | |
|
87 | c = self.load_default_context() | |
|
88 | ||
|
89 | new_repo = self.request.GET.get('repo', '') | |
|
90 | parent_group = safe_int(self.request.GET.get('parent_group')) | |
|
91 | _gr = RepoGroup.get(parent_group) | |
|
92 | ||
|
93 | if not HasPermissionAny('hg.admin', 'hg.create.repository')(): | |
|
94 | # you're not super admin nor have global create permissions, | |
|
95 | # but maybe you have at least write permission to a parent group ? | |
|
96 | ||
|
97 | gr_name = _gr.group_name if _gr else None | |
|
98 | # create repositories with write permission on group is set to true | |
|
99 | create_on_write = HasPermissionAny('hg.create.write_on_repogroup.true')() | |
|
100 | group_admin = HasRepoGroupPermissionAny('group.admin')(group_name=gr_name) | |
|
101 | group_write = HasRepoGroupPermissionAny('group.write')(group_name=gr_name) | |
|
102 | if not (group_admin or (group_write and create_on_write)): | |
|
103 | raise HTTPForbidden() | |
|
104 | ||
|
105 | self._load_form_data(c) | |
|
106 | c.new_repo = repo_name_slug(new_repo) | |
|
107 | ||
|
108 | # apply the defaults from defaults page | |
|
109 | defaults = SettingsModel().get_default_repo_settings(strip_prefix=True) | |
|
110 | # set checkbox to autochecked | |
|
111 | defaults['repo_copy_permissions'] = True | |
|
112 | ||
|
113 | parent_group_choice = '-1' | |
|
114 | if not self._rhodecode_user.is_admin and self._rhodecode_user.personal_repo_group: | |
|
115 | parent_group_choice = self._rhodecode_user.personal_repo_group | |
|
116 | ||
|
117 | if parent_group and _gr: | |
|
118 | if parent_group in [x[0] for x in c.repo_groups]: | |
|
119 | parent_group_choice = safe_unicode(parent_group) | |
|
120 | ||
|
121 | defaults.update({'repo_group': parent_group_choice}) | |
|
122 | ||
|
123 | data = render('rhodecode:templates/admin/repos/repo_add.mako', | |
|
124 | self._get_template_context(c), self.request) | |
|
125 | html = formencode.htmlfill.render( | |
|
126 | data, | |
|
127 | defaults=defaults, | |
|
128 | encoding="UTF-8", | |
|
129 | force_defaults=False | |
|
130 | ) | |
|
131 | return Response(html) | |
|
132 | ||
|
133 | @LoginRequired() | |
|
134 | @NotAnonymous() | |
|
135 | @CSRFRequired() | |
|
136 | # perms check inside | |
|
137 | @view_config( | |
|
138 | route_name='repo_create', request_method='POST', | |
|
139 | renderer='rhodecode:templates/admin/repos/repos.mako') | |
|
140 | def repository_create(self): | |
|
141 | c = self.load_default_context() | |
|
142 | ||
|
143 | form_result = {} | |
|
144 | task_id = None | |
|
145 | self._load_form_data(c) | |
|
146 | ||
|
147 | try: | |
|
148 | # CanWriteToGroup validators checks permissions of this POST | |
|
149 | form_result = RepoForm(repo_groups=c.repo_groups_choices, | |
|
150 | landing_revs=c.landing_revs_choices)()\ | |
|
151 | .to_python(dict(self.request.POST)) | |
|
152 | ||
|
153 | # create is done sometimes async on celery, db transaction | |
|
154 | # management is handled there. | |
|
155 | task = RepoModel().create(form_result, self._rhodecode_user.user_id) | |
|
156 | from celery.result import BaseAsyncResult | |
|
157 | if isinstance(task, BaseAsyncResult): | |
|
158 | task_id = task.task_id | |
|
159 | except formencode.Invalid as errors: | |
|
160 | data = render('rhodecode:templates/admin/repos/repo_add.mako', | |
|
161 | self._get_template_context(c), self.request) | |
|
162 | html = formencode.htmlfill.render( | |
|
163 | data, | |
|
164 | defaults=errors.value, | |
|
165 | errors=errors.error_dict or {}, | |
|
166 | prefix_error=False, | |
|
167 | encoding="UTF-8", | |
|
168 | force_defaults=False | |
|
169 | ) | |
|
170 | return Response(html) | |
|
171 | ||
|
172 | except Exception as e: | |
|
173 | msg = self._log_creation_exception(e, form_result.get('repo_name')) | |
|
174 | h.flash(msg, category='error') | |
|
175 | raise HTTPFound(h.route_path('home')) | |
|
176 | ||
|
177 | raise HTTPFound( | |
|
178 | h.route_path('repo_creating', | |
|
179 | repo_name=form_result['repo_name_full'], | |
|
180 | _query=dict(task_id=task_id))) |
|
1 | NO CONTENT: new file 100644 |
|
1 | NO CONTENT: new file 100644 |
This diff has been collapsed as it changes many lines, (685 lines changed) Show them Hide them | |||
@@ -0,0 +1,685 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 mock | |
|
22 | import pytest | |
|
23 | ||
|
24 | from rhodecode.lib import auth | |
|
25 | from rhodecode.lib.utils2 import str2bool | |
|
26 | from rhodecode.model.db import ( | |
|
27 | Repository, UserRepoToPerm, User) | |
|
28 | from rhodecode.model.meta import Session | |
|
29 | from rhodecode.model.settings import SettingsModel, VcsSettingsModel | |
|
30 | from rhodecode.model.user import UserModel | |
|
31 | from rhodecode.tests import ( | |
|
32 | login_user_session, logout_user_session, | |
|
33 | TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS) | |
|
34 | from rhodecode.tests.fixture import Fixture | |
|
35 | from rhodecode.tests.utils import AssertResponse | |
|
36 | ||
|
37 | fixture = Fixture() | |
|
38 | ||
|
39 | ||
|
40 | def route_path(name, params=None, **kwargs): | |
|
41 | import urllib | |
|
42 | ||
|
43 | base_url = { | |
|
44 | 'repo_summary': '/{repo_name}', | |
|
45 | 'repo_creating_check': '/{repo_name}/repo_creating_check', | |
|
46 | 'edit_repo': '/{repo_name}/settings', | |
|
47 | 'edit_repo_vcs': '/{repo_name}/settings/vcs', | |
|
48 | 'edit_repo_vcs_update': '/{repo_name}/settings/vcs/update', | |
|
49 | 'edit_repo_vcs_svn_pattern_delete': '/{repo_name}/settings/vcs/svn_pattern/delete' | |
|
50 | }[name].format(**kwargs) | |
|
51 | ||
|
52 | if params: | |
|
53 | base_url = '{}?{}'.format(base_url, urllib.urlencode(params)) | |
|
54 | return base_url | |
|
55 | ||
|
56 | ||
|
57 | @pytest.mark.usefixtures("app") | |
|
58 | class TestVcsSettings(object): | |
|
59 | FORM_DATA = { | |
|
60 | 'inherit_global_settings': False, | |
|
61 | 'hooks_changegroup_repo_size': False, | |
|
62 | 'hooks_changegroup_push_logger': False, | |
|
63 | 'hooks_outgoing_pull_logger': False, | |
|
64 | 'extensions_largefiles': False, | |
|
65 | 'extensions_evolve': False, | |
|
66 | 'phases_publish': 'False', | |
|
67 | 'rhodecode_pr_merge_enabled': False, | |
|
68 | 'rhodecode_use_outdated_comments': False, | |
|
69 | 'new_svn_branch': '', | |
|
70 | 'new_svn_tag': '' | |
|
71 | } | |
|
72 | ||
|
73 | @pytest.mark.skip_backends('svn') | |
|
74 | def test_global_settings_initial_values(self, autologin_user, backend): | |
|
75 | repo_name = backend.repo_name | |
|
76 | response = self.app.get(route_path('edit_repo_vcs', repo_name=repo_name)) | |
|
77 | ||
|
78 | expected_settings = ( | |
|
79 | 'rhodecode_use_outdated_comments', 'rhodecode_pr_merge_enabled', | |
|
80 | 'hooks_changegroup_repo_size', 'hooks_changegroup_push_logger', | |
|
81 | 'hooks_outgoing_pull_logger' | |
|
82 | ) | |
|
83 | for setting in expected_settings: | |
|
84 | self.assert_repo_value_equals_global_value(response, setting) | |
|
85 | ||
|
86 | def test_show_settings_requires_repo_admin_permission( | |
|
87 | self, backend, user_util, settings_util): | |
|
88 | repo = backend.create_repo() | |
|
89 | repo_name = repo.repo_name | |
|
90 | user = UserModel().get_by_username(TEST_USER_REGULAR_LOGIN) | |
|
91 | user_util.grant_user_permission_to_repo(repo, user, 'repository.admin') | |
|
92 | login_user_session( | |
|
93 | self.app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS) | |
|
94 | self.app.get(route_path('edit_repo_vcs', repo_name=repo_name), status=200) | |
|
95 | ||
|
96 | def test_inherit_global_settings_flag_is_true_by_default( | |
|
97 | self, autologin_user, backend): | |
|
98 | repo_name = backend.repo_name | |
|
99 | response = self.app.get(route_path('edit_repo_vcs', repo_name=repo_name)) | |
|
100 | ||
|
101 | assert_response = AssertResponse(response) | |
|
102 | element = assert_response.get_element('#inherit_global_settings') | |
|
103 | assert element.checked | |
|
104 | ||
|
105 | @pytest.mark.parametrize('checked_value', [True, False]) | |
|
106 | def test_inherit_global_settings_value( | |
|
107 | self, autologin_user, backend, checked_value, settings_util): | |
|
108 | repo = backend.create_repo() | |
|
109 | repo_name = repo.repo_name | |
|
110 | settings_util.create_repo_rhodecode_setting( | |
|
111 | repo, 'inherit_vcs_settings', checked_value, 'bool') | |
|
112 | response = self.app.get(route_path('edit_repo_vcs', repo_name=repo_name)) | |
|
113 | ||
|
114 | assert_response = AssertResponse(response) | |
|
115 | element = assert_response.get_element('#inherit_global_settings') | |
|
116 | assert element.checked == checked_value | |
|
117 | ||
|
118 | @pytest.mark.skip_backends('svn') | |
|
119 | def test_hooks_settings_are_created( | |
|
120 | self, autologin_user, backend, csrf_token): | |
|
121 | repo_name = backend.repo_name | |
|
122 | data = self.FORM_DATA.copy() | |
|
123 | data['csrf_token'] = csrf_token | |
|
124 | self.app.post( | |
|
125 | route_path('edit_repo_vcs_update', repo_name=repo_name), data, status=302) | |
|
126 | settings = SettingsModel(repo=repo_name) | |
|
127 | try: | |
|
128 | for section, key in VcsSettingsModel.HOOKS_SETTINGS: | |
|
129 | ui = settings.get_ui_by_section_and_key(section, key) | |
|
130 | assert ui.ui_active is False | |
|
131 | finally: | |
|
132 | self._cleanup_repo_settings(settings) | |
|
133 | ||
|
134 | def test_hooks_settings_are_not_created_for_svn( | |
|
135 | self, autologin_user, backend_svn, csrf_token): | |
|
136 | repo_name = backend_svn.repo_name | |
|
137 | data = self.FORM_DATA.copy() | |
|
138 | data['csrf_token'] = csrf_token | |
|
139 | self.app.post( | |
|
140 | route_path('edit_repo_vcs_update', repo_name=repo_name), data, status=302) | |
|
141 | settings = SettingsModel(repo=repo_name) | |
|
142 | try: | |
|
143 | for section, key in VcsSettingsModel.HOOKS_SETTINGS: | |
|
144 | ui = settings.get_ui_by_section_and_key(section, key) | |
|
145 | assert ui is None | |
|
146 | finally: | |
|
147 | self._cleanup_repo_settings(settings) | |
|
148 | ||
|
149 | @pytest.mark.skip_backends('svn') | |
|
150 | def test_hooks_settings_are_updated( | |
|
151 | self, autologin_user, backend, csrf_token): | |
|
152 | repo_name = backend.repo_name | |
|
153 | settings = SettingsModel(repo=repo_name) | |
|
154 | for section, key in VcsSettingsModel.HOOKS_SETTINGS: | |
|
155 | settings.create_ui_section_value(section, '', key=key, active=True) | |
|
156 | ||
|
157 | data = self.FORM_DATA.copy() | |
|
158 | data['csrf_token'] = csrf_token | |
|
159 | self.app.post( | |
|
160 | route_path('edit_repo_vcs_update', repo_name=repo_name), data, status=302) | |
|
161 | try: | |
|
162 | for section, key in VcsSettingsModel.HOOKS_SETTINGS: | |
|
163 | ui = settings.get_ui_by_section_and_key(section, key) | |
|
164 | assert ui.ui_active is False | |
|
165 | finally: | |
|
166 | self._cleanup_repo_settings(settings) | |
|
167 | ||
|
168 | def test_hooks_settings_are_not_updated_for_svn( | |
|
169 | self, autologin_user, backend_svn, csrf_token): | |
|
170 | repo_name = backend_svn.repo_name | |
|
171 | settings = SettingsModel(repo=repo_name) | |
|
172 | for section, key in VcsSettingsModel.HOOKS_SETTINGS: | |
|
173 | settings.create_ui_section_value(section, '', key=key, active=True) | |
|
174 | ||
|
175 | data = self.FORM_DATA.copy() | |
|
176 | data['csrf_token'] = csrf_token | |
|
177 | self.app.post( | |
|
178 | route_path('edit_repo_vcs_update', repo_name=repo_name), data, status=302) | |
|
179 | try: | |
|
180 | for section, key in VcsSettingsModel.HOOKS_SETTINGS: | |
|
181 | ui = settings.get_ui_by_section_and_key(section, key) | |
|
182 | assert ui.ui_active is True | |
|
183 | finally: | |
|
184 | self._cleanup_repo_settings(settings) | |
|
185 | ||
|
186 | @pytest.mark.skip_backends('svn') | |
|
187 | def test_pr_settings_are_created( | |
|
188 | self, autologin_user, backend, csrf_token): | |
|
189 | repo_name = backend.repo_name | |
|
190 | data = self.FORM_DATA.copy() | |
|
191 | data['csrf_token'] = csrf_token | |
|
192 | self.app.post( | |
|
193 | route_path('edit_repo_vcs_update', repo_name=repo_name), data, status=302) | |
|
194 | settings = SettingsModel(repo=repo_name) | |
|
195 | try: | |
|
196 | for name in VcsSettingsModel.GENERAL_SETTINGS: | |
|
197 | setting = settings.get_setting_by_name(name) | |
|
198 | assert setting.app_settings_value is False | |
|
199 | finally: | |
|
200 | self._cleanup_repo_settings(settings) | |
|
201 | ||
|
202 | def test_pr_settings_are_not_created_for_svn( | |
|
203 | self, autologin_user, backend_svn, csrf_token): | |
|
204 | repo_name = backend_svn.repo_name | |
|
205 | data = self.FORM_DATA.copy() | |
|
206 | data['csrf_token'] = csrf_token | |
|
207 | self.app.post( | |
|
208 | route_path('edit_repo_vcs_update', repo_name=repo_name), data, status=302) | |
|
209 | settings = SettingsModel(repo=repo_name) | |
|
210 | try: | |
|
211 | for name in VcsSettingsModel.GENERAL_SETTINGS: | |
|
212 | setting = settings.get_setting_by_name(name) | |
|
213 | assert setting is None | |
|
214 | finally: | |
|
215 | self._cleanup_repo_settings(settings) | |
|
216 | ||
|
217 | def test_pr_settings_creation_requires_repo_admin_permission( | |
|
218 | self, backend, user_util, settings_util, csrf_token): | |
|
219 | repo = backend.create_repo() | |
|
220 | repo_name = repo.repo_name | |
|
221 | ||
|
222 | logout_user_session(self.app, csrf_token) | |
|
223 | session = login_user_session( | |
|
224 | self.app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS) | |
|
225 | new_csrf_token = auth.get_csrf_token(session) | |
|
226 | ||
|
227 | user = UserModel().get_by_username(TEST_USER_REGULAR_LOGIN) | |
|
228 | repo = Repository.get_by_repo_name(repo_name) | |
|
229 | user_util.grant_user_permission_to_repo(repo, user, 'repository.admin') | |
|
230 | data = self.FORM_DATA.copy() | |
|
231 | data['csrf_token'] = new_csrf_token | |
|
232 | settings = SettingsModel(repo=repo_name) | |
|
233 | ||
|
234 | try: | |
|
235 | self.app.post( | |
|
236 | route_path('edit_repo_vcs_update', repo_name=repo_name), data, | |
|
237 | status=302) | |
|
238 | finally: | |
|
239 | self._cleanup_repo_settings(settings) | |
|
240 | ||
|
241 | @pytest.mark.skip_backends('svn') | |
|
242 | def test_pr_settings_are_updated( | |
|
243 | self, autologin_user, backend, csrf_token): | |
|
244 | repo_name = backend.repo_name | |
|
245 | settings = SettingsModel(repo=repo_name) | |
|
246 | for name in VcsSettingsModel.GENERAL_SETTINGS: | |
|
247 | settings.create_or_update_setting(name, True, 'bool') | |
|
248 | ||
|
249 | data = self.FORM_DATA.copy() | |
|
250 | data['csrf_token'] = csrf_token | |
|
251 | self.app.post( | |
|
252 | route_path('edit_repo_vcs_update', repo_name=repo_name), data, status=302) | |
|
253 | try: | |
|
254 | for name in VcsSettingsModel.GENERAL_SETTINGS: | |
|
255 | setting = settings.get_setting_by_name(name) | |
|
256 | assert setting.app_settings_value is False | |
|
257 | finally: | |
|
258 | self._cleanup_repo_settings(settings) | |
|
259 | ||
|
260 | def test_pr_settings_are_not_updated_for_svn( | |
|
261 | self, autologin_user, backend_svn, csrf_token): | |
|
262 | repo_name = backend_svn.repo_name | |
|
263 | settings = SettingsModel(repo=repo_name) | |
|
264 | for name in VcsSettingsModel.GENERAL_SETTINGS: | |
|
265 | settings.create_or_update_setting(name, True, 'bool') | |
|
266 | ||
|
267 | data = self.FORM_DATA.copy() | |
|
268 | data['csrf_token'] = csrf_token | |
|
269 | self.app.post( | |
|
270 | route_path('edit_repo_vcs_update', repo_name=repo_name), data, status=302) | |
|
271 | try: | |
|
272 | for name in VcsSettingsModel.GENERAL_SETTINGS: | |
|
273 | setting = settings.get_setting_by_name(name) | |
|
274 | assert setting.app_settings_value is True | |
|
275 | finally: | |
|
276 | self._cleanup_repo_settings(settings) | |
|
277 | ||
|
278 | def test_svn_settings_are_created( | |
|
279 | self, autologin_user, backend_svn, csrf_token, settings_util): | |
|
280 | repo_name = backend_svn.repo_name | |
|
281 | data = self.FORM_DATA.copy() | |
|
282 | data['new_svn_tag'] = 'svn-tag' | |
|
283 | data['new_svn_branch'] = 'svn-branch' | |
|
284 | data['csrf_token'] = csrf_token | |
|
285 | ||
|
286 | # Create few global settings to make sure that uniqueness validators | |
|
287 | # are not triggered | |
|
288 | settings_util.create_rhodecode_ui( | |
|
289 | VcsSettingsModel.SVN_BRANCH_SECTION, 'svn-branch') | |
|
290 | settings_util.create_rhodecode_ui( | |
|
291 | VcsSettingsModel.SVN_TAG_SECTION, 'svn-tag') | |
|
292 | ||
|
293 | self.app.post( | |
|
294 | route_path('edit_repo_vcs_update', repo_name=repo_name), data, status=302) | |
|
295 | settings = SettingsModel(repo=repo_name) | |
|
296 | try: | |
|
297 | svn_branches = settings.get_ui_by_section( | |
|
298 | VcsSettingsModel.SVN_BRANCH_SECTION) | |
|
299 | svn_branch_names = [b.ui_value for b in svn_branches] | |
|
300 | svn_tags = settings.get_ui_by_section( | |
|
301 | VcsSettingsModel.SVN_TAG_SECTION) | |
|
302 | svn_tag_names = [b.ui_value for b in svn_tags] | |
|
303 | assert 'svn-branch' in svn_branch_names | |
|
304 | assert 'svn-tag' in svn_tag_names | |
|
305 | finally: | |
|
306 | self._cleanup_repo_settings(settings) | |
|
307 | ||
|
308 | def test_svn_settings_are_unique( | |
|
309 | self, autologin_user, backend_svn, csrf_token, settings_util): | |
|
310 | repo = backend_svn.repo | |
|
311 | repo_name = repo.repo_name | |
|
312 | data = self.FORM_DATA.copy() | |
|
313 | data['new_svn_tag'] = 'test_tag' | |
|
314 | data['new_svn_branch'] = 'test_branch' | |
|
315 | data['csrf_token'] = csrf_token | |
|
316 | settings_util.create_repo_rhodecode_ui( | |
|
317 | repo, VcsSettingsModel.SVN_BRANCH_SECTION, 'test_branch') | |
|
318 | settings_util.create_repo_rhodecode_ui( | |
|
319 | repo, VcsSettingsModel.SVN_TAG_SECTION, 'test_tag') | |
|
320 | ||
|
321 | response = self.app.post( | |
|
322 | route_path('edit_repo_vcs_update', repo_name=repo_name), data, status=200) | |
|
323 | response.mustcontain('Pattern already exists') | |
|
324 | ||
|
325 | def test_svn_settings_with_empty_values_are_not_created( | |
|
326 | self, autologin_user, backend_svn, csrf_token): | |
|
327 | repo_name = backend_svn.repo_name | |
|
328 | data = self.FORM_DATA.copy() | |
|
329 | data['csrf_token'] = csrf_token | |
|
330 | self.app.post( | |
|
331 | route_path('edit_repo_vcs_update', repo_name=repo_name), data, status=302) | |
|
332 | settings = SettingsModel(repo=repo_name) | |
|
333 | try: | |
|
334 | svn_branches = settings.get_ui_by_section( | |
|
335 | VcsSettingsModel.SVN_BRANCH_SECTION) | |
|
336 | svn_tags = settings.get_ui_by_section( | |
|
337 | VcsSettingsModel.SVN_TAG_SECTION) | |
|
338 | assert len(svn_branches) == 0 | |
|
339 | assert len(svn_tags) == 0 | |
|
340 | finally: | |
|
341 | self._cleanup_repo_settings(settings) | |
|
342 | ||
|
343 | def test_svn_settings_are_shown_for_svn_repository( | |
|
344 | self, autologin_user, backend_svn, csrf_token): | |
|
345 | repo_name = backend_svn.repo_name | |
|
346 | response = self.app.get( | |
|
347 | route_path('edit_repo_vcs', repo_name=repo_name), status=200) | |
|
348 | response.mustcontain('Subversion Settings') | |
|
349 | ||
|
350 | @pytest.mark.skip_backends('svn') | |
|
351 | def test_svn_settings_are_not_created_for_not_svn_repository( | |
|
352 | self, autologin_user, backend, csrf_token): | |
|
353 | repo_name = backend.repo_name | |
|
354 | data = self.FORM_DATA.copy() | |
|
355 | data['csrf_token'] = csrf_token | |
|
356 | self.app.post( | |
|
357 | route_path('edit_repo_vcs_update', repo_name=repo_name), data, status=302) | |
|
358 | settings = SettingsModel(repo=repo_name) | |
|
359 | try: | |
|
360 | svn_branches = settings.get_ui_by_section( | |
|
361 | VcsSettingsModel.SVN_BRANCH_SECTION) | |
|
362 | svn_tags = settings.get_ui_by_section( | |
|
363 | VcsSettingsModel.SVN_TAG_SECTION) | |
|
364 | assert len(svn_branches) == 0 | |
|
365 | assert len(svn_tags) == 0 | |
|
366 | finally: | |
|
367 | self._cleanup_repo_settings(settings) | |
|
368 | ||
|
369 | @pytest.mark.skip_backends('svn') | |
|
370 | def test_svn_settings_are_shown_only_for_svn_repository( | |
|
371 | self, autologin_user, backend, csrf_token): | |
|
372 | repo_name = backend.repo_name | |
|
373 | response = self.app.get( | |
|
374 | route_path('edit_repo_vcs', repo_name=repo_name), status=200) | |
|
375 | response.mustcontain(no='Subversion Settings') | |
|
376 | ||
|
377 | def test_hg_settings_are_created( | |
|
378 | self, autologin_user, backend_hg, csrf_token): | |
|
379 | repo_name = backend_hg.repo_name | |
|
380 | data = self.FORM_DATA.copy() | |
|
381 | data['new_svn_tag'] = 'svn-tag' | |
|
382 | data['new_svn_branch'] = 'svn-branch' | |
|
383 | data['csrf_token'] = csrf_token | |
|
384 | self.app.post( | |
|
385 | route_path('edit_repo_vcs_update', repo_name=repo_name), data, status=302) | |
|
386 | settings = SettingsModel(repo=repo_name) | |
|
387 | try: | |
|
388 | largefiles_ui = settings.get_ui_by_section_and_key( | |
|
389 | 'extensions', 'largefiles') | |
|
390 | assert largefiles_ui.ui_active is False | |
|
391 | phases_ui = settings.get_ui_by_section_and_key( | |
|
392 | 'phases', 'publish') | |
|
393 | assert str2bool(phases_ui.ui_value) is False | |
|
394 | finally: | |
|
395 | self._cleanup_repo_settings(settings) | |
|
396 | ||
|
397 | def test_hg_settings_are_updated( | |
|
398 | self, autologin_user, backend_hg, csrf_token): | |
|
399 | repo_name = backend_hg.repo_name | |
|
400 | settings = SettingsModel(repo=repo_name) | |
|
401 | settings.create_ui_section_value( | |
|
402 | 'extensions', '', key='largefiles', active=True) | |
|
403 | settings.create_ui_section_value( | |
|
404 | 'phases', '1', key='publish', active=True) | |
|
405 | ||
|
406 | data = self.FORM_DATA.copy() | |
|
407 | data['csrf_token'] = csrf_token | |
|
408 | self.app.post( | |
|
409 | route_path('edit_repo_vcs_update', repo_name=repo_name), data, status=302) | |
|
410 | try: | |
|
411 | largefiles_ui = settings.get_ui_by_section_and_key( | |
|
412 | 'extensions', 'largefiles') | |
|
413 | assert largefiles_ui.ui_active is False | |
|
414 | phases_ui = settings.get_ui_by_section_and_key( | |
|
415 | 'phases', 'publish') | |
|
416 | assert str2bool(phases_ui.ui_value) is False | |
|
417 | finally: | |
|
418 | self._cleanup_repo_settings(settings) | |
|
419 | ||
|
420 | def test_hg_settings_are_shown_for_hg_repository( | |
|
421 | self, autologin_user, backend_hg, csrf_token): | |
|
422 | repo_name = backend_hg.repo_name | |
|
423 | response = self.app.get( | |
|
424 | route_path('edit_repo_vcs', repo_name=repo_name), status=200) | |
|
425 | response.mustcontain('Mercurial Settings') | |
|
426 | ||
|
427 | @pytest.mark.skip_backends('hg') | |
|
428 | def test_hg_settings_are_created_only_for_hg_repository( | |
|
429 | self, autologin_user, backend, csrf_token): | |
|
430 | repo_name = backend.repo_name | |
|
431 | data = self.FORM_DATA.copy() | |
|
432 | data['csrf_token'] = csrf_token | |
|
433 | self.app.post( | |
|
434 | route_path('edit_repo_vcs_update', repo_name=repo_name), data, status=302) | |
|
435 | settings = SettingsModel(repo=repo_name) | |
|
436 | try: | |
|
437 | largefiles_ui = settings.get_ui_by_section_and_key( | |
|
438 | 'extensions', 'largefiles') | |
|
439 | assert largefiles_ui is None | |
|
440 | phases_ui = settings.get_ui_by_section_and_key( | |
|
441 | 'phases', 'publish') | |
|
442 | assert phases_ui is None | |
|
443 | finally: | |
|
444 | self._cleanup_repo_settings(settings) | |
|
445 | ||
|
446 | @pytest.mark.skip_backends('hg') | |
|
447 | def test_hg_settings_are_shown_only_for_hg_repository( | |
|
448 | self, autologin_user, backend, csrf_token): | |
|
449 | repo_name = backend.repo_name | |
|
450 | response = self.app.get( | |
|
451 | route_path('edit_repo_vcs', repo_name=repo_name), status=200) | |
|
452 | response.mustcontain(no='Mercurial Settings') | |
|
453 | ||
|
454 | @pytest.mark.skip_backends('hg') | |
|
455 | def test_hg_settings_are_updated_only_for_hg_repository( | |
|
456 | self, autologin_user, backend, csrf_token): | |
|
457 | repo_name = backend.repo_name | |
|
458 | settings = SettingsModel(repo=repo_name) | |
|
459 | settings.create_ui_section_value( | |
|
460 | 'extensions', '', key='largefiles', active=True) | |
|
461 | settings.create_ui_section_value( | |
|
462 | 'phases', '1', key='publish', active=True) | |
|
463 | ||
|
464 | data = self.FORM_DATA.copy() | |
|
465 | data['csrf_token'] = csrf_token | |
|
466 | self.app.post( | |
|
467 | route_path('edit_repo_vcs_update', repo_name=repo_name), data, status=302) | |
|
468 | try: | |
|
469 | largefiles_ui = settings.get_ui_by_section_and_key( | |
|
470 | 'extensions', 'largefiles') | |
|
471 | assert largefiles_ui.ui_active is True | |
|
472 | phases_ui = settings.get_ui_by_section_and_key( | |
|
473 | 'phases', 'publish') | |
|
474 | assert phases_ui.ui_value == '1' | |
|
475 | finally: | |
|
476 | self._cleanup_repo_settings(settings) | |
|
477 | ||
|
478 | def test_per_repo_svn_settings_are_displayed( | |
|
479 | self, autologin_user, backend_svn, settings_util): | |
|
480 | repo = backend_svn.create_repo() | |
|
481 | repo_name = repo.repo_name | |
|
482 | branches = [ | |
|
483 | settings_util.create_repo_rhodecode_ui( | |
|
484 | repo, VcsSettingsModel.SVN_BRANCH_SECTION, | |
|
485 | 'branch_{}'.format(i)) | |
|
486 | for i in range(10)] | |
|
487 | tags = [ | |
|
488 | settings_util.create_repo_rhodecode_ui( | |
|
489 | repo, VcsSettingsModel.SVN_TAG_SECTION, 'tag_{}'.format(i)) | |
|
490 | for i in range(10)] | |
|
491 | ||
|
492 | response = self.app.get( | |
|
493 | route_path('edit_repo_vcs', repo_name=repo_name), status=200) | |
|
494 | assert_response = AssertResponse(response) | |
|
495 | for branch in branches: | |
|
496 | css_selector = '[name=branch_value_{}]'.format(branch.ui_id) | |
|
497 | element = assert_response.get_element(css_selector) | |
|
498 | assert element.value == branch.ui_value | |
|
499 | for tag in tags: | |
|
500 | css_selector = '[name=tag_ui_value_new_{}]'.format(tag.ui_id) | |
|
501 | element = assert_response.get_element(css_selector) | |
|
502 | assert element.value == tag.ui_value | |
|
503 | ||
|
504 | def test_per_repo_hg_and_pr_settings_are_not_displayed_for_svn( | |
|
505 | self, autologin_user, backend_svn, settings_util): | |
|
506 | repo = backend_svn.create_repo() | |
|
507 | repo_name = repo.repo_name | |
|
508 | response = self.app.get( | |
|
509 | route_path('edit_repo_vcs', repo_name=repo_name), status=200) | |
|
510 | response.mustcontain(no='<label>Hooks:</label>') | |
|
511 | response.mustcontain(no='<label>Pull Request Settings:</label>') | |
|
512 | ||
|
513 | def test_inherit_global_settings_value_is_saved( | |
|
514 | self, autologin_user, backend, csrf_token): | |
|
515 | repo_name = backend.repo_name | |
|
516 | data = self.FORM_DATA.copy() | |
|
517 | data['csrf_token'] = csrf_token | |
|
518 | data['inherit_global_settings'] = True | |
|
519 | self.app.post( | |
|
520 | route_path('edit_repo_vcs_update', repo_name=repo_name), data, status=302) | |
|
521 | ||
|
522 | settings = SettingsModel(repo=repo_name) | |
|
523 | vcs_settings = VcsSettingsModel(repo=repo_name) | |
|
524 | try: | |
|
525 | assert vcs_settings.inherit_global_settings is True | |
|
526 | finally: | |
|
527 | self._cleanup_repo_settings(settings) | |
|
528 | ||
|
529 | def test_repo_cache_is_invalidated_when_settings_are_updated( | |
|
530 | self, autologin_user, backend, csrf_token): | |
|
531 | repo_name = backend.repo_name | |
|
532 | data = self.FORM_DATA.copy() | |
|
533 | data['csrf_token'] = csrf_token | |
|
534 | data['inherit_global_settings'] = True | |
|
535 | settings = SettingsModel(repo=repo_name) | |
|
536 | ||
|
537 | invalidation_patcher = mock.patch( | |
|
538 | 'rhodecode.model.scm.ScmModel.mark_for_invalidation') | |
|
539 | with invalidation_patcher as invalidation_mock: | |
|
540 | self.app.post( | |
|
541 | route_path('edit_repo_vcs_update', repo_name=repo_name), data, | |
|
542 | status=302) | |
|
543 | try: | |
|
544 | invalidation_mock.assert_called_once_with(repo_name, delete=True) | |
|
545 | finally: | |
|
546 | self._cleanup_repo_settings(settings) | |
|
547 | ||
|
548 | def test_other_settings_not_saved_inherit_global_settings_is_true( | |
|
549 | self, autologin_user, backend, csrf_token): | |
|
550 | repo_name = backend.repo_name | |
|
551 | data = self.FORM_DATA.copy() | |
|
552 | data['csrf_token'] = csrf_token | |
|
553 | data['inherit_global_settings'] = True | |
|
554 | self.app.post( | |
|
555 | route_path('edit_repo_vcs_update', repo_name=repo_name), data, status=302) | |
|
556 | ||
|
557 | settings = SettingsModel(repo=repo_name) | |
|
558 | ui_settings = ( | |
|
559 | VcsSettingsModel.HOOKS_SETTINGS + VcsSettingsModel.HG_SETTINGS) | |
|
560 | ||
|
561 | vcs_settings = [] | |
|
562 | try: | |
|
563 | for section, key in ui_settings: | |
|
564 | ui = settings.get_ui_by_section_and_key(section, key) | |
|
565 | if ui: | |
|
566 | vcs_settings.append(ui) | |
|
567 | vcs_settings.extend(settings.get_ui_by_section( | |
|
568 | VcsSettingsModel.SVN_BRANCH_SECTION)) | |
|
569 | vcs_settings.extend(settings.get_ui_by_section( | |
|
570 | VcsSettingsModel.SVN_TAG_SECTION)) | |
|
571 | for name in VcsSettingsModel.GENERAL_SETTINGS: | |
|
572 | setting = settings.get_setting_by_name(name) | |
|
573 | if setting: | |
|
574 | vcs_settings.append(setting) | |
|
575 | assert vcs_settings == [] | |
|
576 | finally: | |
|
577 | self._cleanup_repo_settings(settings) | |
|
578 | ||
|
579 | def test_delete_svn_branch_and_tag_patterns( | |
|
580 | self, autologin_user, backend_svn, settings_util, csrf_token, xhr_header): | |
|
581 | repo = backend_svn.create_repo() | |
|
582 | repo_name = repo.repo_name | |
|
583 | branch = settings_util.create_repo_rhodecode_ui( | |
|
584 | repo, VcsSettingsModel.SVN_BRANCH_SECTION, 'test_branch', | |
|
585 | cleanup=False) | |
|
586 | tag = settings_util.create_repo_rhodecode_ui( | |
|
587 | repo, VcsSettingsModel.SVN_TAG_SECTION, 'test_tag', cleanup=False) | |
|
588 | data = { | |
|
589 | 'csrf_token': csrf_token | |
|
590 | } | |
|
591 | for id_ in (branch.ui_id, tag.ui_id): | |
|
592 | data['delete_svn_pattern'] = id_, | |
|
593 | self.app.post( | |
|
594 | route_path('edit_repo_vcs_svn_pattern_delete', repo_name=repo_name), | |
|
595 | data, extra_environ=xhr_header, status=200) | |
|
596 | settings = VcsSettingsModel(repo=repo_name) | |
|
597 | assert settings.get_repo_svn_branch_patterns() == [] | |
|
598 | ||
|
599 | def test_delete_svn_branch_requires_repo_admin_permission( | |
|
600 | self, backend_svn, user_util, settings_util, csrf_token, xhr_header): | |
|
601 | repo = backend_svn.create_repo() | |
|
602 | repo_name = repo.repo_name | |
|
603 | ||
|
604 | logout_user_session(self.app, csrf_token) | |
|
605 | session = login_user_session( | |
|
606 | self.app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS) | |
|
607 | csrf_token = auth.get_csrf_token(session) | |
|
608 | ||
|
609 | repo = Repository.get_by_repo_name(repo_name) | |
|
610 | user = UserModel().get_by_username(TEST_USER_REGULAR_LOGIN) | |
|
611 | user_util.grant_user_permission_to_repo(repo, user, 'repository.admin') | |
|
612 | branch = settings_util.create_repo_rhodecode_ui( | |
|
613 | repo, VcsSettingsModel.SVN_BRANCH_SECTION, 'test_branch', | |
|
614 | cleanup=False) | |
|
615 | data = { | |
|
616 | 'csrf_token': csrf_token, | |
|
617 | 'delete_svn_pattern': branch.ui_id | |
|
618 | } | |
|
619 | self.app.post( | |
|
620 | route_path('edit_repo_vcs_svn_pattern_delete', repo_name=repo_name), | |
|
621 | data, extra_environ=xhr_header, status=200) | |
|
622 | ||
|
623 | def test_delete_svn_branch_raises_400_when_not_found( | |
|
624 | self, autologin_user, backend_svn, settings_util, csrf_token, xhr_header): | |
|
625 | repo_name = backend_svn.repo_name | |
|
626 | data = { | |
|
627 | 'delete_svn_pattern': 123, | |
|
628 | 'csrf_token': csrf_token | |
|
629 | } | |
|
630 | self.app.post( | |
|
631 | route_path('edit_repo_vcs_svn_pattern_delete', repo_name=repo_name), | |
|
632 | data, extra_environ=xhr_header, status=400) | |
|
633 | ||
|
634 | def test_delete_svn_branch_raises_400_when_no_id_specified( | |
|
635 | self, autologin_user, backend_svn, settings_util, csrf_token, xhr_header): | |
|
636 | repo_name = backend_svn.repo_name | |
|
637 | data = { | |
|
638 | 'csrf_token': csrf_token | |
|
639 | } | |
|
640 | self.app.post( | |
|
641 | route_path('edit_repo_vcs_svn_pattern_delete', repo_name=repo_name), | |
|
642 | data, extra_environ=xhr_header, status=400) | |
|
643 | ||
|
644 | def _cleanup_repo_settings(self, settings_model): | |
|
645 | cleanup = [] | |
|
646 | ui_settings = ( | |
|
647 | VcsSettingsModel.HOOKS_SETTINGS + VcsSettingsModel.HG_SETTINGS) | |
|
648 | ||
|
649 | for section, key in ui_settings: | |
|
650 | ui = settings_model.get_ui_by_section_and_key(section, key) | |
|
651 | if ui: | |
|
652 | cleanup.append(ui) | |
|
653 | ||
|
654 | cleanup.extend(settings_model.get_ui_by_section( | |
|
655 | VcsSettingsModel.INHERIT_SETTINGS)) | |
|
656 | cleanup.extend(settings_model.get_ui_by_section( | |
|
657 | VcsSettingsModel.SVN_BRANCH_SECTION)) | |
|
658 | cleanup.extend(settings_model.get_ui_by_section( | |
|
659 | VcsSettingsModel.SVN_TAG_SECTION)) | |
|
660 | ||
|
661 | for name in VcsSettingsModel.GENERAL_SETTINGS: | |
|
662 | setting = settings_model.get_setting_by_name(name) | |
|
663 | if setting: | |
|
664 | cleanup.append(setting) | |
|
665 | ||
|
666 | for object_ in cleanup: | |
|
667 | Session().delete(object_) | |
|
668 | Session().commit() | |
|
669 | ||
|
670 | def assert_repo_value_equals_global_value(self, response, setting): | |
|
671 | assert_response = AssertResponse(response) | |
|
672 | global_css_selector = '[name={}_inherited]'.format(setting) | |
|
673 | repo_css_selector = '[name={}]'.format(setting) | |
|
674 | repo_element = assert_response.get_element(repo_css_selector) | |
|
675 | global_element = assert_response.get_element(global_css_selector) | |
|
676 | assert repo_element.value == global_element.value | |
|
677 | ||
|
678 | ||
|
679 | def _get_permission_for_user(user, repo): | |
|
680 | perm = UserRepoToPerm.query()\ | |
|
681 | .filter(UserRepoToPerm.repository == | |
|
682 | Repository.get_by_repo_name(repo))\ | |
|
683 | .filter(UserRepoToPerm.user == User.get_by_username(user))\ | |
|
684 | .all() | |
|
685 | return perm |
@@ -0,0 +1,113 b'' | |||
|
1 | # -*- coding: utf-8 -*- | |
|
2 | ||
|
3 | # Copyright (C) 2017-2017 RhodeCode GmbH | |
|
4 | # | |
|
5 | # This program is free software: you can redistribute it and/or modify | |
|
6 | # it under the terms of the GNU Affero General Public License, version 3 | |
|
7 | # (only), as published by the Free Software Foundation. | |
|
8 | # | |
|
9 | # This program is distributed in the hope that it will be useful, | |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
|
12 | # GNU General Public License for more details. | |
|
13 | # | |
|
14 | # You should have received a copy of the GNU Affero General Public License | |
|
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
|
16 | # | |
|
17 | # This program is dual-licensed. If you wish to learn more about the | |
|
18 | # RhodeCode Enterprise Edition, including its added features, Support services, | |
|
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ | |
|
20 | ||
|
21 | import logging | |
|
22 | ||
|
23 | import formencode | |
|
24 | ||
|
25 | from pyramid.httpexceptions import HTTPFound | |
|
26 | from pyramid.view import view_config | |
|
27 | ||
|
28 | from rhodecode.apps._base import RepoAppView | |
|
29 | from rhodecode.lib import audit_logger | |
|
30 | from rhodecode.lib import helpers as h | |
|
31 | from rhodecode.lib.auth import ( | |
|
32 | LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired) | |
|
33 | from rhodecode.model.db import RepositoryField | |
|
34 | from rhodecode.model.forms import RepoFieldForm | |
|
35 | from rhodecode.model.meta import Session | |
|
36 | from rhodecode.model.repo import RepoModel | |
|
37 | ||
|
38 | log = logging.getLogger(__name__) | |
|
39 | ||
|
40 | ||
|
41 | class RepoSettingsFieldsView(RepoAppView): | |
|
42 | def load_default_context(self): | |
|
43 | c = self._get_local_tmpl_context() | |
|
44 | ||
|
45 | # TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead | |
|
46 | c.repo_info = self.db_repo | |
|
47 | ||
|
48 | self._register_global_c(c) | |
|
49 | return c | |
|
50 | ||
|
51 | @LoginRequired() | |
|
52 | @HasRepoPermissionAnyDecorator('repository.admin') | |
|
53 | @view_config( | |
|
54 | route_name='edit_repo_fields', request_method='GET', | |
|
55 | renderer='rhodecode:templates/admin/repos/repo_edit.mako') | |
|
56 | def repo_field_edit(self): | |
|
57 | c = self.load_default_context() | |
|
58 | ||
|
59 | c.active = 'fields' | |
|
60 | c.repo_fields = RepositoryField.query() \ | |
|
61 | .filter(RepositoryField.repository == self.db_repo).all() | |
|
62 | ||
|
63 | return self._get_template_context(c) | |
|
64 | ||
|
65 | @LoginRequired() | |
|
66 | @HasRepoPermissionAnyDecorator('repository.admin') | |
|
67 | @CSRFRequired() | |
|
68 | @view_config( | |
|
69 | route_name='edit_repo_fields_create', request_method='POST', | |
|
70 | renderer='rhodecode:templates/admin/repos/repo_edit.mako') | |
|
71 | def repo_field_create(self): | |
|
72 | _ = self.request.translate | |
|
73 | ||
|
74 | try: | |
|
75 | form_result = RepoFieldForm()().to_python(dict(self.request.POST)) | |
|
76 | RepoModel().add_repo_field( | |
|
77 | self.db_repo_name, | |
|
78 | form_result['new_field_key'], | |
|
79 | field_type=form_result['new_field_type'], | |
|
80 | field_value=form_result['new_field_value'], | |
|
81 | field_label=form_result['new_field_label'], | |
|
82 | field_desc=form_result['new_field_desc']) | |
|
83 | ||
|
84 | Session().commit() | |
|
85 | except Exception as e: | |
|
86 | log.exception("Exception creating field") | |
|
87 | msg = _('An error occurred during creation of field') | |
|
88 | if isinstance(e, formencode.Invalid): | |
|
89 | msg += ". " + e.msg | |
|
90 | h.flash(msg, category='error') | |
|
91 | ||
|
92 | raise HTTPFound( | |
|
93 | h.route_path('edit_repo_fields', repo_name=self.db_repo_name)) | |
|
94 | ||
|
95 | @LoginRequired() | |
|
96 | @HasRepoPermissionAnyDecorator('repository.admin') | |
|
97 | @CSRFRequired() | |
|
98 | @view_config( | |
|
99 | route_name='edit_repo_fields_delete', request_method='POST', | |
|
100 | renderer='rhodecode:templates/admin/repos/repo_edit.mako') | |
|
101 | def repo_field_delete(self): | |
|
102 | _ = self.request.translate | |
|
103 | field = RepositoryField.get_or_404(self.request.matchdict['field_id']) | |
|
104 | try: | |
|
105 | RepoModel().delete_repo_field(self.db_repo_name, field.field_key) | |
|
106 | Session().commit() | |
|
107 | except Exception: | |
|
108 | log.exception('Exception during removal of field') | |
|
109 | msg = _('An error occurred during removal of field') | |
|
110 | h.flash(msg, category='error') | |
|
111 | ||
|
112 | raise HTTPFound( | |
|
113 | h.route_path('edit_repo_fields', repo_name=self.db_repo_name)) |
@@ -0,0 +1,129 b'' | |||
|
1 | # -*- coding: utf-8 -*- | |
|
2 | ||
|
3 | # Copyright (C) 2017-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.httpexceptions import HTTPFound | |
|
24 | from pyramid.view import view_config | |
|
25 | ||
|
26 | from rhodecode.apps._base import RepoAppView | |
|
27 | from rhodecode.lib import audit_logger | |
|
28 | from rhodecode.lib import helpers as h | |
|
29 | from rhodecode.lib.auth import ( | |
|
30 | LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired) | |
|
31 | from rhodecode.model.forms import IssueTrackerPatternsForm | |
|
32 | from rhodecode.model.meta import Session | |
|
33 | from rhodecode.model.settings import IssueTrackerSettingsModel | |
|
34 | ||
|
35 | log = logging.getLogger(__name__) | |
|
36 | ||
|
37 | ||
|
38 | class RepoSettingsIssueTrackersView(RepoAppView): | |
|
39 | def load_default_context(self): | |
|
40 | c = self._get_local_tmpl_context() | |
|
41 | ||
|
42 | # TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead | |
|
43 | c.repo_info = self.db_repo | |
|
44 | ||
|
45 | self._register_global_c(c) | |
|
46 | return c | |
|
47 | ||
|
48 | @LoginRequired() | |
|
49 | @HasRepoPermissionAnyDecorator('repository.admin') | |
|
50 | @view_config( | |
|
51 | route_name='edit_repo_issuetracker', request_method='GET', | |
|
52 | renderer='rhodecode:templates/admin/repos/repo_edit.mako') | |
|
53 | def repo_issuetracker(self): | |
|
54 | c = self.load_default_context() | |
|
55 | c.active = 'issuetracker' | |
|
56 | c.data = 'data' | |
|
57 | ||
|
58 | c.settings_model = IssueTrackerSettingsModel(repo=self.db_repo) | |
|
59 | c.global_patterns = c.settings_model.get_global_settings() | |
|
60 | c.repo_patterns = c.settings_model.get_repo_settings() | |
|
61 | ||
|
62 | return self._get_template_context(c) | |
|
63 | ||
|
64 | @LoginRequired() | |
|
65 | @HasRepoPermissionAnyDecorator('repository.admin') | |
|
66 | @CSRFRequired() | |
|
67 | @view_config( | |
|
68 | route_name='edit_repo_issuetracker_test', request_method='POST', | |
|
69 | xhr=True, renderer='string') | |
|
70 | def repo_issuetracker_test(self): | |
|
71 | return h.urlify_commit_message( | |
|
72 | self.request.POST.get('test_text', ''), | |
|
73 | self.db_repo_name) | |
|
74 | ||
|
75 | @LoginRequired() | |
|
76 | @HasRepoPermissionAnyDecorator('repository.admin') | |
|
77 | @CSRFRequired() | |
|
78 | @view_config( | |
|
79 | route_name='edit_repo_issuetracker_delete', request_method='POST', | |
|
80 | renderer='rhodecode:templates/admin/repos/repo_edit.mako') | |
|
81 | def repo_issuetracker_delete(self): | |
|
82 | _ = self.request.translate | |
|
83 | uid = self.request.POST.get('uid') | |
|
84 | repo_settings = IssueTrackerSettingsModel(repo=self.db_repo_name) | |
|
85 | try: | |
|
86 | repo_settings.delete_entries(uid) | |
|
87 | except Exception: | |
|
88 | h.flash(_('Error occurred during deleting issue tracker entry'), | |
|
89 | category='error') | |
|
90 | else: | |
|
91 | h.flash(_('Removed issue tracker entry'), category='success') | |
|
92 | raise HTTPFound( | |
|
93 | h.route_path('edit_repo_issuetracker', repo_name=self.db_repo_name)) | |
|
94 | ||
|
95 | def _update_patterns(self, form, repo_settings): | |
|
96 | for uid in form['delete_patterns']: | |
|
97 | repo_settings.delete_entries(uid) | |
|
98 | ||
|
99 | for pattern_data in form['patterns']: | |
|
100 | for setting_key, pattern, type_ in pattern_data: | |
|
101 | sett = repo_settings.create_or_update_setting( | |
|
102 | setting_key, pattern.strip(), type_) | |
|
103 | Session().add(sett) | |
|
104 | ||
|
105 | Session().commit() | |
|
106 | ||
|
107 | @LoginRequired() | |
|
108 | @HasRepoPermissionAnyDecorator('repository.admin') | |
|
109 | @CSRFRequired() | |
|
110 | @view_config( | |
|
111 | route_name='edit_repo_issuetracker_update', request_method='POST', | |
|
112 | renderer='rhodecode:templates/admin/repos/repo_edit.mako') | |
|
113 | def repo_issuetracker_update(self): | |
|
114 | _ = self.request.translate | |
|
115 | # Save inheritance | |
|
116 | repo_settings = IssueTrackerSettingsModel(repo=self.db_repo_name) | |
|
117 | inherited = ( | |
|
118 | self.request.POST.get('inherit_global_issuetracker') == "inherited") | |
|
119 | repo_settings.inherit_global_settings = inherited | |
|
120 | Session().commit() | |
|
121 | ||
|
122 | form = IssueTrackerPatternsForm()().to_python(self.request.POST) | |
|
123 | if form: | |
|
124 | self._update_patterns(form, repo_settings) | |
|
125 | ||
|
126 | h.flash(_('Updated issue tracker entries'), category='success') | |
|
127 | raise HTTPFound( | |
|
128 | h.route_path('edit_repo_issuetracker', repo_name=self.db_repo_name)) | |
|
129 |
@@ -0,0 +1,75 b'' | |||
|
1 | # -*- coding: utf-8 -*- | |
|
2 | ||
|
3 | # Copyright (C) 2017-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.httpexceptions import HTTPFound | |
|
24 | from pyramid.view import view_config | |
|
25 | ||
|
26 | from rhodecode.apps._base import RepoAppView | |
|
27 | from rhodecode.lib import helpers as h | |
|
28 | from rhodecode.lib.auth import ( | |
|
29 | LoginRequired, CSRFRequired, HasRepoPermissionAnyDecorator) | |
|
30 | from rhodecode.model.scm import ScmModel | |
|
31 | ||
|
32 | log = logging.getLogger(__name__) | |
|
33 | ||
|
34 | ||
|
35 | class RepoSettingsRemoteView(RepoAppView): | |
|
36 | def load_default_context(self): | |
|
37 | c = self._get_local_tmpl_context() | |
|
38 | ||
|
39 | # TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead | |
|
40 | c.repo_info = self.db_repo | |
|
41 | ||
|
42 | self._register_global_c(c) | |
|
43 | return c | |
|
44 | ||
|
45 | @LoginRequired() | |
|
46 | @HasRepoPermissionAnyDecorator('repository.admin') | |
|
47 | @view_config( | |
|
48 | route_name='edit_repo_remote', request_method='GET', | |
|
49 | renderer='rhodecode:templates/admin/repos/repo_edit.mako') | |
|
50 | def repo_remote_edit_form(self): | |
|
51 | c = self.load_default_context() | |
|
52 | c.active = 'remote' | |
|
53 | ||
|
54 | return self._get_template_context(c) | |
|
55 | ||
|
56 | @LoginRequired() | |
|
57 | @HasRepoPermissionAnyDecorator('repository.admin') | |
|
58 | @CSRFRequired() | |
|
59 | @view_config( | |
|
60 | route_name='edit_repo_remote_pull', request_method='POST', | |
|
61 | renderer=None) | |
|
62 | def repo_remote_pull_changes(self): | |
|
63 | _ = self.request.translate | |
|
64 | self.load_default_context() | |
|
65 | ||
|
66 | try: | |
|
67 | ScmModel().pull_changes( | |
|
68 | self.db_repo_name, self._rhodecode_user.username) | |
|
69 | h.flash(_('Pulled from remote location'), category='success') | |
|
70 | except Exception: | |
|
71 | log.exception("Exception during pull from remote") | |
|
72 | h.flash(_('An error occurred during pull from remote location'), | |
|
73 | category='error') | |
|
74 | raise HTTPFound( | |
|
75 | h.route_path('edit_repo_remote', repo_name=self.db_repo_name)) |
@@ -0,0 +1,172 b'' | |||
|
1 | # -*- coding: utf-8 -*- | |
|
2 | ||
|
3 | # Copyright (C) 2017-2017 RhodeCode GmbH | |
|
4 | # | |
|
5 | # This program is free software: you can redistribute it and/or modify | |
|
6 | # it under the terms of the GNU Affero General Public License, version 3 | |
|
7 | # (only), as published by the Free Software Foundation. | |
|
8 | # | |
|
9 | # This program is distributed in the hope that it will be useful, | |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
|
12 | # GNU General Public License for more details. | |
|
13 | # | |
|
14 | # You should have received a copy of the GNU Affero General Public License | |
|
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
|
16 | # | |
|
17 | # This program is dual-licensed. If you wish to learn more about the | |
|
18 | # RhodeCode Enterprise Edition, including its added features, Support services, | |
|
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ | |
|
20 | ||
|
21 | import logging | |
|
22 | ||
|
23 | import formencode | |
|
24 | from pyramid.httpexceptions import HTTPFound, HTTPBadRequest | |
|
25 | from pyramid.response import Response | |
|
26 | from pyramid.renderers import render | |
|
27 | from pyramid.view import view_config | |
|
28 | ||
|
29 | from rhodecode.apps._base import RepoAppView | |
|
30 | from rhodecode.lib import audit_logger | |
|
31 | from rhodecode.lib import helpers as h | |
|
32 | from rhodecode.lib.auth import ( | |
|
33 | LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired) | |
|
34 | from rhodecode.model.forms import RepoVcsSettingsForm | |
|
35 | from rhodecode.model.meta import Session | |
|
36 | from rhodecode.model.settings import VcsSettingsModel, SettingNotFound | |
|
37 | ||
|
38 | log = logging.getLogger(__name__) | |
|
39 | ||
|
40 | ||
|
41 | class RepoSettingsVcsView(RepoAppView): | |
|
42 | def load_default_context(self): | |
|
43 | c = self._get_local_tmpl_context() | |
|
44 | ||
|
45 | # TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead | |
|
46 | c.repo_info = self.db_repo | |
|
47 | ||
|
48 | self._register_global_c(c) | |
|
49 | return c | |
|
50 | ||
|
51 | def _vcs_form_defaults(self, repo_name): | |
|
52 | model = VcsSettingsModel(repo=repo_name) | |
|
53 | global_defaults = model.get_global_settings() | |
|
54 | ||
|
55 | repo_defaults = {} | |
|
56 | repo_defaults.update(global_defaults) | |
|
57 | repo_defaults.update(model.get_repo_settings()) | |
|
58 | ||
|
59 | global_defaults = { | |
|
60 | '{}_inherited'.format(k): global_defaults[k] | |
|
61 | for k in global_defaults} | |
|
62 | ||
|
63 | defaults = { | |
|
64 | 'inherit_global_settings': model.inherit_global_settings | |
|
65 | } | |
|
66 | defaults.update(global_defaults) | |
|
67 | defaults.update(repo_defaults) | |
|
68 | defaults.update({ | |
|
69 | 'new_svn_branch': '', | |
|
70 | 'new_svn_tag': '', | |
|
71 | }) | |
|
72 | return defaults | |
|
73 | ||
|
74 | @LoginRequired() | |
|
75 | @HasRepoPermissionAnyDecorator('repository.admin') | |
|
76 | @view_config( | |
|
77 | route_name='edit_repo_vcs', request_method='GET', | |
|
78 | renderer='rhodecode:templates/admin/repos/repo_edit.mako') | |
|
79 | def repo_vcs_settings(self): | |
|
80 | c = self.load_default_context() | |
|
81 | model = VcsSettingsModel(repo=self.db_repo_name) | |
|
82 | ||
|
83 | c.active = 'vcs' | |
|
84 | c.global_svn_branch_patterns = model.get_global_svn_branch_patterns() | |
|
85 | c.global_svn_tag_patterns = model.get_global_svn_tag_patterns() | |
|
86 | c.svn_branch_patterns = model.get_repo_svn_branch_patterns() | |
|
87 | c.svn_tag_patterns = model.get_repo_svn_tag_patterns() | |
|
88 | ||
|
89 | defaults = self._vcs_form_defaults(self.db_repo_name) | |
|
90 | c.inherit_global_settings = defaults['inherit_global_settings'] | |
|
91 | ||
|
92 | data = render('rhodecode:templates/admin/repos/repo_edit.mako', | |
|
93 | self._get_template_context(c), self.request) | |
|
94 | html = formencode.htmlfill.render( | |
|
95 | data, | |
|
96 | defaults=defaults, | |
|
97 | encoding="UTF-8", | |
|
98 | force_defaults=False | |
|
99 | ) | |
|
100 | return Response(html) | |
|
101 | ||
|
102 | @LoginRequired() | |
|
103 | @HasRepoPermissionAnyDecorator('repository.admin') | |
|
104 | @CSRFRequired() | |
|
105 | @view_config( | |
|
106 | route_name='edit_repo_vcs_update', request_method='POST', | |
|
107 | renderer='rhodecode:templates/admin/repos/repo_edit.mako') | |
|
108 | def repo_settings_vcs_update(self): | |
|
109 | _ = self.request.translate | |
|
110 | c = self.load_default_context() | |
|
111 | c.active = 'vcs' | |
|
112 | ||
|
113 | model = VcsSettingsModel(repo=self.db_repo_name) | |
|
114 | c.global_svn_branch_patterns = model.get_global_svn_branch_patterns() | |
|
115 | c.global_svn_tag_patterns = model.get_global_svn_tag_patterns() | |
|
116 | c.svn_branch_patterns = model.get_repo_svn_branch_patterns() | |
|
117 | c.svn_tag_patterns = model.get_repo_svn_tag_patterns() | |
|
118 | ||
|
119 | defaults = self._vcs_form_defaults(self.db_repo_name) | |
|
120 | c.inherit_global_settings = defaults['inherit_global_settings'] | |
|
121 | ||
|
122 | application_form = RepoVcsSettingsForm(self.db_repo_name)() | |
|
123 | try: | |
|
124 | form_result = application_form.to_python(dict(self.request.POST)) | |
|
125 | except formencode.Invalid as errors: | |
|
126 | h.flash(_("Some form inputs contain invalid data."), | |
|
127 | category='error') | |
|
128 | ||
|
129 | data = render('rhodecode:templates/admin/repos/repo_edit.mako', | |
|
130 | self._get_template_context(c), self.request) | |
|
131 | html = formencode.htmlfill.render( | |
|
132 | data, | |
|
133 | defaults=errors.value, | |
|
134 | errors=errors.error_dict or {}, | |
|
135 | encoding="UTF-8", | |
|
136 | force_defaults=False | |
|
137 | ) | |
|
138 | return Response(html) | |
|
139 | ||
|
140 | try: | |
|
141 | inherit_global_settings = form_result['inherit_global_settings'] | |
|
142 | model.create_or_update_repo_settings( | |
|
143 | form_result, inherit_global_settings=inherit_global_settings) | |
|
144 | Session().commit() | |
|
145 | h.flash(_('Updated VCS settings'), category='success') | |
|
146 | except Exception: | |
|
147 | log.exception("Exception while updating settings") | |
|
148 | h.flash( | |
|
149 | _('Error occurred during updating repository VCS settings'), | |
|
150 | category='error') | |
|
151 | ||
|
152 | raise HTTPFound( | |
|
153 | h.route_path('edit_repo_vcs', repo_name=self.db_repo_name)) | |
|
154 | ||
|
155 | @LoginRequired() | |
|
156 | @HasRepoPermissionAnyDecorator('repository.admin') | |
|
157 | @CSRFRequired() | |
|
158 | @view_config( | |
|
159 | route_name='edit_repo_vcs_svn_pattern_delete', request_method='POST', | |
|
160 | renderer='json_ext', xhr=True) | |
|
161 | def repo_settings_delete_svn_pattern(self): | |
|
162 | self.load_default_context() | |
|
163 | delete_pattern_id = self.request.POST.get('delete_svn_pattern') | |
|
164 | model = VcsSettingsModel(repo=self.db_repo_name) | |
|
165 | try: | |
|
166 | model.delete_repo_svn_pattern(delete_pattern_id) | |
|
167 | except SettingNotFound: | |
|
168 | log.exception('Failed to delete SVN pattern') | |
|
169 | raise HTTPBadRequest() | |
|
170 | ||
|
171 | Session().commit() | |
|
172 | return True |
|
1 | NO CONTENT: new file 100644 |
@@ -205,6 +205,19 b' def admin_routes(config):' | |||
|
205 | 205 | name='edit_user_group_perms_summary_json', |
|
206 | 206 | pattern='/user_groups/{user_group_id:\d+}/edit/permissions_summary/json') |
|
207 | 207 | |
|
208 | # repos admin | |
|
209 | config.add_route( | |
|
210 | name='repos', | |
|
211 | pattern='/repos') | |
|
212 | ||
|
213 | config.add_route( | |
|
214 | name='repo_new', | |
|
215 | pattern='/repos/new') | |
|
216 | ||
|
217 | config.add_route( | |
|
218 | name='repo_create', | |
|
219 | pattern='/repos/create') | |
|
220 | ||
|
208 | 221 | |
|
209 | 222 | def includeme(config): |
|
210 | 223 | settings = config.get_settings() |
This diff has been collapsed as it changes many lines, (712 lines changed) Show them Hide them | |||
@@ -23,19 +23,19 b' import urllib' | |||
|
23 | 23 | import mock |
|
24 | 24 | import pytest |
|
25 | 25 | |
|
26 | from rhodecode.apps._base import ADMIN_PREFIX | |
|
26 | 27 | from rhodecode.lib import auth |
|
27 |
from rhodecode.lib.utils2 import safe_str |
|
|
28 | from rhodecode.lib.utils2 import safe_str | |
|
28 | 29 | from rhodecode.lib import helpers as h |
|
29 | 30 | from rhodecode.model.db import ( |
|
30 | 31 | Repository, RepoGroup, UserRepoToPerm, User, Permission) |
|
31 | 32 | from rhodecode.model.meta import Session |
|
32 | 33 | from rhodecode.model.repo import RepoModel |
|
33 | 34 | from rhodecode.model.repo_group import RepoGroupModel |
|
34 | from rhodecode.model.settings import SettingsModel, VcsSettingsModel | |
|
35 | 35 | from rhodecode.model.user import UserModel |
|
36 | 36 | from rhodecode.tests import ( |
|
37 |
login_user_session |
|
|
38 |
TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS |
|
|
37 | login_user_session, assert_session_flash, TEST_USER_ADMIN_LOGIN, | |
|
38 | TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS) | |
|
39 | 39 | from rhodecode.tests.fixture import Fixture, error_function |
|
40 | 40 | from rhodecode.tests.utils import AssertResponse, repo_on_filesystem |
|
41 | 41 | |
@@ -46,7 +46,10 b' def route_path(name, params=None, **kwar' | |||
|
46 | 46 | import urllib |
|
47 | 47 | |
|
48 | 48 | base_url = { |
|
49 |
'repo |
|
|
49 | 'repos': ADMIN_PREFIX + '/repos', | |
|
50 | 'repo_new': ADMIN_PREFIX + '/repos/new', | |
|
51 | 'repo_create': ADMIN_PREFIX + '/repos/create', | |
|
52 | ||
|
50 | 53 | 'repo_creating_check': '/{repo_name}/repo_creating_check', |
|
51 | 54 | }[name].format(**kwargs) |
|
52 | 55 | |
@@ -55,35 +58,48 b' def route_path(name, params=None, **kwar' | |||
|
55 | 58 | return base_url |
|
56 | 59 | |
|
57 | 60 | |
|
61 | def _get_permission_for_user(user, repo): | |
|
62 | perm = UserRepoToPerm.query()\ | |
|
63 | .filter(UserRepoToPerm.repository == | |
|
64 | Repository.get_by_repo_name(repo))\ | |
|
65 | .filter(UserRepoToPerm.user == User.get_by_username(user))\ | |
|
66 | .all() | |
|
67 | return perm | |
|
68 | ||
|
69 | ||
|
58 | 70 | @pytest.mark.usefixtures("app") |
|
59 | 71 | class TestAdminRepos(object): |
|
60 | 72 | |
|
61 | def test_index(self): | |
|
62 | self.app.get(url('repos')) | |
|
73 | def test_repo_list(self, autologin_user, user_util): | |
|
74 | repo = user_util.create_repo() | |
|
75 | response = self.app.get( | |
|
76 | route_path('repos'), status=200) | |
|
63 | 77 | |
|
64 | def test_create_page_restricted(self, autologin_user, backend): | |
|
78 | response.mustcontain(repo.repo_name) | |
|
79 | ||
|
80 | def test_create_page_restricted_to_single_backend(self, autologin_user, backend): | |
|
65 | 81 | with mock.patch('rhodecode.BACKENDS', {'git': 'git'}): |
|
66 |
response = self.app.get( |
|
|
82 | response = self.app.get(route_path('repo_new'), status=200) | |
|
67 | 83 | assert_response = AssertResponse(response) |
|
68 | 84 | element = assert_response.get_element('#repo_type') |
|
69 | 85 | assert element.text_content() == '\ngit\n' |
|
70 | 86 | |
|
71 | def test_create_page_non_restricted(self, autologin_user, backend): | |
|
72 |
response = self.app.get( |
|
|
87 | def test_create_page_non_restricted_backends(self, autologin_user, backend): | |
|
88 | response = self.app.get(route_path('repo_new'), status=200) | |
|
73 | 89 | assert_response = AssertResponse(response) |
|
74 | 90 | assert_response.element_contains('#repo_type', 'git') |
|
75 | 91 | assert_response.element_contains('#repo_type', 'svn') |
|
76 | 92 | assert_response.element_contains('#repo_type', 'hg') |
|
77 | 93 | |
|
78 |
@pytest.mark.parametrize( |
|
|
79 |
|
|
|
94 | @pytest.mark.parametrize( | |
|
95 | "suffix", [u'', u'xxa'], ids=['', 'non-ascii']) | |
|
80 | 96 | def test_create(self, autologin_user, backend, suffix, csrf_token): |
|
81 | 97 | repo_name_unicode = backend.new_repo_name(suffix=suffix) |
|
82 | 98 | repo_name = repo_name_unicode.encode('utf8') |
|
83 | 99 | description_unicode = u'description for newly created repo' + suffix |
|
84 | 100 | description = description_unicode.encode('utf8') |
|
85 | 101 | response = self.app.post( |
|
86 |
|
|
|
102 | route_path('repo_create'), | |
|
87 | 103 | fixture._get_repo_create_params( |
|
88 | 104 | repo_private=False, |
|
89 | 105 | repo_name=repo_name, |
@@ -95,12 +111,12 b' class TestAdminRepos(object):' | |||
|
95 | 111 | self.assert_repository_is_created_correctly( |
|
96 | 112 | repo_name, description, backend) |
|
97 | 113 | |
|
98 | def test_create_numeric(self, autologin_user, backend, csrf_token): | |
|
114 | def test_create_numeric_name(self, autologin_user, backend, csrf_token): | |
|
99 | 115 | numeric_repo = '1234' |
|
100 | 116 | repo_name = numeric_repo |
|
101 | 117 | description = 'description for newly created repo' + numeric_repo |
|
102 | 118 | self.app.post( |
|
103 |
|
|
|
119 | route_path('repo_create'), | |
|
104 | 120 | fixture._get_repo_create_params( |
|
105 | 121 | repo_private=False, |
|
106 | 122 | repo_name=repo_name, |
@@ -126,7 +142,7 b' class TestAdminRepos(object):' | |||
|
126 | 142 | [group_name, repo_name]) |
|
127 | 143 | description = u'description for newly created repo' |
|
128 | 144 | self.app.post( |
|
129 |
|
|
|
145 | route_path('repo_create'), | |
|
130 | 146 | fixture._get_repo_create_params( |
|
131 | 147 | repo_private=False, |
|
132 | 148 | repo_name=safe_str(repo_name), |
@@ -149,7 +165,7 b' class TestAdminRepos(object):' | |||
|
149 | 165 | RepoGroupModel().delete(group_name) |
|
150 | 166 | Session().commit() |
|
151 | 167 | |
|
152 | def test_create_in_group_numeric( | |
|
168 | def test_create_in_group_numeric_name( | |
|
153 | 169 | self, autologin_user, backend, csrf_token): |
|
154 | 170 | # create GROUP |
|
155 | 171 | group_name = 'sometest_%s' % backend.alias |
@@ -162,7 +178,7 b' class TestAdminRepos(object):' | |||
|
162 | 178 | repo_name_full = RepoGroup.url_sep().join([group_name, repo_name]) |
|
163 | 179 | description = 'description for newly created repo' |
|
164 | 180 | self.app.post( |
|
165 |
|
|
|
181 | route_path('repo_create'), | |
|
166 | 182 | fixture._get_repo_create_params( |
|
167 | 183 | repo_private=False, |
|
168 | 184 | repo_name=repo_name, |
@@ -221,7 +237,7 b' class TestAdminRepos(object):' | |||
|
221 | 237 | repo_name = 'ingroup' |
|
222 | 238 | description = 'description for newly created repo' |
|
223 | 239 | response = self.app.post( |
|
224 |
|
|
|
240 | route_path('repo_create'), | |
|
225 | 241 | fixture._get_repo_create_params( |
|
226 | 242 | repo_private=False, |
|
227 | 243 | repo_name=repo_name, |
@@ -238,7 +254,7 b' class TestAdminRepos(object):' | |||
|
238 | 254 | [group_name_allowed, repo_name]) |
|
239 | 255 | description = 'description for newly created repo' |
|
240 | 256 | response = self.app.post( |
|
241 |
|
|
|
257 | route_path('repo_create'), | |
|
242 | 258 | fixture._get_repo_create_params( |
|
243 | 259 | repo_private=False, |
|
244 | 260 | repo_name=repo_name, |
@@ -282,7 +298,7 b' class TestAdminRepos(object):' | |||
|
282 | 298 | repo_name_full = RepoGroup.url_sep().join([group_name, repo_name]) |
|
283 | 299 | description = 'description for newly created repo' |
|
284 | 300 | self.app.post( |
|
285 |
|
|
|
301 | route_path('repo_create'), | |
|
286 | 302 | fixture._get_repo_create_params( |
|
287 | 303 | repo_private=False, |
|
288 | 304 | repo_name=repo_name, |
@@ -326,7 +342,7 b' class TestAdminRepos(object):' | |||
|
326 | 342 | |
|
327 | 343 | repo_name = backend.new_repo_name() |
|
328 | 344 | response = self.app.post( |
|
329 |
|
|
|
345 | route_path('repo_create'), | |
|
330 | 346 | fixture._get_repo_create_params( |
|
331 | 347 | repo_private=False, |
|
332 | 348 | repo_name=repo_name, |
@@ -354,7 +370,7 b' class TestAdminRepos(object):' | |||
|
354 | 370 | repo_name = backend.new_repo_name() |
|
355 | 371 | description = 'description for newly created repo' |
|
356 | 372 | response = self.app.post( |
|
357 |
|
|
|
373 | route_path('repo_create'), | |
|
358 | 374 | fixture._get_repo_create_params( |
|
359 | 375 | repo_private=False, |
|
360 | 376 | repo_name=repo_name, |
@@ -370,7 +386,7 b' class TestAdminRepos(object):' | |||
|
370 | 386 | repo_name = backend.new_repo_name() |
|
371 | 387 | description = 'description for newly created repo' |
|
372 | 388 | response = self.app.post( |
|
373 |
|
|
|
389 | route_path('repo_create'), | |
|
374 | 390 | fixture._get_repo_create_params( |
|
375 | 391 | repo_private=False, |
|
376 | 392 | repo_name=repo_name, |
@@ -385,7 +401,7 b' class TestAdminRepos(object):' | |||
|
385 | 401 | repo_name = backend.new_repo_name() + ".git" |
|
386 | 402 | description = 'description for newly created repo' |
|
387 | 403 | response = self.app.post( |
|
388 |
|
|
|
404 | route_path('repo_create'), | |
|
389 | 405 | fixture._get_repo_create_params( |
|
390 | 406 | repo_private=False, |
|
391 | 407 | repo_name=repo_name, |
@@ -394,11 +410,8 b' class TestAdminRepos(object):' | |||
|
394 | 410 | csrf_token=csrf_token)) |
|
395 | 411 | response.mustcontain('Repository name cannot end with .git') |
|
396 | 412 | |
|
397 | def test_show(self, autologin_user, backend): | |
|
398 | self.app.get(url('repo', repo_name=backend.repo_name)) | |
|
399 | ||
|
400 | 413 | def test_default_user_cannot_access_private_repo_in_a_group( |
|
401 |
self, autologin_user, user_util, backend |
|
|
414 | self, autologin_user, user_util, backend): | |
|
402 | 415 | |
|
403 | 416 | group = user_util.create_repo_group() |
|
404 | 417 | |
@@ -434,7 +447,7 b' class TestAdminRepos(object):' | |||
|
434 | 447 | repo_name = backend.new_repo_name() |
|
435 | 448 | description = 'description for newly created repo' |
|
436 | 449 | response = self.app.post( |
|
437 |
|
|
|
450 | route_path('repo_create'), | |
|
438 | 451 | fixture._get_repo_create_params( |
|
439 | 452 | repo_private=False, |
|
440 | 453 | repo_name=repo_name, |
@@ -453,7 +466,7 b' class TestAdminRepos(object):' | |||
|
453 | 466 | description = 'description for newly created repo' |
|
454 | 467 | |
|
455 | 468 | response = self.app.post( |
|
456 |
|
|
|
469 | route_path('repo_create'), | |
|
457 | 470 | fixture._get_repo_create_params( |
|
458 | 471 | repo_private=False, |
|
459 | 472 | repo_name=repo_name, |
@@ -494,638 +507,3 b' class TestAdminRepos(object):' | |||
|
494 | 507 | response.mustcontain(backend.alias) |
|
495 | 508 | |
|
496 | 509 | assert repo_on_filesystem(repo_name) |
|
497 | ||
|
498 | ||
|
499 | @pytest.mark.usefixtures("app") | |
|
500 | class TestVcsSettings(object): | |
|
501 | FORM_DATA = { | |
|
502 | 'inherit_global_settings': False, | |
|
503 | 'hooks_changegroup_repo_size': False, | |
|
504 | 'hooks_changegroup_push_logger': False, | |
|
505 | 'hooks_outgoing_pull_logger': False, | |
|
506 | 'extensions_largefiles': False, | |
|
507 | 'extensions_evolve': False, | |
|
508 | 'phases_publish': 'False', | |
|
509 | 'rhodecode_pr_merge_enabled': False, | |
|
510 | 'rhodecode_use_outdated_comments': False, | |
|
511 | 'new_svn_branch': '', | |
|
512 | 'new_svn_tag': '' | |
|
513 | } | |
|
514 | ||
|
515 | @pytest.mark.skip_backends('svn') | |
|
516 | def test_global_settings_initial_values(self, autologin_user, backend): | |
|
517 | repo_name = backend.repo_name | |
|
518 | response = self.app.get(url('repo_vcs_settings', repo_name=repo_name)) | |
|
519 | ||
|
520 | expected_settings = ( | |
|
521 | 'rhodecode_use_outdated_comments', 'rhodecode_pr_merge_enabled', | |
|
522 | 'hooks_changegroup_repo_size', 'hooks_changegroup_push_logger', | |
|
523 | 'hooks_outgoing_pull_logger' | |
|
524 | ) | |
|
525 | for setting in expected_settings: | |
|
526 | self.assert_repo_value_equals_global_value(response, setting) | |
|
527 | ||
|
528 | def test_show_settings_requires_repo_admin_permission( | |
|
529 | self, backend, user_util, settings_util): | |
|
530 | repo = backend.create_repo() | |
|
531 | repo_name = repo.repo_name | |
|
532 | user = UserModel().get_by_username(TEST_USER_REGULAR_LOGIN) | |
|
533 | user_util.grant_user_permission_to_repo(repo, user, 'repository.admin') | |
|
534 | login_user_session( | |
|
535 | self.app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS) | |
|
536 | self.app.get(url('repo_vcs_settings', repo_name=repo_name), status=200) | |
|
537 | ||
|
538 | def test_inherit_global_settings_flag_is_true_by_default( | |
|
539 | self, autologin_user, backend): | |
|
540 | repo_name = backend.repo_name | |
|
541 | response = self.app.get(url('repo_vcs_settings', repo_name=repo_name)) | |
|
542 | ||
|
543 | assert_response = AssertResponse(response) | |
|
544 | element = assert_response.get_element('#inherit_global_settings') | |
|
545 | assert element.checked | |
|
546 | ||
|
547 | @pytest.mark.parametrize('checked_value', [True, False]) | |
|
548 | def test_inherit_global_settings_value( | |
|
549 | self, autologin_user, backend, checked_value, settings_util): | |
|
550 | repo = backend.create_repo() | |
|
551 | repo_name = repo.repo_name | |
|
552 | settings_util.create_repo_rhodecode_setting( | |
|
553 | repo, 'inherit_vcs_settings', checked_value, 'bool') | |
|
554 | response = self.app.get(url('repo_vcs_settings', repo_name=repo_name)) | |
|
555 | ||
|
556 | assert_response = AssertResponse(response) | |
|
557 | element = assert_response.get_element('#inherit_global_settings') | |
|
558 | assert element.checked == checked_value | |
|
559 | ||
|
560 | @pytest.mark.skip_backends('svn') | |
|
561 | def test_hooks_settings_are_created( | |
|
562 | self, autologin_user, backend, csrf_token): | |
|
563 | repo_name = backend.repo_name | |
|
564 | data = self.FORM_DATA.copy() | |
|
565 | data['csrf_token'] = csrf_token | |
|
566 | self.app.post( | |
|
567 | url('repo_vcs_settings', repo_name=repo_name), data, status=302) | |
|
568 | settings = SettingsModel(repo=repo_name) | |
|
569 | try: | |
|
570 | for section, key in VcsSettingsModel.HOOKS_SETTINGS: | |
|
571 | ui = settings.get_ui_by_section_and_key(section, key) | |
|
572 | assert ui.ui_active is False | |
|
573 | finally: | |
|
574 | self._cleanup_repo_settings(settings) | |
|
575 | ||
|
576 | def test_hooks_settings_are_not_created_for_svn( | |
|
577 | self, autologin_user, backend_svn, csrf_token): | |
|
578 | repo_name = backend_svn.repo_name | |
|
579 | data = self.FORM_DATA.copy() | |
|
580 | data['csrf_token'] = csrf_token | |
|
581 | self.app.post( | |
|
582 | url('repo_vcs_settings', repo_name=repo_name), data, status=302) | |
|
583 | settings = SettingsModel(repo=repo_name) | |
|
584 | try: | |
|
585 | for section, key in VcsSettingsModel.HOOKS_SETTINGS: | |
|
586 | ui = settings.get_ui_by_section_and_key(section, key) | |
|
587 | assert ui is None | |
|
588 | finally: | |
|
589 | self._cleanup_repo_settings(settings) | |
|
590 | ||
|
591 | @pytest.mark.skip_backends('svn') | |
|
592 | def test_hooks_settings_are_updated( | |
|
593 | self, autologin_user, backend, csrf_token): | |
|
594 | repo_name = backend.repo_name | |
|
595 | settings = SettingsModel(repo=repo_name) | |
|
596 | for section, key in VcsSettingsModel.HOOKS_SETTINGS: | |
|
597 | settings.create_ui_section_value(section, '', key=key, active=True) | |
|
598 | ||
|
599 | data = self.FORM_DATA.copy() | |
|
600 | data['csrf_token'] = csrf_token | |
|
601 | self.app.post( | |
|
602 | url('repo_vcs_settings', repo_name=repo_name), data, status=302) | |
|
603 | try: | |
|
604 | for section, key in VcsSettingsModel.HOOKS_SETTINGS: | |
|
605 | ui = settings.get_ui_by_section_and_key(section, key) | |
|
606 | assert ui.ui_active is False | |
|
607 | finally: | |
|
608 | self._cleanup_repo_settings(settings) | |
|
609 | ||
|
610 | def test_hooks_settings_are_not_updated_for_svn( | |
|
611 | self, autologin_user, backend_svn, csrf_token): | |
|
612 | repo_name = backend_svn.repo_name | |
|
613 | settings = SettingsModel(repo=repo_name) | |
|
614 | for section, key in VcsSettingsModel.HOOKS_SETTINGS: | |
|
615 | settings.create_ui_section_value(section, '', key=key, active=True) | |
|
616 | ||
|
617 | data = self.FORM_DATA.copy() | |
|
618 | data['csrf_token'] = csrf_token | |
|
619 | self.app.post( | |
|
620 | url('repo_vcs_settings', repo_name=repo_name), data, status=302) | |
|
621 | try: | |
|
622 | for section, key in VcsSettingsModel.HOOKS_SETTINGS: | |
|
623 | ui = settings.get_ui_by_section_and_key(section, key) | |
|
624 | assert ui.ui_active is True | |
|
625 | finally: | |
|
626 | self._cleanup_repo_settings(settings) | |
|
627 | ||
|
628 | @pytest.mark.skip_backends('svn') | |
|
629 | def test_pr_settings_are_created( | |
|
630 | self, autologin_user, backend, csrf_token): | |
|
631 | repo_name = backend.repo_name | |
|
632 | data = self.FORM_DATA.copy() | |
|
633 | data['csrf_token'] = csrf_token | |
|
634 | self.app.post( | |
|
635 | url('repo_vcs_settings', repo_name=repo_name), data, status=302) | |
|
636 | settings = SettingsModel(repo=repo_name) | |
|
637 | try: | |
|
638 | for name in VcsSettingsModel.GENERAL_SETTINGS: | |
|
639 | setting = settings.get_setting_by_name(name) | |
|
640 | assert setting.app_settings_value is False | |
|
641 | finally: | |
|
642 | self._cleanup_repo_settings(settings) | |
|
643 | ||
|
644 | def test_pr_settings_are_not_created_for_svn( | |
|
645 | self, autologin_user, backend_svn, csrf_token): | |
|
646 | repo_name = backend_svn.repo_name | |
|
647 | data = self.FORM_DATA.copy() | |
|
648 | data['csrf_token'] = csrf_token | |
|
649 | self.app.post( | |
|
650 | url('repo_vcs_settings', repo_name=repo_name), data, status=302) | |
|
651 | settings = SettingsModel(repo=repo_name) | |
|
652 | try: | |
|
653 | for name in VcsSettingsModel.GENERAL_SETTINGS: | |
|
654 | setting = settings.get_setting_by_name(name) | |
|
655 | assert setting is None | |
|
656 | finally: | |
|
657 | self._cleanup_repo_settings(settings) | |
|
658 | ||
|
659 | def test_pr_settings_creation_requires_repo_admin_permission( | |
|
660 | self, backend, user_util, settings_util, csrf_token): | |
|
661 | repo = backend.create_repo() | |
|
662 | repo_name = repo.repo_name | |
|
663 | ||
|
664 | logout_user_session(self.app, csrf_token) | |
|
665 | session = login_user_session( | |
|
666 | self.app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS) | |
|
667 | new_csrf_token = auth.get_csrf_token(session) | |
|
668 | ||
|
669 | user = UserModel().get_by_username(TEST_USER_REGULAR_LOGIN) | |
|
670 | repo = Repository.get_by_repo_name(repo_name) | |
|
671 | user_util.grant_user_permission_to_repo(repo, user, 'repository.admin') | |
|
672 | data = self.FORM_DATA.copy() | |
|
673 | data['csrf_token'] = new_csrf_token | |
|
674 | settings = SettingsModel(repo=repo_name) | |
|
675 | ||
|
676 | try: | |
|
677 | self.app.post( | |
|
678 | url('repo_vcs_settings', repo_name=repo_name), data, | |
|
679 | status=302) | |
|
680 | finally: | |
|
681 | self._cleanup_repo_settings(settings) | |
|
682 | ||
|
683 | @pytest.mark.skip_backends('svn') | |
|
684 | def test_pr_settings_are_updated( | |
|
685 | self, autologin_user, backend, csrf_token): | |
|
686 | repo_name = backend.repo_name | |
|
687 | settings = SettingsModel(repo=repo_name) | |
|
688 | for name in VcsSettingsModel.GENERAL_SETTINGS: | |
|
689 | settings.create_or_update_setting(name, True, 'bool') | |
|
690 | ||
|
691 | data = self.FORM_DATA.copy() | |
|
692 | data['csrf_token'] = csrf_token | |
|
693 | self.app.post( | |
|
694 | url('repo_vcs_settings', repo_name=repo_name), data, status=302) | |
|
695 | try: | |
|
696 | for name in VcsSettingsModel.GENERAL_SETTINGS: | |
|
697 | setting = settings.get_setting_by_name(name) | |
|
698 | assert setting.app_settings_value is False | |
|
699 | finally: | |
|
700 | self._cleanup_repo_settings(settings) | |
|
701 | ||
|
702 | def test_pr_settings_are_not_updated_for_svn( | |
|
703 | self, autologin_user, backend_svn, csrf_token): | |
|
704 | repo_name = backend_svn.repo_name | |
|
705 | settings = SettingsModel(repo=repo_name) | |
|
706 | for name in VcsSettingsModel.GENERAL_SETTINGS: | |
|
707 | settings.create_or_update_setting(name, True, 'bool') | |
|
708 | ||
|
709 | data = self.FORM_DATA.copy() | |
|
710 | data['csrf_token'] = csrf_token | |
|
711 | self.app.post( | |
|
712 | url('repo_vcs_settings', repo_name=repo_name), data, status=302) | |
|
713 | try: | |
|
714 | for name in VcsSettingsModel.GENERAL_SETTINGS: | |
|
715 | setting = settings.get_setting_by_name(name) | |
|
716 | assert setting.app_settings_value is True | |
|
717 | finally: | |
|
718 | self._cleanup_repo_settings(settings) | |
|
719 | ||
|
720 | def test_svn_settings_are_created( | |
|
721 | self, autologin_user, backend_svn, csrf_token, settings_util): | |
|
722 | repo_name = backend_svn.repo_name | |
|
723 | data = self.FORM_DATA.copy() | |
|
724 | data['new_svn_tag'] = 'svn-tag' | |
|
725 | data['new_svn_branch'] = 'svn-branch' | |
|
726 | data['csrf_token'] = csrf_token | |
|
727 | ||
|
728 | # Create few global settings to make sure that uniqueness validators | |
|
729 | # are not triggered | |
|
730 | settings_util.create_rhodecode_ui( | |
|
731 | VcsSettingsModel.SVN_BRANCH_SECTION, 'svn-branch') | |
|
732 | settings_util.create_rhodecode_ui( | |
|
733 | VcsSettingsModel.SVN_TAG_SECTION, 'svn-tag') | |
|
734 | ||
|
735 | self.app.post( | |
|
736 | url('repo_vcs_settings', repo_name=repo_name), data, status=302) | |
|
737 | settings = SettingsModel(repo=repo_name) | |
|
738 | try: | |
|
739 | svn_branches = settings.get_ui_by_section( | |
|
740 | VcsSettingsModel.SVN_BRANCH_SECTION) | |
|
741 | svn_branch_names = [b.ui_value for b in svn_branches] | |
|
742 | svn_tags = settings.get_ui_by_section( | |
|
743 | VcsSettingsModel.SVN_TAG_SECTION) | |
|
744 | svn_tag_names = [b.ui_value for b in svn_tags] | |
|
745 | assert 'svn-branch' in svn_branch_names | |
|
746 | assert 'svn-tag' in svn_tag_names | |
|
747 | finally: | |
|
748 | self._cleanup_repo_settings(settings) | |
|
749 | ||
|
750 | def test_svn_settings_are_unique( | |
|
751 | self, autologin_user, backend_svn, csrf_token, settings_util): | |
|
752 | repo = backend_svn.repo | |
|
753 | repo_name = repo.repo_name | |
|
754 | data = self.FORM_DATA.copy() | |
|
755 | data['new_svn_tag'] = 'test_tag' | |
|
756 | data['new_svn_branch'] = 'test_branch' | |
|
757 | data['csrf_token'] = csrf_token | |
|
758 | settings_util.create_repo_rhodecode_ui( | |
|
759 | repo, VcsSettingsModel.SVN_BRANCH_SECTION, 'test_branch') | |
|
760 | settings_util.create_repo_rhodecode_ui( | |
|
761 | repo, VcsSettingsModel.SVN_TAG_SECTION, 'test_tag') | |
|
762 | ||
|
763 | response = self.app.post( | |
|
764 | url('repo_vcs_settings', repo_name=repo_name), data, status=200) | |
|
765 | response.mustcontain('Pattern already exists') | |
|
766 | ||
|
767 | def test_svn_settings_with_empty_values_are_not_created( | |
|
768 | self, autologin_user, backend_svn, csrf_token): | |
|
769 | repo_name = backend_svn.repo_name | |
|
770 | data = self.FORM_DATA.copy() | |
|
771 | data['csrf_token'] = csrf_token | |
|
772 | self.app.post( | |
|
773 | url('repo_vcs_settings', repo_name=repo_name), data, status=302) | |
|
774 | settings = SettingsModel(repo=repo_name) | |
|
775 | try: | |
|
776 | svn_branches = settings.get_ui_by_section( | |
|
777 | VcsSettingsModel.SVN_BRANCH_SECTION) | |
|
778 | svn_tags = settings.get_ui_by_section( | |
|
779 | VcsSettingsModel.SVN_TAG_SECTION) | |
|
780 | assert len(svn_branches) == 0 | |
|
781 | assert len(svn_tags) == 0 | |
|
782 | finally: | |
|
783 | self._cleanup_repo_settings(settings) | |
|
784 | ||
|
785 | def test_svn_settings_are_shown_for_svn_repository( | |
|
786 | self, autologin_user, backend_svn, csrf_token): | |
|
787 | repo_name = backend_svn.repo_name | |
|
788 | response = self.app.get( | |
|
789 | url('repo_vcs_settings', repo_name=repo_name), status=200) | |
|
790 | response.mustcontain('Subversion Settings') | |
|
791 | ||
|
792 | @pytest.mark.skip_backends('svn') | |
|
793 | def test_svn_settings_are_not_created_for_not_svn_repository( | |
|
794 | self, autologin_user, backend, csrf_token): | |
|
795 | repo_name = backend.repo_name | |
|
796 | data = self.FORM_DATA.copy() | |
|
797 | data['csrf_token'] = csrf_token | |
|
798 | self.app.post( | |
|
799 | url('repo_vcs_settings', repo_name=repo_name), data, status=302) | |
|
800 | settings = SettingsModel(repo=repo_name) | |
|
801 | try: | |
|
802 | svn_branches = settings.get_ui_by_section( | |
|
803 | VcsSettingsModel.SVN_BRANCH_SECTION) | |
|
804 | svn_tags = settings.get_ui_by_section( | |
|
805 | VcsSettingsModel.SVN_TAG_SECTION) | |
|
806 | assert len(svn_branches) == 0 | |
|
807 | assert len(svn_tags) == 0 | |
|
808 | finally: | |
|
809 | self._cleanup_repo_settings(settings) | |
|
810 | ||
|
811 | @pytest.mark.skip_backends('svn') | |
|
812 | def test_svn_settings_are_shown_only_for_svn_repository( | |
|
813 | self, autologin_user, backend, csrf_token): | |
|
814 | repo_name = backend.repo_name | |
|
815 | response = self.app.get( | |
|
816 | url('repo_vcs_settings', repo_name=repo_name), status=200) | |
|
817 | response.mustcontain(no='Subversion Settings') | |
|
818 | ||
|
819 | def test_hg_settings_are_created( | |
|
820 | self, autologin_user, backend_hg, csrf_token): | |
|
821 | repo_name = backend_hg.repo_name | |
|
822 | data = self.FORM_DATA.copy() | |
|
823 | data['new_svn_tag'] = 'svn-tag' | |
|
824 | data['new_svn_branch'] = 'svn-branch' | |
|
825 | data['csrf_token'] = csrf_token | |
|
826 | self.app.post( | |
|
827 | url('repo_vcs_settings', repo_name=repo_name), data, status=302) | |
|
828 | settings = SettingsModel(repo=repo_name) | |
|
829 | try: | |
|
830 | largefiles_ui = settings.get_ui_by_section_and_key( | |
|
831 | 'extensions', 'largefiles') | |
|
832 | assert largefiles_ui.ui_active is False | |
|
833 | phases_ui = settings.get_ui_by_section_and_key( | |
|
834 | 'phases', 'publish') | |
|
835 | assert str2bool(phases_ui.ui_value) is False | |
|
836 | finally: | |
|
837 | self._cleanup_repo_settings(settings) | |
|
838 | ||
|
839 | def test_hg_settings_are_updated( | |
|
840 | self, autologin_user, backend_hg, csrf_token): | |
|
841 | repo_name = backend_hg.repo_name | |
|
842 | settings = SettingsModel(repo=repo_name) | |
|
843 | settings.create_ui_section_value( | |
|
844 | 'extensions', '', key='largefiles', active=True) | |
|
845 | settings.create_ui_section_value( | |
|
846 | 'phases', '1', key='publish', active=True) | |
|
847 | ||
|
848 | data = self.FORM_DATA.copy() | |
|
849 | data['csrf_token'] = csrf_token | |
|
850 | self.app.post( | |
|
851 | url('repo_vcs_settings', repo_name=repo_name), data, status=302) | |
|
852 | try: | |
|
853 | largefiles_ui = settings.get_ui_by_section_and_key( | |
|
854 | 'extensions', 'largefiles') | |
|
855 | assert largefiles_ui.ui_active is False | |
|
856 | phases_ui = settings.get_ui_by_section_and_key( | |
|
857 | 'phases', 'publish') | |
|
858 | assert str2bool(phases_ui.ui_value) is False | |
|
859 | finally: | |
|
860 | self._cleanup_repo_settings(settings) | |
|
861 | ||
|
862 | def test_hg_settings_are_shown_for_hg_repository( | |
|
863 | self, autologin_user, backend_hg, csrf_token): | |
|
864 | repo_name = backend_hg.repo_name | |
|
865 | response = self.app.get( | |
|
866 | url('repo_vcs_settings', repo_name=repo_name), status=200) | |
|
867 | response.mustcontain('Mercurial Settings') | |
|
868 | ||
|
869 | @pytest.mark.skip_backends('hg') | |
|
870 | def test_hg_settings_are_created_only_for_hg_repository( | |
|
871 | self, autologin_user, backend, csrf_token): | |
|
872 | repo_name = backend.repo_name | |
|
873 | data = self.FORM_DATA.copy() | |
|
874 | data['csrf_token'] = csrf_token | |
|
875 | self.app.post( | |
|
876 | url('repo_vcs_settings', repo_name=repo_name), data, status=302) | |
|
877 | settings = SettingsModel(repo=repo_name) | |
|
878 | try: | |
|
879 | largefiles_ui = settings.get_ui_by_section_and_key( | |
|
880 | 'extensions', 'largefiles') | |
|
881 | assert largefiles_ui is None | |
|
882 | phases_ui = settings.get_ui_by_section_and_key( | |
|
883 | 'phases', 'publish') | |
|
884 | assert phases_ui is None | |
|
885 | finally: | |
|
886 | self._cleanup_repo_settings(settings) | |
|
887 | ||
|
888 | @pytest.mark.skip_backends('hg') | |
|
889 | def test_hg_settings_are_shown_only_for_hg_repository( | |
|
890 | self, autologin_user, backend, csrf_token): | |
|
891 | repo_name = backend.repo_name | |
|
892 | response = self.app.get( | |
|
893 | url('repo_vcs_settings', repo_name=repo_name), status=200) | |
|
894 | response.mustcontain(no='Mercurial Settings') | |
|
895 | ||
|
896 | @pytest.mark.skip_backends('hg') | |
|
897 | def test_hg_settings_are_updated_only_for_hg_repository( | |
|
898 | self, autologin_user, backend, csrf_token): | |
|
899 | repo_name = backend.repo_name | |
|
900 | settings = SettingsModel(repo=repo_name) | |
|
901 | settings.create_ui_section_value( | |
|
902 | 'extensions', '', key='largefiles', active=True) | |
|
903 | settings.create_ui_section_value( | |
|
904 | 'phases', '1', key='publish', active=True) | |
|
905 | ||
|
906 | data = self.FORM_DATA.copy() | |
|
907 | data['csrf_token'] = csrf_token | |
|
908 | self.app.post( | |
|
909 | url('repo_vcs_settings', repo_name=repo_name), data, status=302) | |
|
910 | try: | |
|
911 | largefiles_ui = settings.get_ui_by_section_and_key( | |
|
912 | 'extensions', 'largefiles') | |
|
913 | assert largefiles_ui.ui_active is True | |
|
914 | phases_ui = settings.get_ui_by_section_and_key( | |
|
915 | 'phases', 'publish') | |
|
916 | assert phases_ui.ui_value == '1' | |
|
917 | finally: | |
|
918 | self._cleanup_repo_settings(settings) | |
|
919 | ||
|
920 | def test_per_repo_svn_settings_are_displayed( | |
|
921 | self, autologin_user, backend_svn, settings_util): | |
|
922 | repo = backend_svn.create_repo() | |
|
923 | repo_name = repo.repo_name | |
|
924 | branches = [ | |
|
925 | settings_util.create_repo_rhodecode_ui( | |
|
926 | repo, VcsSettingsModel.SVN_BRANCH_SECTION, | |
|
927 | 'branch_{}'.format(i)) | |
|
928 | for i in range(10)] | |
|
929 | tags = [ | |
|
930 | settings_util.create_repo_rhodecode_ui( | |
|
931 | repo, VcsSettingsModel.SVN_TAG_SECTION, 'tag_{}'.format(i)) | |
|
932 | for i in range(10)] | |
|
933 | ||
|
934 | response = self.app.get( | |
|
935 | url('repo_vcs_settings', repo_name=repo_name), status=200) | |
|
936 | assert_response = AssertResponse(response) | |
|
937 | for branch in branches: | |
|
938 | css_selector = '[name=branch_value_{}]'.format(branch.ui_id) | |
|
939 | element = assert_response.get_element(css_selector) | |
|
940 | assert element.value == branch.ui_value | |
|
941 | for tag in tags: | |
|
942 | css_selector = '[name=tag_ui_value_new_{}]'.format(tag.ui_id) | |
|
943 | element = assert_response.get_element(css_selector) | |
|
944 | assert element.value == tag.ui_value | |
|
945 | ||
|
946 | def test_per_repo_hg_and_pr_settings_are_not_displayed_for_svn( | |
|
947 | self, autologin_user, backend_svn, settings_util): | |
|
948 | repo = backend_svn.create_repo() | |
|
949 | repo_name = repo.repo_name | |
|
950 | response = self.app.get( | |
|
951 | url('repo_vcs_settings', repo_name=repo_name), status=200) | |
|
952 | response.mustcontain(no='<label>Hooks:</label>') | |
|
953 | response.mustcontain(no='<label>Pull Request Settings:</label>') | |
|
954 | ||
|
955 | def test_inherit_global_settings_value_is_saved( | |
|
956 | self, autologin_user, backend, csrf_token): | |
|
957 | repo_name = backend.repo_name | |
|
958 | data = self.FORM_DATA.copy() | |
|
959 | data['csrf_token'] = csrf_token | |
|
960 | data['inherit_global_settings'] = True | |
|
961 | self.app.post( | |
|
962 | url('repo_vcs_settings', repo_name=repo_name), data, status=302) | |
|
963 | ||
|
964 | settings = SettingsModel(repo=repo_name) | |
|
965 | vcs_settings = VcsSettingsModel(repo=repo_name) | |
|
966 | try: | |
|
967 | assert vcs_settings.inherit_global_settings is True | |
|
968 | finally: | |
|
969 | self._cleanup_repo_settings(settings) | |
|
970 | ||
|
971 | def test_repo_cache_is_invalidated_when_settings_are_updated( | |
|
972 | self, autologin_user, backend, csrf_token): | |
|
973 | repo_name = backend.repo_name | |
|
974 | data = self.FORM_DATA.copy() | |
|
975 | data['csrf_token'] = csrf_token | |
|
976 | data['inherit_global_settings'] = True | |
|
977 | settings = SettingsModel(repo=repo_name) | |
|
978 | ||
|
979 | invalidation_patcher = mock.patch( | |
|
980 | 'rhodecode.controllers.admin.repos.ScmModel.mark_for_invalidation') | |
|
981 | with invalidation_patcher as invalidation_mock: | |
|
982 | self.app.post( | |
|
983 | url('repo_vcs_settings', repo_name=repo_name), data, | |
|
984 | status=302) | |
|
985 | try: | |
|
986 | invalidation_mock.assert_called_once_with(repo_name, delete=True) | |
|
987 | finally: | |
|
988 | self._cleanup_repo_settings(settings) | |
|
989 | ||
|
990 | def test_other_settings_not_saved_inherit_global_settings_is_true( | |
|
991 | self, autologin_user, backend, csrf_token): | |
|
992 | repo_name = backend.repo_name | |
|
993 | data = self.FORM_DATA.copy() | |
|
994 | data['csrf_token'] = csrf_token | |
|
995 | data['inherit_global_settings'] = True | |
|
996 | self.app.post( | |
|
997 | url('repo_vcs_settings', repo_name=repo_name), data, status=302) | |
|
998 | ||
|
999 | settings = SettingsModel(repo=repo_name) | |
|
1000 | ui_settings = ( | |
|
1001 | VcsSettingsModel.HOOKS_SETTINGS + VcsSettingsModel.HG_SETTINGS) | |
|
1002 | ||
|
1003 | vcs_settings = [] | |
|
1004 | try: | |
|
1005 | for section, key in ui_settings: | |
|
1006 | ui = settings.get_ui_by_section_and_key(section, key) | |
|
1007 | if ui: | |
|
1008 | vcs_settings.append(ui) | |
|
1009 | vcs_settings.extend(settings.get_ui_by_section( | |
|
1010 | VcsSettingsModel.SVN_BRANCH_SECTION)) | |
|
1011 | vcs_settings.extend(settings.get_ui_by_section( | |
|
1012 | VcsSettingsModel.SVN_TAG_SECTION)) | |
|
1013 | for name in VcsSettingsModel.GENERAL_SETTINGS: | |
|
1014 | setting = settings.get_setting_by_name(name) | |
|
1015 | if setting: | |
|
1016 | vcs_settings.append(setting) | |
|
1017 | assert vcs_settings == [] | |
|
1018 | finally: | |
|
1019 | self._cleanup_repo_settings(settings) | |
|
1020 | ||
|
1021 | def test_delete_svn_branch_and_tag_patterns( | |
|
1022 | self, autologin_user, backend_svn, settings_util, csrf_token): | |
|
1023 | repo = backend_svn.create_repo() | |
|
1024 | repo_name = repo.repo_name | |
|
1025 | branch = settings_util.create_repo_rhodecode_ui( | |
|
1026 | repo, VcsSettingsModel.SVN_BRANCH_SECTION, 'test_branch', | |
|
1027 | cleanup=False) | |
|
1028 | tag = settings_util.create_repo_rhodecode_ui( | |
|
1029 | repo, VcsSettingsModel.SVN_TAG_SECTION, 'test_tag', cleanup=False) | |
|
1030 | data = { | |
|
1031 | '_method': 'delete', | |
|
1032 | 'csrf_token': csrf_token | |
|
1033 | } | |
|
1034 | for id_ in (branch.ui_id, tag.ui_id): | |
|
1035 | data['delete_svn_pattern'] = id_, | |
|
1036 | self.app.post( | |
|
1037 | url('repo_vcs_settings', repo_name=repo_name), data, | |
|
1038 | headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200) | |
|
1039 | settings = VcsSettingsModel(repo=repo_name) | |
|
1040 | assert settings.get_repo_svn_branch_patterns() == [] | |
|
1041 | ||
|
1042 | def test_delete_svn_branch_requires_repo_admin_permission( | |
|
1043 | self, backend_svn, user_util, settings_util, csrf_token): | |
|
1044 | repo = backend_svn.create_repo() | |
|
1045 | repo_name = repo.repo_name | |
|
1046 | ||
|
1047 | logout_user_session(self.app, csrf_token) | |
|
1048 | session = login_user_session( | |
|
1049 | self.app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS) | |
|
1050 | csrf_token = auth.get_csrf_token(session) | |
|
1051 | ||
|
1052 | repo = Repository.get_by_repo_name(repo_name) | |
|
1053 | user = UserModel().get_by_username(TEST_USER_REGULAR_LOGIN) | |
|
1054 | user_util.grant_user_permission_to_repo(repo, user, 'repository.admin') | |
|
1055 | branch = settings_util.create_repo_rhodecode_ui( | |
|
1056 | repo, VcsSettingsModel.SVN_BRANCH_SECTION, 'test_branch', | |
|
1057 | cleanup=False) | |
|
1058 | data = { | |
|
1059 | '_method': 'delete', | |
|
1060 | 'csrf_token': csrf_token, | |
|
1061 | 'delete_svn_pattern': branch.ui_id | |
|
1062 | } | |
|
1063 | self.app.post( | |
|
1064 | url('repo_vcs_settings', repo_name=repo_name), data, | |
|
1065 | headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200) | |
|
1066 | ||
|
1067 | def test_delete_svn_branch_raises_400_when_not_found( | |
|
1068 | self, autologin_user, backend_svn, settings_util, csrf_token): | |
|
1069 | repo_name = backend_svn.repo_name | |
|
1070 | data = { | |
|
1071 | '_method': 'delete', | |
|
1072 | 'delete_svn_pattern': 123, | |
|
1073 | 'csrf_token': csrf_token | |
|
1074 | } | |
|
1075 | self.app.post( | |
|
1076 | url('repo_vcs_settings', repo_name=repo_name), data, | |
|
1077 | headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=400) | |
|
1078 | ||
|
1079 | def test_delete_svn_branch_raises_400_when_no_id_specified( | |
|
1080 | self, autologin_user, backend_svn, settings_util, csrf_token): | |
|
1081 | repo_name = backend_svn.repo_name | |
|
1082 | data = { | |
|
1083 | '_method': 'delete', | |
|
1084 | 'csrf_token': csrf_token | |
|
1085 | } | |
|
1086 | self.app.post( | |
|
1087 | url('repo_vcs_settings', repo_name=repo_name), data, | |
|
1088 | headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=400) | |
|
1089 | ||
|
1090 | def _cleanup_repo_settings(self, settings_model): | |
|
1091 | cleanup = [] | |
|
1092 | ui_settings = ( | |
|
1093 | VcsSettingsModel.HOOKS_SETTINGS + VcsSettingsModel.HG_SETTINGS) | |
|
1094 | ||
|
1095 | for section, key in ui_settings: | |
|
1096 | ui = settings_model.get_ui_by_section_and_key(section, key) | |
|
1097 | if ui: | |
|
1098 | cleanup.append(ui) | |
|
1099 | ||
|
1100 | cleanup.extend(settings_model.get_ui_by_section( | |
|
1101 | VcsSettingsModel.INHERIT_SETTINGS)) | |
|
1102 | cleanup.extend(settings_model.get_ui_by_section( | |
|
1103 | VcsSettingsModel.SVN_BRANCH_SECTION)) | |
|
1104 | cleanup.extend(settings_model.get_ui_by_section( | |
|
1105 | VcsSettingsModel.SVN_TAG_SECTION)) | |
|
1106 | ||
|
1107 | for name in VcsSettingsModel.GENERAL_SETTINGS: | |
|
1108 | setting = settings_model.get_setting_by_name(name) | |
|
1109 | if setting: | |
|
1110 | cleanup.append(setting) | |
|
1111 | ||
|
1112 | for object_ in cleanup: | |
|
1113 | Session().delete(object_) | |
|
1114 | Session().commit() | |
|
1115 | ||
|
1116 | def assert_repo_value_equals_global_value(self, response, setting): | |
|
1117 | assert_response = AssertResponse(response) | |
|
1118 | global_css_selector = '[name={}_inherited]'.format(setting) | |
|
1119 | repo_css_selector = '[name={}]'.format(setting) | |
|
1120 | repo_element = assert_response.get_element(repo_css_selector) | |
|
1121 | global_element = assert_response.get_element(global_css_selector) | |
|
1122 | assert repo_element.value == global_element.value | |
|
1123 | ||
|
1124 | ||
|
1125 | def _get_permission_for_user(user, repo): | |
|
1126 | perm = UserRepoToPerm.query()\ | |
|
1127 | .filter(UserRepoToPerm.repository == | |
|
1128 | Repository.get_by_repo_name(repo))\ | |
|
1129 | .filter(UserRepoToPerm.user == User.get_by_username(user))\ | |
|
1130 | .all() | |
|
1131 | return perm |
@@ -48,7 +48,7 b' class OpenSourceLicensesAdminSettingsVie' | |||
|
48 | 48 | c = self.load_default_context() |
|
49 | 49 | c.active = 'open_source' |
|
50 | 50 | c.navlist = navigation_list(self.request) |
|
51 | c.opensource_licenses = collections.OrderedDict( | |
|
52 | sorted(read_opensource_licenses().items(), key=lambda t: t[0])) | |
|
51 | items = sorted(read_opensource_licenses().items(), key=lambda t: t[0]) | |
|
52 | c.opensource_licenses = collections.OrderedDict(items) | |
|
53 | 53 | |
|
54 | 54 | return self._get_template_context(c) |
@@ -19,7 +19,6 b'' | |||
|
19 | 19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ |
|
20 | 20 | |
|
21 | 21 | import logging |
|
22 | import datetime | |
|
23 | 22 | |
|
24 | 23 | from pyramid.httpexceptions import HTTPFound |
|
25 | 24 | from pyramid.view import view_config |
@@ -28,12 +27,11 b' from rhodecode.model.scm import UserGrou' | |||
|
28 | 27 | |
|
29 | 28 | from rhodecode.apps._base import BaseAppView, DataGridAppView |
|
30 | 29 | from rhodecode.lib.auth import ( |
|
31 |
LoginRequired, |
|
|
30 | LoginRequired, NotAnonymous, | |
|
32 | 31 | HasUserGroupPermissionAnyDecorator) |
|
33 | 32 | from rhodecode.lib import helpers as h |
|
34 | 33 | from rhodecode.lib.utils import PartialRenderer |
|
35 |
from rhodecode.lib.utils2 import |
|
|
36 | from rhodecode.model.user_group import UserGroupModel | |
|
34 | from rhodecode.lib.utils2 import safe_unicode | |
|
37 | 35 | from rhodecode.model.db import ( |
|
38 | 36 | joinedload, or_, count, User, UserGroup, UserGroupMember, |
|
39 | 37 | UserGroupRepoToPerm, UserGroupRepoGroupToPerm) |
@@ -337,7 +337,76 b' def includeme(config):' | |||
|
337 | 337 | name='edit_repo_perms', |
|
338 | 338 | pattern='/{repo_name:.*?[^/]}/settings/permissions', repo_route=True) |
|
339 | 339 | |
|
340 | # Repo Review Rules | |
|
340 | # Maintenance | |
|
341 | config.add_route( | |
|
342 | name='edit_repo_maintenance', | |
|
343 | pattern='/{repo_name:.*?[^/]}/settings/maintenance', repo_route=True) | |
|
344 | ||
|
345 | config.add_route( | |
|
346 | name='edit_repo_maintenance_execute', | |
|
347 | pattern='/{repo_name:.*?[^/]}/settings/maintenance/execute', repo_route=True) | |
|
348 | ||
|
349 | # Fields | |
|
350 | config.add_route( | |
|
351 | name='edit_repo_fields', | |
|
352 | pattern='/{repo_name:.*?[^/]}/settings/fields', repo_route=True) | |
|
353 | config.add_route( | |
|
354 | name='edit_repo_fields_create', | |
|
355 | pattern='/{repo_name:.*?[^/]}/settings/fields/create', repo_route=True) | |
|
356 | config.add_route( | |
|
357 | name='edit_repo_fields_delete', | |
|
358 | pattern='/{repo_name:.*?[^/]}/settings/fields/{field_id}/delete', repo_route=True) | |
|
359 | ||
|
360 | # Locking | |
|
361 | config.add_route( | |
|
362 | name='repo_edit_toggle_locking', | |
|
363 | pattern='/{repo_name:.*?[^/]}/settings/toggle_locking', repo_route=True) | |
|
364 | ||
|
365 | # Remote | |
|
366 | config.add_route( | |
|
367 | name='edit_repo_remote', | |
|
368 | pattern='/{repo_name:.*?[^/]}/settings/remote', repo_route=True) | |
|
369 | config.add_route( | |
|
370 | name='edit_repo_remote_pull', | |
|
371 | pattern='/{repo_name:.*?[^/]}/settings/remote/pull', repo_route=True) | |
|
372 | ||
|
373 | ||
|
374 | # Statistics | |
|
375 | config.add_route( | |
|
376 | name='edit_repo_statistics', | |
|
377 | pattern='/{repo_name:.*?[^/]}/settings/statistics', repo_route=True) | |
|
378 | config.add_route( | |
|
379 | name='edit_repo_statistics_reset', | |
|
380 | pattern='/{repo_name:.*?[^/]}/settings/statistics/update', repo_route=True) | |
|
381 | ||
|
382 | # Issue trackers | |
|
383 | config.add_route( | |
|
384 | name='edit_repo_issuetracker', | |
|
385 | pattern='/{repo_name:.*?[^/]}/settings/issue_trackers', repo_route=True) | |
|
386 | config.add_route( | |
|
387 | name='edit_repo_issuetracker_test', | |
|
388 | pattern='/{repo_name:.*?[^/]}/settings/issue_trackers/test', repo_route=True) | |
|
389 | config.add_route( | |
|
390 | name='edit_repo_issuetracker_delete', | |
|
391 | pattern='/{repo_name:.*?[^/]}/settings/issue_trackers/delete', repo_route=True) | |
|
392 | config.add_route( | |
|
393 | name='edit_repo_issuetracker_update', | |
|
394 | pattern='/{repo_name:.*?[^/]}/settings/issue_trackers/update', repo_route=True) | |
|
395 | ||
|
396 | # VCS Settings | |
|
397 | config.add_route( | |
|
398 | name='edit_repo_vcs', | |
|
399 | pattern='/{repo_name:.*?[^/]}/settings/vcs', repo_route=True) | |
|
400 | config.add_route( | |
|
401 | name='edit_repo_vcs_update', | |
|
402 | pattern='/{repo_name:.*?[^/]}/settings/vcs/update', repo_route=True) | |
|
403 | ||
|
404 | # svn pattern | |
|
405 | config.add_route( | |
|
406 | name='edit_repo_vcs_svn_pattern_delete', | |
|
407 | pattern='/{repo_name:.*?[^/]}/settings/vcs/svn_pattern/delete', repo_route=True) | |
|
408 | ||
|
409 | # Repo Review Rules (EE feature) | |
|
341 | 410 | config.add_route( |
|
342 | 411 | name='repo_reviewers', |
|
343 | 412 | pattern='/{repo_name:.*?[^/]}/settings/review/rules', repo_route=True) |
@@ -346,18 +415,9 b' def includeme(config):' | |||
|
346 | 415 | name='repo_default_reviewers_data', |
|
347 | 416 | pattern='/{repo_name:.*?[^/]}/settings/review/default-reviewers', repo_route=True) |
|
348 | 417 | |
|
349 | # Maintenance | |
|
350 | config.add_route( | |
|
351 | name='repo_maintenance', | |
|
352 | pattern='/{repo_name:.*?[^/]}/settings/maintenance', repo_route=True) | |
|
353 | ||
|
354 | config.add_route( | |
|
355 | name='repo_maintenance_execute', | |
|
356 | pattern='/{repo_name:.*?[^/]}/settings/maintenance/execute', repo_route=True) | |
|
357 | ||
|
358 | 418 | # Strip |
|
359 | 419 | config.add_route( |
|
360 | name='strip', | |
|
420 | name='edit_repo_strip', | |
|
361 | 421 | pattern='/{repo_name:.*?[^/]}/settings/strip', repo_route=True) |
|
362 | 422 | |
|
363 | 423 | config.add_route( |
@@ -24,23 +24,38 b' from rhodecode.lib.utils2 import md5' | |||
|
24 | 24 | from rhodecode.model.db import Repository |
|
25 | 25 | from rhodecode.model.meta import Session |
|
26 | 26 | from rhodecode.model.settings import SettingsModel, IssueTrackerSettingsModel |
|
27 | from rhodecode.tests import url | |
|
27 | ||
|
28 | ||
|
29 | def route_path(name, params=None, **kwargs): | |
|
30 | import urllib | |
|
31 | ||
|
32 | base_url = { | |
|
33 | 'repo_summary': '/{repo_name}', | |
|
34 | 'edit_repo_issuetracker': '/{repo_name}/settings/issue_trackers', | |
|
35 | 'edit_repo_issuetracker_test': '/{repo_name}/settings/issue_trackers/test', | |
|
36 | 'edit_repo_issuetracker_delete': '/{repo_name}/settings/issue_trackers/delete', | |
|
37 | 'edit_repo_issuetracker_update': '/{repo_name}/settings/issue_trackers/update', | |
|
38 | }[name].format(**kwargs) | |
|
39 | ||
|
40 | if params: | |
|
41 | base_url = '{}?{}'.format(base_url, urllib.urlencode(params)) | |
|
42 | return base_url | |
|
28 | 43 | |
|
29 | 44 | |
|
30 | 45 | @pytest.mark.usefixtures("app") |
|
31 | class TestAdminRepos: | |
|
46 | class TestRepoIssueTracker(object): | |
|
32 | 47 | def test_issuetracker_index(self, autologin_user, backend): |
|
33 | 48 | repo = backend.create_repo() |
|
34 |
response = self.app.get( |
|
|
49 | response = self.app.get(route_path('edit_repo_issuetracker', | |
|
35 | 50 | repo_name=repo.repo_name)) |
|
36 | 51 | assert response.status_code == 200 |
|
37 | 52 | |
|
38 | def test_add_issuetracker_patterns( | |
|
39 | self, autologin_user, backend, csrf_token, request): | |
|
53 | def test_add_and_test_issuetracker_patterns( | |
|
54 | self, autologin_user, backend, csrf_token, request, xhr_header): | |
|
40 | 55 | pattern = 'issuetracker_pat' |
|
41 | 56 | another_pattern = pattern+'1' |
|
42 | post_url = url('repo_issuetracker_save', | |
|
43 |
|
|
|
57 | post_url = route_path( | |
|
58 | 'edit_repo_issuetracker_update', repo_name=backend.repo.repo_name) | |
|
44 | 59 | post_data = { |
|
45 | 60 | 'new_pattern_pattern_0': pattern, |
|
46 | 61 | 'new_pattern_url_0': 'url', |
@@ -60,6 +75,17 b' class TestAdminRepos:' | |||
|
60 | 75 | self.another_uid = md5(another_pattern) |
|
61 | 76 | assert settings[self.another_uid]['pat'] == another_pattern |
|
62 | 77 | |
|
78 | # test pattern | |
|
79 | data = {'test_text': 'example of issuetracker_pat replacement', | |
|
80 | 'csrf_token': csrf_token} | |
|
81 | response = self.app.post( | |
|
82 | route_path('edit_repo_issuetracker_test', | |
|
83 | repo_name=backend.repo.repo_name), | |
|
84 | extra_environ=xhr_header, params=data) | |
|
85 | ||
|
86 | assert response.body == \ | |
|
87 | 'example of <a class="issue-tracker-link" href="url">prefix</a> replacement' | |
|
88 | ||
|
63 | 89 | @request.addfinalizer |
|
64 | 90 | def cleanup(): |
|
65 | 91 | self.settings_model.delete_entries(self.uid) |
@@ -76,8 +102,8 b' class TestAdminRepos:' | |||
|
76 | 102 | entry_key+old_uid, old_pattern, 'unicode') |
|
77 | 103 | Session().add(sett) |
|
78 | 104 | Session().commit() |
|
79 | post_url = url('repo_issuetracker_save', | |
|
80 |
|
|
|
105 | post_url = route_path( | |
|
106 | 'edit_repo_issuetracker_update', repo_name=backend.repo.repo_name) | |
|
81 | 107 | post_data = { |
|
82 | 108 | 'new_pattern_pattern_0': pattern, |
|
83 | 109 | 'new_pattern_url_0': 'url', |
@@ -92,7 +118,7 b' class TestAdminRepos:' | |||
|
92 | 118 | self.uid = md5(pattern) |
|
93 | 119 | assert settings[self.uid]['pat'] == pattern |
|
94 | 120 | with pytest.raises(KeyError): |
|
95 | settings[old_uid] | |
|
121 | key = settings[old_uid] | |
|
96 | 122 | |
|
97 | 123 | @request.addfinalizer |
|
98 | 124 | def cleanup(): |
@@ -110,10 +136,10 b' class TestAdminRepos:' | |||
|
110 | 136 | value=entry_key, type_='unicode', cleanup=False) |
|
111 | 137 | |
|
112 | 138 | self.app.post( |
|
113 | url('repo_issuetracker_delete', | |
|
139 | route_path( | |
|
140 | 'edit_repo_issuetracker_delete', | |
|
114 | 141 | repo_name=backend.repo.repo_name), |
|
115 | 142 | { |
|
116 | '_method': 'delete', | |
|
117 | 143 | 'uid': uid, |
|
118 | 144 | 'csrf_token': csrf_token |
|
119 | 145 | }, status=302) |
@@ -26,8 +26,7 b' from rhodecode.lib.vcs.exceptions import' | |||
|
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 |
|
|
|
30 | assert_session_flash) | |
|
29 | TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN, assert_session_flash) | |
|
31 | 30 | from rhodecode.tests.fixture import Fixture |
|
32 | 31 | |
|
33 | 32 | fixture = Fixture() |
@@ -41,6 +40,11 b' def route_path(name, params=None, **kwar' | |||
|
41 | 40 | 'edit_repo_advanced': '/{repo_name}/settings/advanced', |
|
42 | 41 | 'edit_repo_caches': '/{repo_name}/settings/caches', |
|
43 | 42 | 'edit_repo_perms': '/{repo_name}/settings/permissions', |
|
43 | 'edit_repo_vcs': '/{repo_name}/settings/vcs', | |
|
44 | 'edit_repo_issuetracker': '/{repo_name}/settings/issue_trackers', | |
|
45 | 'edit_repo_fields': '/{repo_name}/settings/fields', | |
|
46 | 'edit_repo_remote': '/{repo_name}/settings/remote', | |
|
47 | 'edit_repo_statistics': '/{repo_name}/settings/statistics', | |
|
44 | 48 | }[name].format(**kwargs) |
|
45 | 49 | |
|
46 | 50 | if params: |
@@ -64,6 +68,11 b' class TestAdminRepoSettings(object):' | |||
|
64 | 68 | 'edit_repo_caches', |
|
65 | 69 | 'edit_repo_perms', |
|
66 | 70 | 'edit_repo_advanced', |
|
71 | 'edit_repo_vcs', | |
|
72 | 'edit_repo_issuetracker', | |
|
73 | 'edit_repo_fields', | |
|
74 | 'edit_repo_remote', | |
|
75 | 'edit_repo_statistics', | |
|
67 | 76 | ]) |
|
68 | 77 | def test_show_page(self, urlname, app, backend): |
|
69 | 78 | app.get(route_path(urlname, repo_name=backend.repo_name), status=200) |
@@ -75,16 +84,6 b' class TestAdminRepoSettings(object):' | |||
|
75 | 84 | with scm_patcher: |
|
76 | 85 | self.app.get(route_path('edit_repo', repo_name=backend_hg.repo_name)) |
|
77 | 86 | |
|
78 | @pytest.mark.parametrize('urlname', [ | |
|
79 | 'repo_vcs_settings', | |
|
80 | 'repo_settings_issuetracker', | |
|
81 | 'edit_repo_fields', | |
|
82 | 'edit_repo_remote', | |
|
83 | 'edit_repo_statistics', | |
|
84 | ]) | |
|
85 | def test_show_page_pylons(self, urlname, app): | |
|
86 | app.get(url(urlname, repo_name=HG_REPO)) | |
|
87 | ||
|
88 | 87 | @pytest.mark.parametrize('update_settings', [ |
|
89 | 88 | {'repo_description': 'alter-desc'}, |
|
90 | 89 | {'repo_owner': TEST_USER_REGULAR_LOGIN}, |
@@ -24,7 +24,6 b' import pytest' | |||
|
24 | 24 | import rhodecode |
|
25 | 25 | from rhodecode.model.db import Repository |
|
26 | 26 | from rhodecode.model.settings import SettingsModel |
|
27 | from rhodecode.tests import url | |
|
28 | 27 | from rhodecode.tests.utils import AssertResponse |
|
29 | 28 | |
|
30 | 29 | |
@@ -33,6 +32,8 b' def route_path(name, params=None, **kwar' | |||
|
33 | 32 | |
|
34 | 33 | base_url = { |
|
35 | 34 | 'edit_repo': '/{repo_name}/settings', |
|
35 | 'edit_repo_vcs': '/{repo_name}/settings/vcs', | |
|
36 | 'edit_repo_vcs_update': '/{repo_name}/settings/vcs/update', | |
|
36 | 37 | }[name].format(**kwargs) |
|
37 | 38 | |
|
38 | 39 | if params: |
@@ -51,8 +52,8 b' class TestAdminRepoVcsSettings(object):' | |||
|
51 | 52 | if backend.alias not in setting_backends: |
|
52 | 53 | pytest.skip('Setting not available for backend {}'.format(backend)) |
|
53 | 54 | |
|
54 |
vcs_settings_url = |
|
|
55 |
'repo_vcs |
|
|
55 | vcs_settings_url = route_path( | |
|
56 | 'edit_repo_vcs', repo_name=backend.repo.repo_name) | |
|
56 | 57 | |
|
57 | 58 | with mock.patch.dict( |
|
58 | 59 | rhodecode.CONFIG, {'labs_settings_active': 'true'}): |
@@ -64,24 +65,6 b' class TestAdminRepoVcsSettings(object):' | |||
|
64 | 65 | @pytest.mark.parametrize('setting_name, setting_backends', [ |
|
65 | 66 | ('hg_use_rebase_for_merging', ['hg']), |
|
66 | 67 | ]) |
|
67 | def test_labs_settings_not_visible_if_disabled( | |
|
68 | self, setting_name, setting_backends, backend): | |
|
69 | if backend.alias not in setting_backends: | |
|
70 | pytest.skip('Setting not available for backend {}'.format(backend)) | |
|
71 | ||
|
72 | vcs_settings_url = url( | |
|
73 | 'repo_vcs_settings', repo_name=backend.repo.repo_name) | |
|
74 | ||
|
75 | with mock.patch.dict( | |
|
76 | rhodecode.CONFIG, {'labs_settings_active': 'false'}): | |
|
77 | response = self.app.get(vcs_settings_url) | |
|
78 | ||
|
79 | assertr = AssertResponse(response) | |
|
80 | assertr.no_element_exists('#rhodecode_{}'.format(setting_name)) | |
|
81 | ||
|
82 | @pytest.mark.parametrize('setting_name, setting_backends', [ | |
|
83 | ('hg_use_rebase_for_merging', ['hg']), | |
|
84 | ]) | |
|
85 | 68 | def test_update_boolean_settings( |
|
86 | 69 | self, csrf_token, setting_name, setting_backends, backend): |
|
87 | 70 | if backend.alias not in setting_backends: |
@@ -91,8 +74,8 b' class TestAdminRepoVcsSettings(object):' | |||
|
91 | 74 | repo_name = repo.repo_name |
|
92 | 75 | |
|
93 | 76 | settings_model = SettingsModel(repo=repo) |
|
94 |
vcs_settings_url = |
|
|
95 |
'repo_vcs_ |
|
|
77 | vcs_settings_url = route_path( | |
|
78 | 'edit_repo_vcs_update', repo_name=repo_name) | |
|
96 | 79 | |
|
97 | 80 | self.app.post( |
|
98 | 81 | vcs_settings_url, |
@@ -36,7 +36,7 b' from rhodecode.lib.auth import (' | |||
|
36 | 36 | from rhodecode.lib.compat import OrderedDict |
|
37 | 37 | from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError |
|
38 | 38 | import rhodecode.lib.helpers as h |
|
39 |
from rhodecode.lib.utils2 import safe_unicode |
|
|
39 | from rhodecode.lib.utils2 import safe_unicode | |
|
40 | 40 | from rhodecode.lib.vcs.backends.base import EmptyCommit |
|
41 | 41 | from rhodecode.lib.vcs.exceptions import ( |
|
42 | 42 | RepositoryError, CommitDoesNotExistError, NodeDoesNotExistError) |
@@ -29,10 +29,9 b' from webhelpers.feedgenerator import Rss' | |||
|
29 | 29 | from rhodecode.apps._base import RepoAppView |
|
30 | 30 | from rhodecode.lib import audit_logger |
|
31 | 31 | from rhodecode.lib import helpers as h |
|
32 | from rhodecode.lib.auth import (LoginRequired, HasRepoPermissionAnyDecorator, | |
|
33 | NotAnonymous, CSRFRequired) | |
|
32 | from rhodecode.lib.auth import ( | |
|
33 | LoginRequired, HasRepoPermissionAnyDecorator) | |
|
34 | 34 | from rhodecode.lib.diffs import DiffProcessor, LimitedDiffContainer |
|
35 | from rhodecode.lib.ext_json import json | |
|
36 | 35 | from rhodecode.lib.utils2 import str2bool, safe_int |
|
37 | 36 | from rhodecode.model.db import UserApiKeys, CacheKey |
|
38 | 37 |
@@ -23,8 +23,7 b' import logging' | |||
|
23 | 23 | from pyramid.view import view_config |
|
24 | 24 | |
|
25 | 25 | from rhodecode.apps._base import RepoAppView |
|
26 |
from rhodecode.lib.auth import |
|
|
27 | NotAnonymous) | |
|
26 | from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator | |
|
28 | 27 | from rhodecode.lib import repo_maintenance |
|
29 | 28 | |
|
30 | 29 | log = logging.getLogger(__name__) |
@@ -43,7 +42,7 b' class RepoMaintenanceView(RepoAppView):' | |||
|
43 | 42 | @LoginRequired() |
|
44 | 43 | @HasRepoPermissionAnyDecorator('repository.admin') |
|
45 | 44 | @view_config( |
|
46 | route_name='repo_maintenance', request_method='GET', | |
|
45 | route_name='edit_repo_maintenance', request_method='GET', | |
|
47 | 46 | renderer='rhodecode:templates/admin/repos/repo_edit.mako') |
|
48 | 47 | def repo_maintenance(self): |
|
49 | 48 | c = self.load_default_context() |
@@ -55,7 +54,7 b' class RepoMaintenanceView(RepoAppView):' | |||
|
55 | 54 | @LoginRequired() |
|
56 | 55 | @HasRepoPermissionAnyDecorator('repository.admin') |
|
57 | 56 | @view_config( |
|
58 | route_name='repo_maintenance_execute', request_method='GET', | |
|
57 | route_name='edit_repo_maintenance_execute', request_method='GET', | |
|
59 | 58 | renderer='json', xhr=True) |
|
60 | 59 | def repo_maintenance_execute(self): |
|
61 | 60 | c = self.load_default_context() |
@@ -20,23 +20,17 b'' | |||
|
20 | 20 | |
|
21 | 21 | import logging |
|
22 | 22 | |
|
23 | import deform | |
|
24 | 23 | from pyramid.httpexceptions import HTTPFound |
|
25 | 24 | from pyramid.view import view_config |
|
26 | 25 | |
|
27 | 26 | from rhodecode.apps._base import RepoAppView |
|
28 | from rhodecode.forms import RcForm | |
|
29 | 27 | from rhodecode.lib import helpers as h |
|
30 | 28 | from rhodecode.lib import audit_logger |
|
31 | 29 | from rhodecode.lib.auth import ( |
|
32 | LoginRequired, HasRepoPermissionAnyDecorator, | |
|
33 | HasRepoPermissionAllDecorator, CSRFRequired) | |
|
34 | from rhodecode.model.db import RepositoryField, RepoGroup | |
|
30 | LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired) | |
|
35 | 31 | from rhodecode.model.forms import RepoPermsForm |
|
36 | 32 | from rhodecode.model.meta import Session |
|
37 | 33 | from rhodecode.model.repo import RepoModel |
|
38 | from rhodecode.model.scm import RepoGroupList, ScmModel | |
|
39 | from rhodecode.model.validation_schema.schemas import repo_schema | |
|
40 | 34 | |
|
41 | 35 | log = logging.getLogger(__name__) |
|
42 | 36 | |
@@ -63,7 +57,7 b' class RepoSettingsPermissionsView(RepoAp' | |||
|
63 | 57 | return self._get_template_context(c) |
|
64 | 58 | |
|
65 | 59 | @LoginRequired() |
|
66 |
@HasRepoPermissionA |
|
|
60 | @HasRepoPermissionAnyDecorator('repository.admin') | |
|
67 | 61 | @CSRFRequired() |
|
68 | 62 | @view_config( |
|
69 | 63 | route_name='edit_repo_perms', request_method='POST', |
@@ -74,7 +68,7 b' class RepoSettingsPermissionsView(RepoAp' | |||
|
74 | 68 | c.active = 'permissions' |
|
75 | 69 | data = self.request.POST |
|
76 | 70 | # store private flag outside of HTML to verify if we can modify |
|
77 |
# default user permissions, prevents submi |
|
|
71 | # default user permissions, prevents submission of FAKE post data | |
|
78 | 72 | # into the form for private repos |
|
79 | 73 | data['repo_private'] = self.db_repo.private |
|
80 | 74 | form = RepoPermsForm()().to_python(data) |
@@ -95,4 +89,4 b' class RepoSettingsPermissionsView(RepoAp' | |||
|
95 | 89 | h.flash(_('Repository permissions updated'), category='success') |
|
96 | 90 | |
|
97 | 91 | raise HTTPFound( |
|
98 |
|
|
|
92 | h.route_path('edit_repo_perms', repo_name=self.db_repo_name)) |
@@ -29,9 +29,8 b' from rhodecode.forms import RcForm' | |||
|
29 | 29 | from rhodecode.lib import helpers as h |
|
30 | 30 | from rhodecode.lib import audit_logger |
|
31 | 31 | from rhodecode.lib.auth import ( |
|
32 | LoginRequired, HasRepoPermissionAnyDecorator, | |
|
33 | HasRepoPermissionAllDecorator, CSRFRequired) | |
|
34 | from rhodecode.model.db import RepositoryField, RepoGroup | |
|
32 | LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired) | |
|
33 | from rhodecode.model.db import RepositoryField, RepoGroup, Repository | |
|
35 | 34 | from rhodecode.model.meta import Session |
|
36 | 35 | from rhodecode.model.repo import RepoModel |
|
37 | 36 | from rhodecode.model.scm import RepoGroupList, ScmModel |
@@ -109,7 +108,7 b' class RepoSettingsView(RepoAppView):' | |||
|
109 | 108 | return self._get_template_context(c) |
|
110 | 109 | |
|
111 | 110 | @LoginRequired() |
|
112 |
@HasRepoPermissionA |
|
|
111 | @HasRepoPermissionAnyDecorator('repository.admin') | |
|
113 | 112 | @CSRFRequired() |
|
114 | 113 | @view_config( |
|
115 | 114 | route_name='edit_repo', request_method='POST', |
@@ -176,4 +175,80 b' class RepoSettingsView(RepoAppView):' | |||
|
176 | 175 | old_repo_name), category='error') |
|
177 | 176 | |
|
178 | 177 | raise HTTPFound( |
|
179 |
|
|
|
178 | h.route_path('edit_repo', repo_name=new_repo_name)) | |
|
179 | ||
|
180 | @LoginRequired() | |
|
181 | @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin') | |
|
182 | @view_config( | |
|
183 | route_name='repo_edit_toggle_locking', request_method='GET', | |
|
184 | renderer='rhodecode:templates/admin/repos/repo_edit.mako') | |
|
185 | def toggle_locking(self): | |
|
186 | """ | |
|
187 | Toggle locking of repository by simple GET call to url | |
|
188 | """ | |
|
189 | _ = self.request.translate | |
|
190 | repo = self.db_repo | |
|
191 | ||
|
192 | try: | |
|
193 | if repo.enable_locking: | |
|
194 | if repo.locked[0]: | |
|
195 | Repository.unlock(repo) | |
|
196 | action = _('Unlocked') | |
|
197 | else: | |
|
198 | Repository.lock( | |
|
199 | repo, self._rhodecode_user.user_id, | |
|
200 | lock_reason=Repository.LOCK_WEB) | |
|
201 | action = _('Locked') | |
|
202 | ||
|
203 | h.flash(_('Repository has been %s') % action, | |
|
204 | category='success') | |
|
205 | except Exception: | |
|
206 | log.exception("Exception during unlocking") | |
|
207 | h.flash(_('An error occurred during unlocking'), | |
|
208 | category='error') | |
|
209 | raise HTTPFound( | |
|
210 | h.route_path('repo_summary', repo_name=self.db_repo_name)) | |
|
211 | ||
|
212 | @LoginRequired() | |
|
213 | @HasRepoPermissionAnyDecorator('repository.admin') | |
|
214 | @view_config( | |
|
215 | route_name='edit_repo_statistics', request_method='GET', | |
|
216 | renderer='rhodecode:templates/admin/repos/repo_edit.mako') | |
|
217 | def edit_statistics_form(self): | |
|
218 | c = self.load_default_context() | |
|
219 | ||
|
220 | if self.db_repo.stats: | |
|
221 | # this is on what revision we ended up so we add +1 for count | |
|
222 | last_rev = self.db_repo.stats.stat_on_revision + 1 | |
|
223 | else: | |
|
224 | last_rev = 0 | |
|
225 | ||
|
226 | c.active = 'statistics' | |
|
227 | c.stats_revision = last_rev | |
|
228 | c.repo_last_rev = self.rhodecode_vcs_repo.count() | |
|
229 | ||
|
230 | if last_rev == 0 or c.repo_last_rev == 0: | |
|
231 | c.stats_percentage = 0 | |
|
232 | else: | |
|
233 | c.stats_percentage = '%.2f' % ( | |
|
234 | (float((last_rev)) / c.repo_last_rev) * 100) | |
|
235 | return self._get_template_context(c) | |
|
236 | ||
|
237 | @LoginRequired() | |
|
238 | @HasRepoPermissionAnyDecorator('repository.admin') | |
|
239 | @CSRFRequired() | |
|
240 | @view_config( | |
|
241 | route_name='edit_repo_statistics_reset', request_method='POST', | |
|
242 | renderer='rhodecode:templates/admin/repos/repo_edit.mako') | |
|
243 | def repo_statistics_reset(self): | |
|
244 | _ = self.request.translate | |
|
245 | ||
|
246 | try: | |
|
247 | RepoModel().delete_stats(self.db_repo_name) | |
|
248 | Session().commit() | |
|
249 | except Exception: | |
|
250 | log.exception('Edit statistics failure') | |
|
251 | h.flash(_('An error occurred during deletion of repository stats'), | |
|
252 | category='error') | |
|
253 | raise HTTPFound( | |
|
254 | h.route_path('edit_repo_statistics', repo_name=self.db_repo_name)) |
@@ -61,7 +61,7 b' class RepoSettingsView(RepoAppView):' | |||
|
61 | 61 | c.default_user_id = User.get_default_user().user_id |
|
62 | 62 | c.in_public_journal = UserFollowing.query() \ |
|
63 | 63 | .filter(UserFollowing.user_id == c.default_user_id) \ |
|
64 |
.filter(UserFollowing.follows_repository == |
|
|
64 | .filter(UserFollowing.follows_repository == self.db_repo).scalar() | |
|
65 | 65 | |
|
66 | 66 | c.has_origin_repo_read_perm = False |
|
67 | 67 | if self.db_repo.fork: |
@@ -24,8 +24,8 b' from pyramid.view import view_config' | |||
|
24 | 24 | from rhodecode.apps._base import RepoAppView |
|
25 | 25 | from rhodecode.lib import audit_logger |
|
26 | 26 | from rhodecode.lib import helpers as h |
|
27 | from rhodecode.lib.auth import (LoginRequired, HasRepoPermissionAnyDecorator, | |
|
28 | NotAnonymous, CSRFRequired) | |
|
27 | from rhodecode.lib.auth import ( | |
|
28 | LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired) | |
|
29 | 29 | from rhodecode.lib.ext_json import json |
|
30 | 30 | |
|
31 | 31 | log = logging.getLogger(__name__) |
@@ -44,7 +44,7 b' class StripView(RepoAppView):' | |||
|
44 | 44 | @LoginRequired() |
|
45 | 45 | @HasRepoPermissionAnyDecorator('repository.admin') |
|
46 | 46 | @view_config( |
|
47 | route_name='strip', request_method='GET', | |
|
47 | route_name='edit_repo_strip', request_method='GET', | |
|
48 | 48 | renderer='rhodecode:templates/admin/repos/repo_edit.mako') |
|
49 | 49 | def strip(self): |
|
50 | 50 | c = self.load_default_context() |
@@ -99,10 +99,10 b' class StripView(RepoAppView):' | |||
|
99 | 99 | continue |
|
100 | 100 | try: |
|
101 | 101 | ScmModel().strip( |
|
102 |
repo= |
|
|
102 | repo=self.db_repo, | |
|
103 | 103 | commit_id=commit['rev'], branch=commit['branch']) |
|
104 | 104 | log.info('Stripped commit %s from repo `%s` by %s' % ( |
|
105 |
commit['rev'], |
|
|
105 | commit['rev'], self.db_repo_name, user)) | |
|
106 | 106 | data[commit['rev']] = True |
|
107 | 107 | |
|
108 | 108 | audit_logger.store_web( |
@@ -22,12 +22,9 b' import logging' | |||
|
22 | 22 | import string |
|
23 | 23 | |
|
24 | 24 | from pyramid.view import view_config |
|
25 | ||
|
26 | 25 | from beaker.cache import cache_region |
|
27 | 26 | |
|
28 | ||
|
29 | 27 | from rhodecode.controllers import utils |
|
30 | ||
|
31 | 28 | from rhodecode.apps._base import RepoAppView |
|
32 | 29 | from rhodecode.config.conf import (LANGUAGES_EXTENSIONS_MAP) |
|
33 | 30 | from rhodecode.lib import caches, helpers as h |
@@ -74,11 +71,16 b' class RepoSummaryView(RepoAppView):' | |||
|
74 | 71 | log.debug("Searching for a README file.") |
|
75 | 72 | readme_node = ReadmeFinder(default_renderer).search(commit) |
|
76 | 73 | if readme_node: |
|
77 |
relative_url = |
|
|
78 |
' |
|
|
79 | commit_id=commit.raw_id, f_path=readme_node.path) | |
|
74 | relative_urls = { | |
|
75 | 'raw': h.route_path( | |
|
76 | 'repo_file_raw', repo_name=repo_name, | |
|
77 | commit_id=commit.raw_id, f_path=readme_node.path), | |
|
78 | 'standard': h.route_path( | |
|
79 | 'repo_files', repo_name=repo_name, | |
|
80 | commit_id=commit.raw_id, f_path=readme_node.path), | |
|
81 | } | |
|
80 | 82 | readme_data = self._render_readme_or_none( |
|
81 | commit, readme_node, relative_url) | |
|
83 | commit, readme_node, relative_urls) | |
|
82 | 84 | readme_filename = readme_node.path |
|
83 | 85 | return readme_data, readme_filename |
|
84 | 86 | |
@@ -103,15 +105,15 b' class RepoSummaryView(RepoAppView):' | |||
|
103 | 105 | log.exception( |
|
104 | 106 | "Problem getting commit when trying to render the README.") |
|
105 | 107 | |
|
106 | def _render_readme_or_none(self, commit, readme_node, relative_url): | |
|
108 | def _render_readme_or_none(self, commit, readme_node, relative_urls): | |
|
107 | 109 | log.debug( |
|
108 | 110 | 'Found README file `%s` rendering...', readme_node.path) |
|
109 | 111 | renderer = MarkupRenderer() |
|
110 | 112 | try: |
|
111 | 113 | html_source = renderer.render( |
|
112 | 114 | readme_node.content, filename=readme_node.path) |
|
113 | if relative_url: | |
|
114 | return relative_links(html_source, relative_url) | |
|
115 | if relative_urls: | |
|
116 | return relative_links(html_source, relative_urls) | |
|
115 | 117 | return html_source |
|
116 | 118 | except Exception: |
|
117 | 119 | log.exception( |
@@ -143,6 +143,8 b' def load_pyramid_environment(global_conf' | |||
|
143 | 143 | |
|
144 | 144 | # Store the settings to make them available to other modules. |
|
145 | 145 | rhodecode.PYRAMID_SETTINGS = settings_merged |
|
146 | # NOTE(marcink): needs to be enabled after full port to pyramid | |
|
147 | # rhodecode.CONFIG = config | |
|
146 | 148 | |
|
147 | 149 | # If this is a test run we prepare the test environment like |
|
148 | 150 | # creating a test database, test search index and test repositories. |
@@ -176,23 +176,6 b' def make_map(config):' | |||
|
176 | 176 | rmap.connect('ping', '%s/ping' % (ADMIN_PREFIX,), controller='home', action='ping') |
|
177 | 177 | rmap.connect('error_test', '%s/error_test' % (ADMIN_PREFIX,), controller='home', action='error_test') |
|
178 | 178 | |
|
179 | # ADMIN REPOSITORY ROUTES | |
|
180 | with rmap.submapper(path_prefix=ADMIN_PREFIX, | |
|
181 | controller='admin/repos') as m: | |
|
182 | m.connect('repos', '/repos', | |
|
183 | action='create', conditions={'method': ['POST']}) | |
|
184 | m.connect('repos', '/repos', | |
|
185 | action='index', conditions={'method': ['GET']}) | |
|
186 | m.connect('new_repo', '/create_repository', jsroute=True, | |
|
187 | action='create_repository', conditions={'method': ['GET']}) | |
|
188 | m.connect('delete_repo', '/repos/{repo_name}', | |
|
189 | action='delete', conditions={'method': ['DELETE']}, | |
|
190 | requirements=URL_NAME_REQUIREMENTS) | |
|
191 | m.connect('repo', '/repos/{repo_name}', | |
|
192 | action='show', conditions={'method': ['GET'], | |
|
193 | 'function': check_repo}, | |
|
194 | requirements=URL_NAME_REQUIREMENTS) | |
|
195 | ||
|
196 | 179 | # ADMIN REPOSITORY GROUPS ROUTES |
|
197 | 180 | with rmap.submapper(path_prefix=ADMIN_PREFIX, |
|
198 | 181 | controller='admin/repo_groups') as m: |
@@ -406,81 +389,5 b' def make_map(config):' | |||
|
406 | 389 | m.connect('my_account_password', '/my_account/password', |
|
407 | 390 | action='my_account_password', conditions={'method': ['GET']}) |
|
408 | 391 | |
|
409 | #========================================================================== | |
|
410 | # REPOSITORY ROUTES | |
|
411 | #========================================================================== | |
|
412 | ||
|
413 | # repo edit options | |
|
414 | rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields', | |
|
415 | controller='admin/repos', action='edit_fields', | |
|
416 | conditions={'method': ['GET'], 'function': check_repo}, | |
|
417 | requirements=URL_NAME_REQUIREMENTS) | |
|
418 | rmap.connect('create_repo_fields', '/{repo_name}/settings/fields/new', | |
|
419 | controller='admin/repos', action='create_repo_field', | |
|
420 | conditions={'method': ['PUT'], 'function': check_repo}, | |
|
421 | requirements=URL_NAME_REQUIREMENTS) | |
|
422 | rmap.connect('delete_repo_fields', '/{repo_name}/settings/fields/{field_id}', | |
|
423 | controller='admin/repos', action='delete_repo_field', | |
|
424 | conditions={'method': ['DELETE'], 'function': check_repo}, | |
|
425 | requirements=URL_NAME_REQUIREMENTS) | |
|
426 | ||
|
427 | rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle', | |
|
428 | controller='admin/repos', action='toggle_locking', | |
|
429 | conditions={'method': ['GET'], 'function': check_repo}, | |
|
430 | requirements=URL_NAME_REQUIREMENTS) | |
|
431 | ||
|
432 | rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote', | |
|
433 | controller='admin/repos', action='edit_remote_form', | |
|
434 | conditions={'method': ['GET'], 'function': check_repo}, | |
|
435 | requirements=URL_NAME_REQUIREMENTS) | |
|
436 | rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote', | |
|
437 | controller='admin/repos', action='edit_remote', | |
|
438 | conditions={'method': ['PUT'], 'function': check_repo}, | |
|
439 | requirements=URL_NAME_REQUIREMENTS) | |
|
440 | ||
|
441 | rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics', | |
|
442 | controller='admin/repos', action='edit_statistics_form', | |
|
443 | conditions={'method': ['GET'], 'function': check_repo}, | |
|
444 | requirements=URL_NAME_REQUIREMENTS) | |
|
445 | rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics', | |
|
446 | controller='admin/repos', action='edit_statistics', | |
|
447 | conditions={'method': ['PUT'], 'function': check_repo}, | |
|
448 | requirements=URL_NAME_REQUIREMENTS) | |
|
449 | rmap.connect('repo_settings_issuetracker', | |
|
450 | '/{repo_name}/settings/issue-tracker', | |
|
451 | controller='admin/repos', action='repo_issuetracker', | |
|
452 | conditions={'method': ['GET'], 'function': check_repo}, | |
|
453 | requirements=URL_NAME_REQUIREMENTS) | |
|
454 | rmap.connect('repo_issuetracker_test', | |
|
455 | '/{repo_name}/settings/issue-tracker/test', | |
|
456 | controller='admin/repos', action='repo_issuetracker_test', | |
|
457 | conditions={'method': ['POST'], 'function': check_repo}, | |
|
458 | requirements=URL_NAME_REQUIREMENTS) | |
|
459 | rmap.connect('repo_issuetracker_delete', | |
|
460 | '/{repo_name}/settings/issue-tracker/delete', | |
|
461 | controller='admin/repos', action='repo_issuetracker_delete', | |
|
462 | conditions={'method': ['DELETE'], 'function': check_repo}, | |
|
463 | requirements=URL_NAME_REQUIREMENTS) | |
|
464 | rmap.connect('repo_issuetracker_save', | |
|
465 | '/{repo_name}/settings/issue-tracker/save', | |
|
466 | controller='admin/repos', action='repo_issuetracker_save', | |
|
467 | conditions={'method': ['POST'], 'function': check_repo}, | |
|
468 | requirements=URL_NAME_REQUIREMENTS) | |
|
469 | rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs', | |
|
470 | controller='admin/repos', action='repo_settings_vcs_update', | |
|
471 | conditions={'method': ['POST'], 'function': check_repo}, | |
|
472 | requirements=URL_NAME_REQUIREMENTS) | |
|
473 | rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs', | |
|
474 | controller='admin/repos', action='repo_settings_vcs', | |
|
475 | conditions={'method': ['GET'], 'function': check_repo}, | |
|
476 | requirements=URL_NAME_REQUIREMENTS) | |
|
477 | rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs', | |
|
478 | controller='admin/repos', action='repo_delete_svn_pattern', | |
|
479 | conditions={'method': ['DELETE'], 'function': check_repo}, | |
|
480 | requirements=URL_NAME_REQUIREMENTS) | |
|
481 | rmap.connect('repo_pullrequest_settings', '/{repo_name}/settings/pullrequest', | |
|
482 | controller='admin/repos', action='repo_settings_pullrequest', | |
|
483 | conditions={'method': ['GET', 'POST'], 'function': check_repo}, | |
|
484 | requirements=URL_NAME_REQUIREMENTS) | |
|
485 | 392 | |
|
486 | 393 | return rmap |
@@ -32,6 +32,7 b' from formencode import htmlfill' | |||
|
32 | 32 | from pylons import request, tmpl_context as c, url, config |
|
33 | 33 | from pylons.controllers.util import redirect |
|
34 | 34 | from pylons.i18n.translation import _ |
|
35 | from pylons.decorators import jsonify | |
|
35 | 36 | from pyramid.threadlocal import get_current_registry |
|
36 | 37 | from webob.exc import HTTPBadRequest |
|
37 | 38 | |
@@ -47,7 +48,6 b' from rhodecode.lib.utils import repo2db_' | |||
|
47 | 48 | from rhodecode.lib.utils2 import ( |
|
48 | 49 | str2bool, safe_unicode, AttributeDict, safe_int) |
|
49 | 50 | from rhodecode.lib.compat import OrderedDict |
|
50 | from rhodecode.lib.utils import jsonify | |
|
51 | 51 | |
|
52 | 52 | from rhodecode.model.db import RhodeCodeUi, Repository |
|
53 | 53 | from rhodecode.model.forms import ApplicationSettingsForm, \ |
@@ -31,25 +31,19 b' from pylons import request, tmpl_context' | |||
|
31 | 31 | from pylons.controllers.util import redirect |
|
32 | 32 | from pylons.i18n.translation import _ |
|
33 | 33 | |
|
34 | from sqlalchemy.orm import joinedload | |
|
35 | ||
|
36 | 34 | from rhodecode.lib import auth |
|
37 | 35 | from rhodecode.lib import helpers as h |
|
38 | 36 | from rhodecode.lib import audit_logger |
|
39 | from rhodecode.lib.ext_json import json | |
|
40 | 37 | from rhodecode.lib.exceptions import UserGroupAssignedException,\ |
|
41 | 38 | RepoGroupAssignmentError |
|
42 | from rhodecode.lib.utils import jsonify | |
|
43 | 39 | from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int |
|
44 | 40 | from rhodecode.lib.auth import ( |
|
45 | 41 | LoginRequired, NotAnonymous, HasUserGroupPermissionAnyDecorator, |
|
46 | 42 | HasPermissionAnyDecorator) |
|
47 | 43 | from rhodecode.lib.base import BaseController, render |
|
48 | 44 | from rhodecode.model.permission import PermissionModel |
|
49 | from rhodecode.model.scm import UserGroupList | |
|
50 | 45 | from rhodecode.model.user_group import UserGroupModel |
|
51 |
from rhodecode.model.db import |
|
|
52 | User, UserGroup, UserGroupRepoToPerm, UserGroupRepoGroupToPerm) | |
|
46 | from rhodecode.model.db import User, UserGroup | |
|
53 | 47 | from rhodecode.model.forms import ( |
|
54 | 48 | UserGroupForm, UserGroupPermsForm, UserIndividualPermissionsForm, |
|
55 | 49 | UserPermissionsForm) |
@@ -34,15 +34,6 b' import pyramid.threadlocal' | |||
|
34 | 34 | from paste.auth.basic import AuthBasicAuthenticator |
|
35 | 35 | from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden, get_exception |
|
36 | 36 | from paste.httpheaders import WWW_AUTHENTICATE, AUTHORIZATION |
|
37 | from pylons import tmpl_context as c, request, url | |
|
38 | from pylons.controllers import WSGIController | |
|
39 | from pylons.controllers.util import redirect | |
|
40 | from pylons.i18n import translation | |
|
41 | # marcink: don't remove this import | |
|
42 | from pylons.templating import render_mako, pylons_globals, literal, cached_template | |
|
43 | from pylons.i18n.translation import _ | |
|
44 | from webob.exc import HTTPFound | |
|
45 | ||
|
46 | 37 | |
|
47 | 38 | import rhodecode |
|
48 | 39 | from rhodecode.authentication.base import VCS_TYPE |
@@ -55,13 +46,15 b' from rhodecode.lib.utils import (' | |||
|
55 | 46 | get_enabled_hook_classes) |
|
56 | 47 | from rhodecode.lib.utils2 import ( |
|
57 | 48 | str2bool, safe_unicode, AttributeDict, safe_int, md5, aslist) |
|
58 | from rhodecode.lib.vcs.exceptions import RepositoryRequirementError | |
|
59 | 49 | from rhodecode.model import meta |
|
60 | 50 | from rhodecode.model.db import Repository, User, ChangesetComment |
|
61 | 51 | from rhodecode.model.notification import NotificationModel |
|
62 | 52 | from rhodecode.model.scm import ScmModel |
|
63 | 53 | from rhodecode.model.settings import VcsSettingsModel, SettingsModel |
|
64 | 54 | |
|
55 | # NOTE(marcink): remove after base controller is no longer required | |
|
56 | from pylons.controllers import WSGIController | |
|
57 | from pylons.i18n import translation | |
|
65 | 58 | |
|
66 | 59 | log = logging.getLogger(__name__) |
|
67 | 60 | |
@@ -75,6 +68,9 b' def render(template_name, extra_vars=Non' | |||
|
75 | 68 | ``cache_expire``. |
|
76 | 69 | |
|
77 | 70 | """ |
|
71 | from pylons.templating import literal | |
|
72 | from pylons.templating import cached_template, pylons_globals | |
|
73 | ||
|
78 | 74 | # Create a render callable for the cache function |
|
79 | 75 | def render_template(): |
|
80 | 76 | # Pull in extra vars if needed |
@@ -411,10 +407,6 b' def attach_context_attributes(context, r' | |||
|
411 | 407 | 'refresh_time': 120 * 1000, |
|
412 | 408 | 'cutoff_limit': 1000 * 60 * 60 * 24 * 7 |
|
413 | 409 | }, |
|
414 | 'pylons_dispatch': { | |
|
415 | # 'controller': request.environ['pylons.routes_dict']['controller'], | |
|
416 | # 'action': request.environ['pylons.routes_dict']['action'], | |
|
417 | }, | |
|
418 | 410 | 'pyramid_dispatch': { |
|
419 | 411 | |
|
420 | 412 | }, |
@@ -512,6 +504,7 b' class BaseController(WSGIController):' | |||
|
512 | 504 | """ |
|
513 | 505 | # on each call propagate settings calls into global settings. |
|
514 | 506 | from pylons import config |
|
507 | from pylons import tmpl_context as c, request, url | |
|
515 | 508 | set_rhodecode_config(config) |
|
516 | 509 | attach_context_attributes(c, request, self._rhodecode_user.user_id) |
|
517 | 510 | |
@@ -531,6 +524,7 b' class BaseController(WSGIController):' | |||
|
531 | 524 | user_lang, self._rhodecode_user) |
|
532 | 525 | |
|
533 | 526 | def _dispatch_redirect(self, with_url, environ, start_response): |
|
527 | from webob.exc import HTTPFound | |
|
534 | 528 | resp = HTTPFound(with_url) |
|
535 | 529 | environ['SCRIPT_NAME'] = '' # handle prefix middleware |
|
536 | 530 | environ['PATH_INFO'] = with_url |
@@ -542,6 +536,7 b' class BaseController(WSGIController):' | |||
|
542 | 536 | # the request is routed to. This routing information is |
|
543 | 537 | # available in environ['pylons.routes_dict'] |
|
544 | 538 | from rhodecode.lib import helpers as h |
|
539 | from pylons import tmpl_context as c, request, url | |
|
545 | 540 | |
|
546 | 541 | # Provide the Pylons context to Pyramid's debugtoolbar if it asks |
|
547 | 542 | if environ.get('debugtoolbar.wants_pylons_context', False): |
@@ -620,93 +615,3 b' def bootstrap_request(**kwargs):' | |||
|
620 | 615 | |
|
621 | 616 | config = pyramid.testing.setUp(request=request) |
|
622 | 617 | add_events_routes(config) |
|
623 | ||
|
624 | ||
|
625 | class BaseRepoController(BaseController): | |
|
626 | """ | |
|
627 | Base class for controllers responsible for loading all needed data for | |
|
628 | repository loaded items are | |
|
629 | ||
|
630 | c.rhodecode_repo: instance of scm repository | |
|
631 | c.rhodecode_db_repo: instance of db | |
|
632 | c.repository_requirements_missing: shows that repository specific data | |
|
633 | could not be displayed due to the missing requirements | |
|
634 | c.repository_pull_requests: show number of open pull requests | |
|
635 | """ | |
|
636 | ||
|
637 | def __before__(self): | |
|
638 | super(BaseRepoController, self).__before__() | |
|
639 | if c.repo_name: # extracted from routes | |
|
640 | db_repo = Repository.get_by_repo_name(c.repo_name) | |
|
641 | if not db_repo: | |
|
642 | return | |
|
643 | ||
|
644 | log.debug( | |
|
645 | 'Found repository in database %s with state `%s`', | |
|
646 | safe_unicode(db_repo), safe_unicode(db_repo.repo_state)) | |
|
647 | route = getattr(request.environ.get('routes.route'), 'name', '') | |
|
648 | ||
|
649 | # allow to delete repos that are somehow damages in filesystem | |
|
650 | if route in ['delete_repo']: | |
|
651 | return | |
|
652 | ||
|
653 | if db_repo.repo_state in [Repository.STATE_PENDING]: | |
|
654 | if route in ['repo_creating_home']: | |
|
655 | return | |
|
656 | check_url = url('repo_creating_home', repo_name=c.repo_name) | |
|
657 | return redirect(check_url) | |
|
658 | ||
|
659 | self.rhodecode_db_repo = db_repo | |
|
660 | ||
|
661 | missing_requirements = False | |
|
662 | try: | |
|
663 | self.rhodecode_repo = self.rhodecode_db_repo.scm_instance() | |
|
664 | except RepositoryRequirementError as e: | |
|
665 | missing_requirements = True | |
|
666 | self._handle_missing_requirements(e) | |
|
667 | ||
|
668 | if self.rhodecode_repo is None and not missing_requirements: | |
|
669 | log.error('%s this repository is present in database but it ' | |
|
670 | 'cannot be created as an scm instance', c.repo_name) | |
|
671 | ||
|
672 | h.flash(_( | |
|
673 | "The repository at %(repo_name)s cannot be located.") % | |
|
674 | {'repo_name': c.repo_name}, | |
|
675 | category='error', ignore_duplicate=True) | |
|
676 | redirect(h.route_path('home')) | |
|
677 | ||
|
678 | # update last change according to VCS data | |
|
679 | if not missing_requirements: | |
|
680 | commit = db_repo.get_commit( | |
|
681 | pre_load=["author", "date", "message", "parents"]) | |
|
682 | db_repo.update_commit_cache(commit) | |
|
683 | ||
|
684 | # Prepare context | |
|
685 | c.rhodecode_db_repo = db_repo | |
|
686 | c.rhodecode_repo = self.rhodecode_repo | |
|
687 | c.repository_requirements_missing = missing_requirements | |
|
688 | ||
|
689 | self._update_global_counters(self.scm_model, db_repo) | |
|
690 | ||
|
691 | def _update_global_counters(self, scm_model, db_repo): | |
|
692 | """ | |
|
693 | Base variables that are exposed to every page of repository | |
|
694 | """ | |
|
695 | c.repository_pull_requests = scm_model.get_pull_requests(db_repo) | |
|
696 | ||
|
697 | def _handle_missing_requirements(self, error): | |
|
698 | self.rhodecode_repo = None | |
|
699 | log.error( | |
|
700 | 'Requirements are missing for repository %s: %s', | |
|
701 | c.repo_name, error.message) | |
|
702 | ||
|
703 | summary_url = h.route_path('repo_summary', repo_name=c.repo_name) | |
|
704 | statistics_url = url('edit_repo_statistics', repo_name=c.repo_name) | |
|
705 | settings_update_url = url('repo', repo_name=c.repo_name) | |
|
706 | path = request.path | |
|
707 | should_redirect = ( | |
|
708 | path not in (summary_url, settings_update_url) | |
|
709 | and '/settings' not in path or path == statistics_url | |
|
710 | ) | |
|
711 | if should_redirect: | |
|
712 | redirect(summary_url) |
@@ -852,30 +852,6 b' class BasePasterCommand(Command):' | |||
|
852 | 852 | initialize_database(config) |
|
853 | 853 | |
|
854 | 854 | |
|
855 | @decorator.decorator | |
|
856 | def jsonify(func, *args, **kwargs): | |
|
857 | """Action decorator that formats output for JSON | |
|
858 | ||
|
859 | Given a function that will return content, this decorator will turn | |
|
860 | the result into JSON, with a content-type of 'application/json' and | |
|
861 | output it. | |
|
862 | ||
|
863 | """ | |
|
864 | from pylons.decorators.util import get_pylons | |
|
865 | from rhodecode.lib.ext_json import json | |
|
866 | pylons = get_pylons(args) | |
|
867 | pylons.response.headers['Content-Type'] = 'application/json; charset=utf-8' | |
|
868 | data = func(*args, **kwargs) | |
|
869 | if isinstance(data, (list, tuple)): | |
|
870 | msg = "JSON responses with Array envelopes are susceptible to " \ | |
|
871 | "cross-site data leak attacks, see " \ | |
|
872 | "http://wiki.pylonshq.com/display/pylonsfaq/Warnings" | |
|
873 | warnings.warn(msg, Warning, 2) | |
|
874 | log.warning(msg) | |
|
875 | log.debug("Returning JSON wrapped action output") | |
|
876 | return json.dumps(data, encoding='utf-8') | |
|
877 | ||
|
878 | ||
|
879 | 855 | class PartialRenderer(object): |
|
880 | 856 | """ |
|
881 | 857 | Partial renderer used to render chunks of html used in datagrids |
@@ -18,10 +18,6 b'' | |||
|
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 | """ | |
|
22 | Repository model for rhodecode | |
|
23 | """ | |
|
24 | ||
|
25 | 21 | import logging |
|
26 | 22 | import os |
|
27 | 23 | import re |
@@ -55,7 +55,7 b' function setRCMouseBindings(repoName, re' | |||
|
55 | 55 | window.location = pyroutes.url('gists_new'); |
|
56 | 56 | }); |
|
57 | 57 | Mousetrap.bind(['n r'], function(e) { |
|
58 |
window.location = pyroutes.url(' |
|
|
58 | window.location = pyroutes.url('repo_new'); | |
|
59 | 59 | }); |
|
60 | 60 | |
|
61 | 61 | if (repoName && repoName != '') { |
@@ -12,7 +12,6 b'' | |||
|
12 | 12 | ******************************************************************************/ |
|
13 | 13 | function registerRCRoutes() { |
|
14 | 14 | // routes registration |
|
15 | pyroutes.register('new_repo', '/_admin/create_repository', []); | |
|
16 | 15 | pyroutes.register('edit_user', '/_admin/users/%(user_id)s/edit', ['user_id']); |
|
17 | 16 | pyroutes.register('favicon', '/favicon.ico', []); |
|
18 | 17 | pyroutes.register('robots', '/robots.txt', []); |
@@ -82,6 +81,9 b' function registerRCRoutes() {' | |||
|
82 | 81 | pyroutes.register('user_group_members_data', '/_admin/user_groups/%(user_group_id)s/members', ['user_group_id']); |
|
83 | 82 | pyroutes.register('edit_user_group_perms_summary', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary', ['user_group_id']); |
|
84 | 83 | pyroutes.register('edit_user_group_perms_summary_json', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary/json', ['user_group_id']); |
|
84 | pyroutes.register('repos', '/_admin/repos', []); | |
|
85 | pyroutes.register('repo_new', '/_admin/repos/new', []); | |
|
86 | pyroutes.register('repo_create', '/_admin/repos/create', []); | |
|
85 | 87 | pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []); |
|
86 | 88 | pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []); |
|
87 | 89 | pyroutes.register('channelstream_proxy', '/_channelstream', []); |
@@ -177,11 +179,26 b' function registerRCRoutes() {' | |||
|
177 | 179 | pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']); |
|
178 | 180 | pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']); |
|
179 | 181 | pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']); |
|
182 | pyroutes.register('edit_repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']); | |
|
183 | pyroutes.register('edit_repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']); | |
|
184 | pyroutes.register('edit_repo_fields', '/%(repo_name)s/settings/fields', ['repo_name']); | |
|
185 | pyroutes.register('edit_repo_fields_create', '/%(repo_name)s/settings/fields/create', ['repo_name']); | |
|
186 | pyroutes.register('edit_repo_fields_delete', '/%(repo_name)s/settings/fields/%(field_id)s/delete', ['repo_name', 'field_id']); | |
|
187 | pyroutes.register('repo_edit_toggle_locking', '/%(repo_name)s/settings/toggle_locking', ['repo_name']); | |
|
188 | pyroutes.register('edit_repo_remote', '/%(repo_name)s/settings/remote', ['repo_name']); | |
|
189 | pyroutes.register('edit_repo_remote_pull', '/%(repo_name)s/settings/remote/pull', ['repo_name']); | |
|
190 | pyroutes.register('edit_repo_statistics', '/%(repo_name)s/settings/statistics', ['repo_name']); | |
|
191 | pyroutes.register('edit_repo_statistics_reset', '/%(repo_name)s/settings/statistics/update', ['repo_name']); | |
|
192 | pyroutes.register('edit_repo_issuetracker', '/%(repo_name)s/settings/issue_trackers', ['repo_name']); | |
|
193 | pyroutes.register('edit_repo_issuetracker_test', '/%(repo_name)s/settings/issue_trackers/test', ['repo_name']); | |
|
194 | pyroutes.register('edit_repo_issuetracker_delete', '/%(repo_name)s/settings/issue_trackers/delete', ['repo_name']); | |
|
195 | pyroutes.register('edit_repo_issuetracker_update', '/%(repo_name)s/settings/issue_trackers/update', ['repo_name']); | |
|
196 | pyroutes.register('edit_repo_vcs', '/%(repo_name)s/settings/vcs', ['repo_name']); | |
|
197 | pyroutes.register('edit_repo_vcs_update', '/%(repo_name)s/settings/vcs/update', ['repo_name']); | |
|
198 | pyroutes.register('edit_repo_vcs_svn_pattern_delete', '/%(repo_name)s/settings/vcs/svn_pattern/delete', ['repo_name']); | |
|
180 | 199 | pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']); |
|
181 | 200 | pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/settings/review/default-reviewers', ['repo_name']); |
|
182 |
pyroutes.register(' |
|
|
183 | pyroutes.register('repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']); | |
|
184 | pyroutes.register('strip', '/%(repo_name)s/settings/strip', ['repo_name']); | |
|
201 | pyroutes.register('edit_repo_strip', '/%(repo_name)s/settings/strip', ['repo_name']); | |
|
185 | 202 | pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']); |
|
186 | 203 | pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']); |
|
187 | 204 | pyroutes.register('rss_feed_home', '/%(repo_name)s/feed/rss', ['repo_name']); |
@@ -10,9 +10,9 b'' | |||
|
10 | 10 | |
|
11 | 11 | <%def name="breadcrumbs_links()"> |
|
12 | 12 | %if c.rhodecode_user.is_admin: |
|
13 | ${h.link_to(_('Admin'),h.route_path('admin_home'))} | |
|
13 | ${h.link_to(_('Admin'), h.route_path('admin_home'))} | |
|
14 | 14 | » |
|
15 |
${h.link_to(_('Repositories'),h. |
|
|
15 | ${h.link_to(_('Repositories'), h.route_path('repos'))} | |
|
16 | 16 | %else: |
|
17 | 17 | ${_('Admin')} |
|
18 | 18 | » |
@@ -1,6 +1,6 b'' | |||
|
1 | 1 | ## -*- coding: utf-8 -*- |
|
2 | 2 | |
|
3 | ${h.secure_form(h.url('repos'))} | |
|
3 | ${h.secure_form(h.route_path('repo_create'), method='POST', request=request)} | |
|
4 | 4 | <div class="form"> |
|
5 | 5 | <!-- fields --> |
|
6 | 6 | <div class="fields"> |
@@ -52,24 +52,24 b'' | |||
|
52 | 52 | <a href="${h.route_path('edit_repo_advanced', repo_name=c.repo_name)}">${_('Advanced')}</a> |
|
53 | 53 | </li> |
|
54 | 54 | <li class="${'active' if c.active=='vcs' else ''}"> |
|
55 |
<a href="${h. |
|
|
55 | <a href="${h.route_path('edit_repo_vcs', repo_name=c.repo_name)}">${_('VCS')}</a> | |
|
56 | 56 | </li> |
|
57 | 57 | <li class="${'active' if c.active=='fields' else ''}"> |
|
58 |
<a href="${h. |
|
|
58 | <a href="${h.route_path('edit_repo_fields', repo_name=c.repo_name)}">${_('Extra Fields')}</a> | |
|
59 | 59 | </li> |
|
60 | 60 | <li class="${'active' if c.active=='issuetracker' else ''}"> |
|
61 |
<a href="${h. |
|
|
61 | <a href="${h.route_path('edit_repo_issuetracker', repo_name=c.repo_name)}">${_('Issue Tracker')}</a> | |
|
62 | 62 | </li> |
|
63 | 63 | <li class="${'active' if c.active=='caches' else ''}"> |
|
64 | 64 | <a href="${h.route_path('edit_repo_caches', repo_name=c.repo_name)}">${_('Caches')}</a> |
|
65 | 65 | </li> |
|
66 | 66 | %if c.repo_info.repo_type != 'svn': |
|
67 | 67 | <li class="${'active' if c.active=='remote' else ''}"> |
|
68 |
<a href="${h. |
|
|
68 | <a href="${h.route_path('edit_repo_remote', repo_name=c.repo_name)}">${_('Remote')}</a> | |
|
69 | 69 | </li> |
|
70 | 70 | %endif |
|
71 | 71 | <li class="${'active' if c.active=='statistics' else ''}"> |
|
72 |
<a href="${h. |
|
|
72 | <a href="${h.route_path('edit_repo_statistics', repo_name=c.repo_name)}">${_('Statistics')}</a> | |
|
73 | 73 | </li> |
|
74 | 74 | <li class="${'active' if c.active=='integrations' else ''}"> |
|
75 | 75 | <a href="${h.route_path('repo_integrations_home', repo_name=c.repo_name)}">${_('Integrations')}</a> |
@@ -80,10 +80,10 b'' | |||
|
80 | 80 | </li> |
|
81 | 81 | %endif |
|
82 | 82 | <li class="${'active' if c.active=='maintenance' else ''}"> |
|
83 | <a href="${h.route_path('repo_maintenance', repo_name=c.repo_name)}">${_('Maintenance')}</a> | |
|
83 | <a href="${h.route_path('edit_repo_maintenance', repo_name=c.repo_name)}">${_('Maintenance')}</a> | |
|
84 | 84 | </li> |
|
85 | 85 | <li class="${'active' if c.active=='strip' else ''}"> |
|
86 | <a href="${h.route_path('strip', repo_name=c.repo_name)}">${_('Strip')}</a> | |
|
86 | <a href="${h.route_path('edit_repo_strip', repo_name=c.repo_name)}">${_('Strip')}</a> | |
|
87 | 87 | </li> |
|
88 | 88 | |
|
89 | 89 | </ul> |
@@ -18,7 +18,7 b'' | |||
|
18 | 18 | <td class="td-hash">${field.field_key}</td> |
|
19 | 19 | <td class="td-type">${field.field_type}</td> |
|
20 | 20 | <td class="td-action"> |
|
21 |
${h.secure_form(h. |
|
|
21 | ${h.secure_form(h.route_path('edit_repo_fields_delete', repo_name=c.repo_info.repo_name, field_id=field.repo_field_id), method='POST', request=request)} | |
|
22 | 22 | ${h.hidden('del_repo_field',field.repo_field_id)} |
|
23 | 23 | <button class="btn btn-link btn-danger" type="submit" |
|
24 | 24 | onclick="return confirm('${_('Confirm to delete this field: %s') % field.field_key}');"> |
@@ -31,7 +31,7 b'' | |||
|
31 | 31 | </table> |
|
32 | 32 | </div> |
|
33 | 33 | %endif |
|
34 |
${h.secure_form(h. |
|
|
34 | ${h.secure_form(h.route_path('edit_repo_fields_create', repo_name=c.repo_name), method='POST', request=request)} | |
|
35 | 35 | <div class="form"> |
|
36 | 36 | <!-- fields --> |
|
37 | 37 | <div class="fields"> |
@@ -1,7 +1,7 b'' | |||
|
1 | 1 | <%namespace name="its" file="/base/issue_tracker_settings.mako"/> |
|
2 | 2 | |
|
3 | 3 | <div id="repo_issue_tracker" class="${'inherited' if c.settings_model.inherit_global_settings else ''}"> |
|
4 |
${h.secure_form(h. |
|
|
4 | ${h.secure_form(h.route_path('edit_repo_issuetracker_update', repo_name=c.repo_name), id="inherit-form", method='POST', request=request)} | |
|
5 | 5 | <div class="panel panel-default panel-body"> |
|
6 | 6 | <div class="fields"> |
|
7 | 7 | <div class="field"> |
@@ -72,8 +72,8 b'' | |||
|
72 | 72 | <div class="panel-body"> |
|
73 | 73 | ${its.issue_tracker_settings_table( |
|
74 | 74 | patterns=c.repo_patterns.items(), |
|
75 |
form_url=h. |
|
|
76 |
delete_url=h. |
|
|
75 | form_url=h.route_path('edit_repo_issuetracker', repo_name=c.repo_info.repo_name), | |
|
76 | delete_url=h.route_path('edit_repo_issuetracker_delete', repo_name=c.repo_info.repo_name) | |
|
77 | 77 | )} |
|
78 | 78 | <div class="buttons"> |
|
79 | 79 | <button type="submit" class="btn btn-primary save-inheritance" id="save">${_('Save')}</button> |
@@ -92,7 +92,7 b'' | |||
|
92 | 92 | </div> |
|
93 | 93 | <div class="panel-body"> |
|
94 | 94 | ${its.issue_tracker_new_row()} |
|
95 |
${its.issue_tracker_settings_test(test_url=h. |
|
|
95 | ${its.issue_tracker_settings_test(test_url=h.route_path('edit_repo_issuetracker_test', repo_name=c.repo_info.repo_name))} | |
|
96 | 96 | </div> |
|
97 | 97 | </div> |
|
98 | 98 |
@@ -19,7 +19,7 b'' | |||
|
19 | 19 | </code> |
|
20 | 20 | </p> |
|
21 | 21 | |
|
22 |
${h.secure_form(h. |
|
|
22 | ${h.secure_form(h.route_path('edit_repo_remote_pull', repo_name=c.repo_name), method='POST', request=request)} | |
|
23 | 23 | <div class="form"> |
|
24 | 24 | <div class="fields"> |
|
25 | 25 | ${h.submit('remote_pull_%s' % c.repo_info.repo_name,_('Pull changes from remote location'),class_="btn btn-small",onclick="return confirm('"+_('Confirm to pull changes from remote side')+"');")} |
@@ -83,7 +83,7 b'' | |||
|
83 | 83 | ${h.hidden('repo_clone_uri_change', 'NEW')} |
|
84 | 84 | %endif |
|
85 | 85 | <p id="alter_clone_uri_help_block" class="help-block"> |
|
86 |
<% pull_link = h.literal(h.link_to('remote sync', h. |
|
|
86 | <% pull_link = h.literal(h.link_to('remote sync', h.route_path('edit_repo_remote', repo_name=c.repo_name))) %> | |
|
87 | 87 | ${_('http[s] url where from repository was imported, this field can used for doing {pull_link}.').format(pull_link=pull_link)|n} <br/> |
|
88 | 88 | ${_('This field is stored encrypted inside Database, a format of http://user:password@server.com/repo_name can be used and will be hidden from display.')} |
|
89 | 89 | </p> |
@@ -3,7 +3,7 b'' | |||
|
3 | 3 | <h3 class="panel-title">${_('Repository statistics')}</h3> |
|
4 | 4 | </div> |
|
5 | 5 | <div class="panel-body"> |
|
6 |
${h.secure_form(h. |
|
|
6 | ${h.secure_form(h.route_path('edit_repo_statistics_reset', repo_name=c.repo_info.repo_name), method='POST', request=request)} | |
|
7 | 7 | <div class="form"> |
|
8 | 8 | <div class="fields"> |
|
9 | 9 | <div class="field" > |
@@ -1,7 +1,7 b'' | |||
|
1 | 1 | <%namespace name="vcss" file="/base/vcs_settings.mako"/> |
|
2 | 2 | |
|
3 | 3 | <div id="repo_vcs_settings" class="${'inherited' if c.inherit_global_settings else ''}"> |
|
4 |
${h.secure_form(h. |
|
|
4 | ${h.secure_form(h.route_path('edit_repo_vcs_update', repo_name=c.repo_info.repo_name), method='POST', request=request)} | |
|
5 | 5 | <div class="form panel panel-default"> |
|
6 | 6 | <div class="fields panel-body"> |
|
7 | 7 | <div class="field"> |
@@ -50,13 +50,12 b'' | |||
|
50 | 50 | <script type="text/javascript"> |
|
51 | 51 | |
|
52 | 52 | function ajaxDeletePattern(pattern_id, field_id) { |
|
53 |
var sUrl = "${h. |
|
|
53 | var sUrl = "${h.route_path('edit_repo_vcs_svn_pattern_delete', repo_name=c.repo_info.repo_name)}"; | |
|
54 | 54 | var callback = function (o) { |
|
55 | 55 | var elem = $("#"+field_id); |
|
56 | 56 | elem.remove(); |
|
57 | 57 | }; |
|
58 | 58 | var postData = { |
|
59 | '_method': 'delete', | |
|
60 | 59 | 'delete_svn_pattern': pattern_id, |
|
61 | 60 | 'csrf_token': CSRF_TOKEN |
|
62 | 61 | }; |
@@ -24,7 +24,7 b'' | |||
|
24 | 24 | <ul class="links"> |
|
25 | 25 | %if h.HasPermissionAny('hg.admin','hg.create.repository')(): |
|
26 | 26 | <li> |
|
27 |
<a href="${h. |
|
|
27 | <a href="${h.route_path('repo_new')}" class="btn btn-small btn-success">${_(u'Add Repository')}</a> | |
|
28 | 28 | </li> |
|
29 | 29 | %endif |
|
30 | 30 | </ul> |
@@ -73,7 +73,7 b'' | |||
|
73 | 73 | <%def name="admin_menu()"> |
|
74 | 74 | <ul class="admin_menu submenu"> |
|
75 | 75 | <li><a href="${h.route_path('admin_audit_logs')}">${_('Admin audit logs')}</a></li> |
|
76 |
<li><a href="${h. |
|
|
76 | <li><a href="${h.route_path('repos')}">${_('Repositories')}</a></li> | |
|
77 | 77 | <li><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li> |
|
78 | 78 | <li><a href="${h.route_path('users')}">${_('Users')}</a></li> |
|
79 | 79 | <li><a href="${h.url('users_groups')}">${_('User groups')}</a></li> |
@@ -145,7 +145,7 b'' | |||
|
145 | 145 | <%def name="admin_menu_simple(repositories=None, repository_groups=None, user_groups=None)"> |
|
146 | 146 | <ul class="submenu"> |
|
147 | 147 | %if repositories: |
|
148 |
<li class="local-admin-repos"><a href="${h. |
|
|
148 | <li class="local-admin-repos"><a href="${h.route_path('repos')}">${_('Repositories')}</a></li> | |
|
149 | 149 | %endif |
|
150 | 150 | %if repository_groups: |
|
151 | 151 | <li class="local-admin-repo-groups"><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li> |
@@ -268,9 +268,9 b'' | |||
|
268 | 268 | |
|
269 | 269 | %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking: |
|
270 | 270 | %if c.rhodecode_db_repo.locked[0]: |
|
271 |
<li><a class="locking_del" href="${h. |
|
|
271 | <li><a class="locking_del" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Unlock')}</a></li> | |
|
272 | 272 | %else: |
|
273 |
<li><a class="locking_add" href="${h. |
|
|
273 | <li><a class="locking_add" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Lock')}</a></li> | |
|
274 | 274 | %endif |
|
275 | 275 | %endif |
|
276 | 276 | %if c.rhodecode_user.username != h.DEFAULT_USER: |
@@ -108,8 +108,8 b'' | |||
|
108 | 108 | alert("Error while deleting entry.\nError code {0} ({1}). URL: {2}".format(data.status,data.statusText,$(entry)[0].url)); |
|
109 | 109 | } |
|
110 | 110 | }); |
|
111 |
} |
|
|
112 | } | |
|
111 | } | |
|
112 | }; | |
|
113 | 113 | |
|
114 | 114 | $('.delete_issuetracker_entry').on('click', function(e){ |
|
115 | 115 | e.preventDefault(); |
@@ -74,6 +74,8 b'' | |||
|
74 | 74 | %else: |
|
75 | 75 | % if c.file.size < c.visual.cut_off_limit_file: |
|
76 | 76 | %if c.renderer and not c.annotate: |
|
77 | ## pick relative url based on renderer | |
|
78 | ${c.renderer} XXXX | |
|
77 | 79 | <% |
|
78 | 80 | relative_urls = { |
|
79 | 81 | 'raw': h.route_path('repo_file_raw',repo_name=c.repo_name,commit_id=c.commit.raw_id,f_path=c.f_path), |
@@ -27,7 +27,7 b'' | |||
|
27 | 27 | %if not c.repo_group: |
|
28 | 28 | ## no repository group context here |
|
29 | 29 | %if is_admin or create_repo: |
|
30 |
<a href="${h. |
|
|
30 | <a href="${h.route_path('repo_new')}" class="btn btn-small btn-success btn-primary">${_('Add Repository')}</a> | |
|
31 | 31 | %endif |
|
32 | 32 | |
|
33 | 33 | %if is_admin or create_repo_group: |
@@ -36,7 +36,7 b'' | |||
|
36 | 36 | %else: |
|
37 | 37 | ##we're inside other repository group other terms apply |
|
38 | 38 | %if is_admin or group_admin or (group_write and create_on_write): |
|
39 |
<a href="${h. |
|
|
39 | <a href="${h.route_path('repo_new',parent_group=c.repo_group.group_id)}" class="btn btn-small btn-success btn-primary">${_('Add Repository')}</a> | |
|
40 | 40 | %endif |
|
41 | 41 | %if is_admin or group_admin: |
|
42 | 42 | <a href="${h.url('new_repo_group', parent_group=c.repo_group.group_id)}" class="btn btn-small btn-default">${_(u'Add Repository Group')}</a> |
@@ -15,7 +15,7 b'' | |||
|
15 | 15 | <div class="alert alert-dismissable alert-warning"> |
|
16 | 16 | <strong>Missing requirements</strong> |
|
17 | 17 | Commits cannot be displayed, because this repository uses one or more extensions, which was not enabled. <br/> |
|
18 |
Please <a href="${h. |
|
|
18 | Please <a href="${h.route_path('edit_repo_vcs', repo_name=c.repo_name)}">enable extension in settings</a>, or contact the repository owner for help. | |
|
19 | 19 | Missing extensions could be: |
|
20 | 20 | <pre> |
|
21 | 21 |
@@ -20,10 +20,7 b'' | |||
|
20 | 20 | |
|
21 | 21 | import pytest |
|
22 | 22 | |
|
23 |
from rhodecode.tests import |
|
|
24 | TestController, url, assert_session_flash, link_to) | |
|
25 | from rhodecode.model.db import User, UserGroup | |
|
26 | from rhodecode.model.meta import Session | |
|
23 | from rhodecode.tests import TestController, url | |
|
27 | 24 | from rhodecode.tests.fixture import Fixture |
|
28 | 25 | |
|
29 | 26 | |
@@ -33,6 +30,8 b' def route_path(name, params=None, **kwar' | |||
|
33 | 30 | |
|
34 | 31 | base_url = { |
|
35 | 32 | 'home': '/', |
|
33 | 'repos': | |
|
34 | ADMIN_PREFIX + '/repos', | |
|
36 | 35 | 'user_groups': |
|
37 | 36 | ADMIN_PREFIX + '/user_groups', |
|
38 | 37 | 'user_groups_data': |
@@ -63,7 +62,7 b' class TestAdminDelegatedUser(TestControl' | |||
|
63 | 62 | assert_response.no_element_exists('li.local-admin-repo-groups') |
|
64 | 63 | assert_response.no_element_exists('li.local-admin-user-groups') |
|
65 | 64 | |
|
66 |
response = self.app.get( |
|
|
65 | response = self.app.get(route_path('repos'), status=200) | |
|
67 | 66 | response.mustcontain('data: []') |
|
68 | 67 | |
|
69 | 68 | response = self.app.get(url('repo_groups'), status=200) |
@@ -98,7 +97,7 b' class TestAdminDelegatedUser(TestControl' | |||
|
98 | 97 | assert_response.one_element_exists('li.local-admin-user-groups') |
|
99 | 98 | |
|
100 | 99 | # admin interfaces have visible elements |
|
101 |
response = self.app.get( |
|
|
100 | response = self.app.get(route_path('repos'), status=200) | |
|
102 | 101 | response.mustcontain('"name_raw": "{}"'.format(repo_name)) |
|
103 | 102 | |
|
104 | 103 | response = self.app.get(url('repo_groups'), status=200) |
@@ -140,7 +139,7 b' class TestAdminDelegatedUser(TestControl' | |||
|
140 | 139 | assert_response.one_element_exists('li.local-admin-user-groups') |
|
141 | 140 | |
|
142 | 141 | # admin interfaces have visible elements |
|
143 |
response = self.app.get( |
|
|
142 | response = self.app.get(route_path('repos'), status=200) | |
|
144 | 143 | response.mustcontain('"name_raw": "{}"'.format(repo_name)) |
|
145 | 144 | |
|
146 | 145 | response = self.app.get(url('repo_groups'), status=200) |
@@ -22,8 +22,6 b' import pytest' | |||
|
22 | 22 | from mock import Mock, patch |
|
23 | 23 | |
|
24 | 24 | from rhodecode.lib import base |
|
25 | from rhodecode.lib.vcs.exceptions import RepositoryRequirementError | |
|
26 | from rhodecode.lib import helpers as h | |
|
27 | 25 | from rhodecode.model import db |
|
28 | 26 | |
|
29 | 27 | |
@@ -94,162 +92,3 b' def call_vcs_operation_context(**kwargs_' | |||
|
94 | 92 | return result |
|
95 | 93 | |
|
96 | 94 | |
|
97 | class TestBaseRepoController(object): | |
|
98 | def test_context_is_updated_when_update_global_counters_is_called(self): | |
|
99 | followers = 1 | |
|
100 | forks = 2 | |
|
101 | pull_requests = 3 | |
|
102 | is_following = True | |
|
103 | scm_model = Mock(name="scm_model") | |
|
104 | db_repo = Mock(name="db_repo") | |
|
105 | scm_model.get_followers.return_value = followers | |
|
106 | scm_model.get_forks.return_value = forks | |
|
107 | scm_model.get_pull_requests.return_value = pull_requests | |
|
108 | scm_model.is_following_repo.return_value = is_following | |
|
109 | ||
|
110 | controller = base.BaseRepoController() | |
|
111 | with patch.object(base, 'c') as context_mock: | |
|
112 | controller._update_global_counters(scm_model, db_repo) | |
|
113 | ||
|
114 | scm_model.get_pull_requests.assert_called_once_with(db_repo) | |
|
115 | ||
|
116 | assert context_mock.repository_pull_requests == pull_requests | |
|
117 | ||
|
118 | ||
|
119 | class TestBaseRepoControllerHandleMissingRequirements(object): | |
|
120 | def test_logs_error_and_sets_repo_to_none(self, app): | |
|
121 | controller = base.BaseRepoController() | |
|
122 | error_message = 'Some message' | |
|
123 | error = RepositoryRequirementError(error_message) | |
|
124 | context_patcher = patch.object(base, 'c') | |
|
125 | log_patcher = patch.object(base, 'log') | |
|
126 | request_patcher = patch.object(base, 'request') | |
|
127 | redirect_patcher = patch.object(base, 'redirect') | |
|
128 | controller.rhodecode_repo = 'something' | |
|
129 | ||
|
130 | with context_patcher as context_mock, log_patcher as log_mock, \ | |
|
131 | request_patcher, redirect_patcher: | |
|
132 | context_mock.repo_name = 'abcde' | |
|
133 | controller._handle_missing_requirements(error) | |
|
134 | ||
|
135 | expected_log_message = ( | |
|
136 | 'Requirements are missing for repository %s: %s', 'abcde', | |
|
137 | error_message) | |
|
138 | log_mock.error.assert_called_once_with(*expected_log_message) | |
|
139 | ||
|
140 | assert controller.rhodecode_repo is None | |
|
141 | ||
|
142 | @pytest.mark.parametrize('path, should_redirect', [ | |
|
143 | ('/abcde', False), | |
|
144 | ('/abcde/settings', False), | |
|
145 | ('/abcde/settings/vcs', False), | |
|
146 | ('/_admin/repos/abcde', False), # Settings update | |
|
147 | ('/abcde/changelog', True), | |
|
148 | ('/abcde/files/tip', True), | |
|
149 | ('/abcde/settings/statistics', True), | |
|
150 | ]) | |
|
151 | def test_redirects_if_not_summary_or_settings_page( | |
|
152 | self, app, path, should_redirect): | |
|
153 | repo_name = 'abcde' | |
|
154 | controller = base.BaseRepoController() | |
|
155 | error = RepositoryRequirementError('Some message') | |
|
156 | context_patcher = patch.object(base, 'c') | |
|
157 | controller.rhodecode_repo = repo_name | |
|
158 | request_patcher = patch.object(base, 'request') | |
|
159 | redirect_patcher = patch.object(base, 'redirect') | |
|
160 | ||
|
161 | with context_patcher as context_mock, \ | |
|
162 | request_patcher as request_mock, \ | |
|
163 | redirect_patcher as redirect_mock: | |
|
164 | request_mock.path = path | |
|
165 | context_mock.repo_name = repo_name | |
|
166 | controller._handle_missing_requirements(error) | |
|
167 | ||
|
168 | expected_url = h.route_path('repo_summary', repo_name=repo_name) | |
|
169 | if should_redirect: | |
|
170 | redirect_mock.assert_called_once_with(expected_url) | |
|
171 | else: | |
|
172 | redirect_mock.call_count == 0 | |
|
173 | ||
|
174 | ||
|
175 | class TestBaseRepoControllerBefore(object): | |
|
176 | def test_flag_is_true_when_requirements_are_missing(self, before_mocks): | |
|
177 | controller = self._get_controller() | |
|
178 | ||
|
179 | handle_patcher = patch.object( | |
|
180 | controller, '_handle_missing_requirements') | |
|
181 | ||
|
182 | error = RepositoryRequirementError() | |
|
183 | before_mocks.repository.scm_instance.side_effect = error | |
|
184 | ||
|
185 | with handle_patcher as handle_mock: | |
|
186 | controller.__before__() | |
|
187 | ||
|
188 | handle_mock.assert_called_once_with(error) | |
|
189 | assert before_mocks['context'].repository_requirements_missing is True | |
|
190 | ||
|
191 | def test_flag_is_false_when_no_requirements_are_missing( | |
|
192 | self, before_mocks): | |
|
193 | controller = self._get_controller() | |
|
194 | ||
|
195 | handle_patcher = patch.object( | |
|
196 | controller, '_handle_missing_requirements') | |
|
197 | with handle_patcher as handle_mock: | |
|
198 | controller.__before__() | |
|
199 | handle_mock.call_count == 0 | |
|
200 | assert before_mocks['context'].repository_requirements_missing is False | |
|
201 | ||
|
202 | def test_update_global_counters_is_called(self, before_mocks): | |
|
203 | controller = self._get_controller() | |
|
204 | ||
|
205 | update_counters_patcher = patch.object( | |
|
206 | controller, '_update_global_counters') | |
|
207 | ||
|
208 | with update_counters_patcher as update_counters_mock: | |
|
209 | controller.__before__() | |
|
210 | update_counters_mock.assert_called_once_with( | |
|
211 | controller.scm_model, before_mocks.repository) | |
|
212 | ||
|
213 | def _get_controller(self): | |
|
214 | controller = base.BaseRepoController() | |
|
215 | controller.scm_model = Mock() | |
|
216 | controller.rhodecode_repo = Mock() | |
|
217 | return controller | |
|
218 | ||
|
219 | ||
|
220 | @pytest.fixture | |
|
221 | def before_mocks(request): | |
|
222 | patcher = BeforePatcher() | |
|
223 | patcher.start() | |
|
224 | request.addfinalizer(patcher.stop) | |
|
225 | return patcher | |
|
226 | ||
|
227 | ||
|
228 | class BeforePatcher(object): | |
|
229 | patchers = {} | |
|
230 | mocks = {} | |
|
231 | repository = None | |
|
232 | ||
|
233 | def __init__(self): | |
|
234 | self.repository = Mock() | |
|
235 | ||
|
236 | def start(self): | |
|
237 | self.patchers = { | |
|
238 | 'request': patch.object(base, 'request'), | |
|
239 | 'before': patch.object(base.BaseController, '__before__'), | |
|
240 | 'context': patch.object(base, 'c'), | |
|
241 | 'repo': patch.object( | |
|
242 | base.Repository, 'get_by_repo_name', | |
|
243 | return_value=self.repository) | |
|
244 | ||
|
245 | } | |
|
246 | self.mocks = { | |
|
247 | p: self.patchers[p].start() for p in self.patchers | |
|
248 | } | |
|
249 | ||
|
250 | def stop(self): | |
|
251 | for patcher in self.patchers.values(): | |
|
252 | patcher.stop() | |
|
253 | ||
|
254 | def __getitem__(self, key): | |
|
255 | return self.mocks[key] |
@@ -1045,7 +1045,7 b' class TestCreateOrUpdateRepoSettings(obj' | |||
|
1045 | 1045 | def test_cache_is_marked_for_invalidation(self, repo_stub): |
|
1046 | 1046 | model = VcsSettingsModel(repo=repo_stub) |
|
1047 | 1047 | invalidation_patcher = mock.patch( |
|
1048 |
'rhodecode. |
|
|
1048 | 'rhodecode.model.scm.ScmModel.mark_for_invalidation') | |
|
1049 | 1049 | with invalidation_patcher as invalidation_mock: |
|
1050 | 1050 | model.create_or_update_repo_settings( |
|
1051 | 1051 | data=self.FORM_DATA, inherit_global_settings=True) |
General Comments 0
You need to be logged in to leave comments.
Login now