##// END OF EJS Templates
repositories: rewrote whole admin section to pyramid....
marcink -
r2014:b776c5e0 default
parent child Browse files
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
NO CONTENT: new file 100644
1 NO CONTENT: new file 100644
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
NO CONTENT: new file 100644
@@ -205,6 +205,19 b' def admin_routes(config):'
205 name='edit_user_group_perms_summary_json',
205 name='edit_user_group_perms_summary_json',
206 pattern='/user_groups/{user_group_id:\d+}/edit/permissions_summary/json')
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 def includeme(config):
222 def includeme(config):
210 settings = config.get_settings()
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 import mock
23 import mock
24 import pytest
24 import pytest
25
25
26 from rhodecode.apps._base import ADMIN_PREFIX
26 from rhodecode.lib import auth
27 from rhodecode.lib import auth
27 from rhodecode.lib.utils2 import safe_str, str2bool
28 from rhodecode.lib.utils2 import safe_str
28 from rhodecode.lib import helpers as h
29 from rhodecode.lib import helpers as h
29 from rhodecode.model.db import (
30 from rhodecode.model.db import (
30 Repository, RepoGroup, UserRepoToPerm, User, Permission)
31 Repository, RepoGroup, UserRepoToPerm, User, Permission)
31 from rhodecode.model.meta import Session
32 from rhodecode.model.meta import Session
32 from rhodecode.model.repo import RepoModel
33 from rhodecode.model.repo import RepoModel
33 from rhodecode.model.repo_group import RepoGroupModel
34 from rhodecode.model.repo_group import RepoGroupModel
34 from rhodecode.model.settings import SettingsModel, VcsSettingsModel
35 from rhodecode.model.user import UserModel
35 from rhodecode.model.user import UserModel
36 from rhodecode.tests import (
36 from rhodecode.tests import (
37 login_user_session, url, assert_session_flash, TEST_USER_ADMIN_LOGIN,
37 login_user_session, assert_session_flash, TEST_USER_ADMIN_LOGIN,
38 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS, logout_user_session)
38 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
39 from rhodecode.tests.fixture import Fixture, error_function
39 from rhodecode.tests.fixture import Fixture, error_function
40 from rhodecode.tests.utils import AssertResponse, repo_on_filesystem
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 import urllib
46 import urllib
47
47
48 base_url = {
48 base_url = {
49 'repo_summary': '/{repo_name}',
49 'repos': ADMIN_PREFIX + '/repos',
50 'repo_new': ADMIN_PREFIX + '/repos/new',
51 'repo_create': ADMIN_PREFIX + '/repos/create',
52
50 'repo_creating_check': '/{repo_name}/repo_creating_check',
53 'repo_creating_check': '/{repo_name}/repo_creating_check',
51 }[name].format(**kwargs)
54 }[name].format(**kwargs)
52
55
@@ -55,35 +58,48 b' def route_path(name, params=None, **kwar'
55 return base_url
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 @pytest.mark.usefixtures("app")
70 @pytest.mark.usefixtures("app")
59 class TestAdminRepos(object):
71 class TestAdminRepos(object):
60
72
61 def test_index(self):
73 def test_repo_list(self, autologin_user, user_util):
62 self.app.get(url('repos'))
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 with mock.patch('rhodecode.BACKENDS', {'git': 'git'}):
81 with mock.patch('rhodecode.BACKENDS', {'git': 'git'}):
66 response = self.app.get(url('new_repo'), status=200)
82 response = self.app.get(route_path('repo_new'), status=200)
67 assert_response = AssertResponse(response)
83 assert_response = AssertResponse(response)
68 element = assert_response.get_element('#repo_type')
84 element = assert_response.get_element('#repo_type')
69 assert element.text_content() == '\ngit\n'
85 assert element.text_content() == '\ngit\n'
70
86
71 def test_create_page_non_restricted(self, autologin_user, backend):
87 def test_create_page_non_restricted_backends(self, autologin_user, backend):
72 response = self.app.get(url('new_repo'), status=200)
88 response = self.app.get(route_path('repo_new'), status=200)
73 assert_response = AssertResponse(response)
89 assert_response = AssertResponse(response)
74 assert_response.element_contains('#repo_type', 'git')
90 assert_response.element_contains('#repo_type', 'git')
75 assert_response.element_contains('#repo_type', 'svn')
91 assert_response.element_contains('#repo_type', 'svn')
76 assert_response.element_contains('#repo_type', 'hg')
92 assert_response.element_contains('#repo_type', 'hg')
77
93
78 @pytest.mark.parametrize("suffix",
94 @pytest.mark.parametrize(
79 [u'', u'xxa'], ids=['', 'non-ascii'])
95 "suffix", [u'', u'xxa'], ids=['', 'non-ascii'])
80 def test_create(self, autologin_user, backend, suffix, csrf_token):
96 def test_create(self, autologin_user, backend, suffix, csrf_token):
81 repo_name_unicode = backend.new_repo_name(suffix=suffix)
97 repo_name_unicode = backend.new_repo_name(suffix=suffix)
82 repo_name = repo_name_unicode.encode('utf8')
98 repo_name = repo_name_unicode.encode('utf8')
83 description_unicode = u'description for newly created repo' + suffix
99 description_unicode = u'description for newly created repo' + suffix
84 description = description_unicode.encode('utf8')
100 description = description_unicode.encode('utf8')
85 response = self.app.post(
101 response = self.app.post(
86 url('repos'),
102 route_path('repo_create'),
87 fixture._get_repo_create_params(
103 fixture._get_repo_create_params(
88 repo_private=False,
104 repo_private=False,
89 repo_name=repo_name,
105 repo_name=repo_name,
@@ -95,12 +111,12 b' class TestAdminRepos(object):'
95 self.assert_repository_is_created_correctly(
111 self.assert_repository_is_created_correctly(
96 repo_name, description, backend)
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 numeric_repo = '1234'
115 numeric_repo = '1234'
100 repo_name = numeric_repo
116 repo_name = numeric_repo
101 description = 'description for newly created repo' + numeric_repo
117 description = 'description for newly created repo' + numeric_repo
102 self.app.post(
118 self.app.post(
103 url('repos'),
119 route_path('repo_create'),
104 fixture._get_repo_create_params(
120 fixture._get_repo_create_params(
105 repo_private=False,
121 repo_private=False,
106 repo_name=repo_name,
122 repo_name=repo_name,
@@ -126,7 +142,7 b' class TestAdminRepos(object):'
126 [group_name, repo_name])
142 [group_name, repo_name])
127 description = u'description for newly created repo'
143 description = u'description for newly created repo'
128 self.app.post(
144 self.app.post(
129 url('repos'),
145 route_path('repo_create'),
130 fixture._get_repo_create_params(
146 fixture._get_repo_create_params(
131 repo_private=False,
147 repo_private=False,
132 repo_name=safe_str(repo_name),
148 repo_name=safe_str(repo_name),
@@ -149,7 +165,7 b' class TestAdminRepos(object):'
149 RepoGroupModel().delete(group_name)
165 RepoGroupModel().delete(group_name)
150 Session().commit()
166 Session().commit()
151
167
152 def test_create_in_group_numeric(
168 def test_create_in_group_numeric_name(
153 self, autologin_user, backend, csrf_token):
169 self, autologin_user, backend, csrf_token):
154 # create GROUP
170 # create GROUP
155 group_name = 'sometest_%s' % backend.alias
171 group_name = 'sometest_%s' % backend.alias
@@ -162,7 +178,7 b' class TestAdminRepos(object):'
162 repo_name_full = RepoGroup.url_sep().join([group_name, repo_name])
178 repo_name_full = RepoGroup.url_sep().join([group_name, repo_name])
163 description = 'description for newly created repo'
179 description = 'description for newly created repo'
164 self.app.post(
180 self.app.post(
165 url('repos'),
181 route_path('repo_create'),
166 fixture._get_repo_create_params(
182 fixture._get_repo_create_params(
167 repo_private=False,
183 repo_private=False,
168 repo_name=repo_name,
184 repo_name=repo_name,
@@ -221,7 +237,7 b' class TestAdminRepos(object):'
221 repo_name = 'ingroup'
237 repo_name = 'ingroup'
222 description = 'description for newly created repo'
238 description = 'description for newly created repo'
223 response = self.app.post(
239 response = self.app.post(
224 url('repos'),
240 route_path('repo_create'),
225 fixture._get_repo_create_params(
241 fixture._get_repo_create_params(
226 repo_private=False,
242 repo_private=False,
227 repo_name=repo_name,
243 repo_name=repo_name,
@@ -238,7 +254,7 b' class TestAdminRepos(object):'
238 [group_name_allowed, repo_name])
254 [group_name_allowed, repo_name])
239 description = 'description for newly created repo'
255 description = 'description for newly created repo'
240 response = self.app.post(
256 response = self.app.post(
241 url('repos'),
257 route_path('repo_create'),
242 fixture._get_repo_create_params(
258 fixture._get_repo_create_params(
243 repo_private=False,
259 repo_private=False,
244 repo_name=repo_name,
260 repo_name=repo_name,
@@ -282,7 +298,7 b' class TestAdminRepos(object):'
282 repo_name_full = RepoGroup.url_sep().join([group_name, repo_name])
298 repo_name_full = RepoGroup.url_sep().join([group_name, repo_name])
283 description = 'description for newly created repo'
299 description = 'description for newly created repo'
284 self.app.post(
300 self.app.post(
285 url('repos'),
301 route_path('repo_create'),
286 fixture._get_repo_create_params(
302 fixture._get_repo_create_params(
287 repo_private=False,
303 repo_private=False,
288 repo_name=repo_name,
304 repo_name=repo_name,
@@ -326,7 +342,7 b' class TestAdminRepos(object):'
326
342
327 repo_name = backend.new_repo_name()
343 repo_name = backend.new_repo_name()
328 response = self.app.post(
344 response = self.app.post(
329 url('repos'),
345 route_path('repo_create'),
330 fixture._get_repo_create_params(
346 fixture._get_repo_create_params(
331 repo_private=False,
347 repo_private=False,
332 repo_name=repo_name,
348 repo_name=repo_name,
@@ -354,7 +370,7 b' class TestAdminRepos(object):'
354 repo_name = backend.new_repo_name()
370 repo_name = backend.new_repo_name()
355 description = 'description for newly created repo'
371 description = 'description for newly created repo'
356 response = self.app.post(
372 response = self.app.post(
357 url('repos'),
373 route_path('repo_create'),
358 fixture._get_repo_create_params(
374 fixture._get_repo_create_params(
359 repo_private=False,
375 repo_private=False,
360 repo_name=repo_name,
376 repo_name=repo_name,
@@ -370,7 +386,7 b' class TestAdminRepos(object):'
370 repo_name = backend.new_repo_name()
386 repo_name = backend.new_repo_name()
371 description = 'description for newly created repo'
387 description = 'description for newly created repo'
372 response = self.app.post(
388 response = self.app.post(
373 url('repos'),
389 route_path('repo_create'),
374 fixture._get_repo_create_params(
390 fixture._get_repo_create_params(
375 repo_private=False,
391 repo_private=False,
376 repo_name=repo_name,
392 repo_name=repo_name,
@@ -385,7 +401,7 b' class TestAdminRepos(object):'
385 repo_name = backend.new_repo_name() + ".git"
401 repo_name = backend.new_repo_name() + ".git"
386 description = 'description for newly created repo'
402 description = 'description for newly created repo'
387 response = self.app.post(
403 response = self.app.post(
388 url('repos'),
404 route_path('repo_create'),
389 fixture._get_repo_create_params(
405 fixture._get_repo_create_params(
390 repo_private=False,
406 repo_private=False,
391 repo_name=repo_name,
407 repo_name=repo_name,
@@ -394,11 +410,8 b' class TestAdminRepos(object):'
394 csrf_token=csrf_token))
410 csrf_token=csrf_token))
395 response.mustcontain('Repository name cannot end with .git')
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 def test_default_user_cannot_access_private_repo_in_a_group(
413 def test_default_user_cannot_access_private_repo_in_a_group(
401 self, autologin_user, user_util, backend, csrf_token):
414 self, autologin_user, user_util, backend):
402
415
403 group = user_util.create_repo_group()
416 group = user_util.create_repo_group()
404
417
@@ -434,7 +447,7 b' class TestAdminRepos(object):'
434 repo_name = backend.new_repo_name()
447 repo_name = backend.new_repo_name()
435 description = 'description for newly created repo'
448 description = 'description for newly created repo'
436 response = self.app.post(
449 response = self.app.post(
437 url('repos'),
450 route_path('repo_create'),
438 fixture._get_repo_create_params(
451 fixture._get_repo_create_params(
439 repo_private=False,
452 repo_private=False,
440 repo_name=repo_name,
453 repo_name=repo_name,
@@ -453,7 +466,7 b' class TestAdminRepos(object):'
453 description = 'description for newly created repo'
466 description = 'description for newly created repo'
454
467
455 response = self.app.post(
468 response = self.app.post(
456 url('repos'),
469 route_path('repo_create'),
457 fixture._get_repo_create_params(
470 fixture._get_repo_create_params(
458 repo_private=False,
471 repo_private=False,
459 repo_name=repo_name,
472 repo_name=repo_name,
@@ -494,638 +507,3 b' class TestAdminRepos(object):'
494 response.mustcontain(backend.alias)
507 response.mustcontain(backend.alias)
495
508
496 assert repo_on_filesystem(repo_name)
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 c = self.load_default_context()
48 c = self.load_default_context()
49 c.active = 'open_source'
49 c.active = 'open_source'
50 c.navlist = navigation_list(self.request)
50 c.navlist = navigation_list(self.request)
51 c.opensource_licenses = collections.OrderedDict(
51 items = sorted(read_opensource_licenses().items(), key=lambda t: t[0])
52 sorted(read_opensource_licenses().items(), key=lambda t: t[0]))
52 c.opensource_licenses = collections.OrderedDict(items)
53
53
54 return self._get_template_context(c)
54 return self._get_template_context(c)
@@ -19,7 +19,6 b''
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22 import datetime
23
22
24 from pyramid.httpexceptions import HTTPFound
23 from pyramid.httpexceptions import HTTPFound
25 from pyramid.view import view_config
24 from pyramid.view import view_config
@@ -28,12 +27,11 b' from rhodecode.model.scm import UserGrou'
28
27
29 from rhodecode.apps._base import BaseAppView, DataGridAppView
28 from rhodecode.apps._base import BaseAppView, DataGridAppView
30 from rhodecode.lib.auth import (
29 from rhodecode.lib.auth import (
31 LoginRequired, HasPermissionAllDecorator, CSRFRequired, NotAnonymous,
30 LoginRequired, NotAnonymous,
32 HasUserGroupPermissionAnyDecorator)
31 HasUserGroupPermissionAnyDecorator)
33 from rhodecode.lib import helpers as h
32 from rhodecode.lib import helpers as h
34 from rhodecode.lib.utils import PartialRenderer
33 from rhodecode.lib.utils import PartialRenderer
35 from rhodecode.lib.utils2 import safe_int, safe_unicode
34 from rhodecode.lib.utils2 import safe_unicode
36 from rhodecode.model.user_group import UserGroupModel
37 from rhodecode.model.db import (
35 from rhodecode.model.db import (
38 joinedload, or_, count, User, UserGroup, UserGroupMember,
36 joinedload, or_, count, User, UserGroup, UserGroupMember,
39 UserGroupRepoToPerm, UserGroupRepoGroupToPerm)
37 UserGroupRepoToPerm, UserGroupRepoGroupToPerm)
@@ -337,7 +337,76 b' def includeme(config):'
337 name='edit_repo_perms',
337 name='edit_repo_perms',
338 pattern='/{repo_name:.*?[^/]}/settings/permissions', repo_route=True)
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 config.add_route(
410 config.add_route(
342 name='repo_reviewers',
411 name='repo_reviewers',
343 pattern='/{repo_name:.*?[^/]}/settings/review/rules', repo_route=True)
412 pattern='/{repo_name:.*?[^/]}/settings/review/rules', repo_route=True)
@@ -346,18 +415,9 b' def includeme(config):'
346 name='repo_default_reviewers_data',
415 name='repo_default_reviewers_data',
347 pattern='/{repo_name:.*?[^/]}/settings/review/default-reviewers', repo_route=True)
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 # Strip
418 # Strip
359 config.add_route(
419 config.add_route(
360 name='strip',
420 name='edit_repo_strip',
361 pattern='/{repo_name:.*?[^/]}/settings/strip', repo_route=True)
421 pattern='/{repo_name:.*?[^/]}/settings/strip', repo_route=True)
362
422
363 config.add_route(
423 config.add_route(
@@ -24,23 +24,38 b' from rhodecode.lib.utils2 import md5'
24 from rhodecode.model.db import Repository
24 from rhodecode.model.db import Repository
25 from rhodecode.model.meta import Session
25 from rhodecode.model.meta import Session
26 from rhodecode.model.settings import SettingsModel, IssueTrackerSettingsModel
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 @pytest.mark.usefixtures("app")
45 @pytest.mark.usefixtures("app")
31 class TestAdminRepos:
46 class TestRepoIssueTracker(object):
32 def test_issuetracker_index(self, autologin_user, backend):
47 def test_issuetracker_index(self, autologin_user, backend):
33 repo = backend.create_repo()
48 repo = backend.create_repo()
34 response = self.app.get(url('repo_settings_issuetracker',
49 response = self.app.get(route_path('edit_repo_issuetracker',
35 repo_name=repo.repo_name))
50 repo_name=repo.repo_name))
36 assert response.status_code == 200
51 assert response.status_code == 200
37
52
38 def test_add_issuetracker_patterns(
53 def test_add_and_test_issuetracker_patterns(
39 self, autologin_user, backend, csrf_token, request):
54 self, autologin_user, backend, csrf_token, request, xhr_header):
40 pattern = 'issuetracker_pat'
55 pattern = 'issuetracker_pat'
41 another_pattern = pattern+'1'
56 another_pattern = pattern+'1'
42 post_url = url('repo_issuetracker_save',
57 post_url = route_path(
43 repo_name=backend.repo.repo_name)
58 'edit_repo_issuetracker_update', repo_name=backend.repo.repo_name)
44 post_data = {
59 post_data = {
45 'new_pattern_pattern_0': pattern,
60 'new_pattern_pattern_0': pattern,
46 'new_pattern_url_0': 'url',
61 'new_pattern_url_0': 'url',
@@ -60,6 +75,17 b' class TestAdminRepos:'
60 self.another_uid = md5(another_pattern)
75 self.another_uid = md5(another_pattern)
61 assert settings[self.another_uid]['pat'] == another_pattern
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 @request.addfinalizer
89 @request.addfinalizer
64 def cleanup():
90 def cleanup():
65 self.settings_model.delete_entries(self.uid)
91 self.settings_model.delete_entries(self.uid)
@@ -76,8 +102,8 b' class TestAdminRepos:'
76 entry_key+old_uid, old_pattern, 'unicode')
102 entry_key+old_uid, old_pattern, 'unicode')
77 Session().add(sett)
103 Session().add(sett)
78 Session().commit()
104 Session().commit()
79 post_url = url('repo_issuetracker_save',
105 post_url = route_path(
80 repo_name=backend.repo.repo_name)
106 'edit_repo_issuetracker_update', repo_name=backend.repo.repo_name)
81 post_data = {
107 post_data = {
82 'new_pattern_pattern_0': pattern,
108 'new_pattern_pattern_0': pattern,
83 'new_pattern_url_0': 'url',
109 'new_pattern_url_0': 'url',
@@ -92,7 +118,7 b' class TestAdminRepos:'
92 self.uid = md5(pattern)
118 self.uid = md5(pattern)
93 assert settings[self.uid]['pat'] == pattern
119 assert settings[self.uid]['pat'] == pattern
94 with pytest.raises(KeyError):
120 with pytest.raises(KeyError):
95 settings[old_uid]
121 key = settings[old_uid]
96
122
97 @request.addfinalizer
123 @request.addfinalizer
98 def cleanup():
124 def cleanup():
@@ -110,10 +136,10 b' class TestAdminRepos:'
110 value=entry_key, type_='unicode', cleanup=False)
136 value=entry_key, type_='unicode', cleanup=False)
111
137
112 self.app.post(
138 self.app.post(
113 url('repo_issuetracker_delete',
139 route_path(
140 'edit_repo_issuetracker_delete',
114 repo_name=backend.repo.repo_name),
141 repo_name=backend.repo.repo_name),
115 {
142 {
116 '_method': 'delete',
117 'uid': uid,
143 'uid': uid,
118 'csrf_token': csrf_token
144 'csrf_token': csrf_token
119 }, status=302)
145 }, status=302)
@@ -26,8 +26,7 b' from rhodecode.lib.vcs.exceptions import'
26 from rhodecode.model.db import Repository, UserRepoToPerm, Permission, User
26 from rhodecode.model.db import Repository, UserRepoToPerm, Permission, User
27 from rhodecode.model.meta import Session
27 from rhodecode.model.meta import Session
28 from rhodecode.tests import (
28 from rhodecode.tests import (
29 url, HG_REPO, TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN,
29 TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN, assert_session_flash)
30 assert_session_flash)
31 from rhodecode.tests.fixture import Fixture
30 from rhodecode.tests.fixture import Fixture
32
31
33 fixture = Fixture()
32 fixture = Fixture()
@@ -41,6 +40,11 b' def route_path(name, params=None, **kwar'
41 'edit_repo_advanced': '/{repo_name}/settings/advanced',
40 'edit_repo_advanced': '/{repo_name}/settings/advanced',
42 'edit_repo_caches': '/{repo_name}/settings/caches',
41 'edit_repo_caches': '/{repo_name}/settings/caches',
43 'edit_repo_perms': '/{repo_name}/settings/permissions',
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 }[name].format(**kwargs)
48 }[name].format(**kwargs)
45
49
46 if params:
50 if params:
@@ -64,6 +68,11 b' class TestAdminRepoSettings(object):'
64 'edit_repo_caches',
68 'edit_repo_caches',
65 'edit_repo_perms',
69 'edit_repo_perms',
66 'edit_repo_advanced',
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 def test_show_page(self, urlname, app, backend):
77 def test_show_page(self, urlname, app, backend):
69 app.get(route_path(urlname, repo_name=backend.repo_name), status=200)
78 app.get(route_path(urlname, repo_name=backend.repo_name), status=200)
@@ -75,16 +84,6 b' class TestAdminRepoSettings(object):'
75 with scm_patcher:
84 with scm_patcher:
76 self.app.get(route_path('edit_repo', repo_name=backend_hg.repo_name))
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 @pytest.mark.parametrize('update_settings', [
87 @pytest.mark.parametrize('update_settings', [
89 {'repo_description': 'alter-desc'},
88 {'repo_description': 'alter-desc'},
90 {'repo_owner': TEST_USER_REGULAR_LOGIN},
89 {'repo_owner': TEST_USER_REGULAR_LOGIN},
@@ -24,7 +24,6 b' import pytest'
24 import rhodecode
24 import rhodecode
25 from rhodecode.model.db import Repository
25 from rhodecode.model.db import Repository
26 from rhodecode.model.settings import SettingsModel
26 from rhodecode.model.settings import SettingsModel
27 from rhodecode.tests import url
28 from rhodecode.tests.utils import AssertResponse
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 base_url = {
33 base_url = {
35 'edit_repo': '/{repo_name}/settings',
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 }[name].format(**kwargs)
37 }[name].format(**kwargs)
37
38
38 if params:
39 if params:
@@ -51,8 +52,8 b' class TestAdminRepoVcsSettings(object):'
51 if backend.alias not in setting_backends:
52 if backend.alias not in setting_backends:
52 pytest.skip('Setting not available for backend {}'.format(backend))
53 pytest.skip('Setting not available for backend {}'.format(backend))
53
54
54 vcs_settings_url = url(
55 vcs_settings_url = route_path(
55 'repo_vcs_settings', repo_name=backend.repo.repo_name)
56 'edit_repo_vcs', repo_name=backend.repo.repo_name)
56
57
57 with mock.patch.dict(
58 with mock.patch.dict(
58 rhodecode.CONFIG, {'labs_settings_active': 'true'}):
59 rhodecode.CONFIG, {'labs_settings_active': 'true'}):
@@ -64,24 +65,6 b' class TestAdminRepoVcsSettings(object):'
64 @pytest.mark.parametrize('setting_name, setting_backends', [
65 @pytest.mark.parametrize('setting_name, setting_backends', [
65 ('hg_use_rebase_for_merging', ['hg']),
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 def test_update_boolean_settings(
68 def test_update_boolean_settings(
86 self, csrf_token, setting_name, setting_backends, backend):
69 self, csrf_token, setting_name, setting_backends, backend):
87 if backend.alias not in setting_backends:
70 if backend.alias not in setting_backends:
@@ -91,8 +74,8 b' class TestAdminRepoVcsSettings(object):'
91 repo_name = repo.repo_name
74 repo_name = repo.repo_name
92
75
93 settings_model = SettingsModel(repo=repo)
76 settings_model = SettingsModel(repo=repo)
94 vcs_settings_url = url(
77 vcs_settings_url = route_path(
95 'repo_vcs_settings', repo_name=repo_name)
78 'edit_repo_vcs_update', repo_name=repo_name)
96
79
97 self.app.post(
80 self.app.post(
98 vcs_settings_url,
81 vcs_settings_url,
@@ -36,7 +36,7 b' from rhodecode.lib.auth import ('
36 from rhodecode.lib.compat import OrderedDict
36 from rhodecode.lib.compat import OrderedDict
37 from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError
37 from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError
38 import rhodecode.lib.helpers as h
38 import rhodecode.lib.helpers as h
39 from rhodecode.lib.utils2 import safe_unicode, safe_int
39 from rhodecode.lib.utils2 import safe_unicode
40 from rhodecode.lib.vcs.backends.base import EmptyCommit
40 from rhodecode.lib.vcs.backends.base import EmptyCommit
41 from rhodecode.lib.vcs.exceptions import (
41 from rhodecode.lib.vcs.exceptions import (
42 RepositoryError, CommitDoesNotExistError, NodeDoesNotExistError)
42 RepositoryError, CommitDoesNotExistError, NodeDoesNotExistError)
@@ -29,10 +29,9 b' from webhelpers.feedgenerator import Rss'
29 from rhodecode.apps._base import RepoAppView
29 from rhodecode.apps._base import RepoAppView
30 from rhodecode.lib import audit_logger
30 from rhodecode.lib import audit_logger
31 from rhodecode.lib import helpers as h
31 from rhodecode.lib import helpers as h
32 from rhodecode.lib.auth import (LoginRequired, HasRepoPermissionAnyDecorator,
32 from rhodecode.lib.auth import (
33 NotAnonymous, CSRFRequired)
33 LoginRequired, HasRepoPermissionAnyDecorator)
34 from rhodecode.lib.diffs import DiffProcessor, LimitedDiffContainer
34 from rhodecode.lib.diffs import DiffProcessor, LimitedDiffContainer
35 from rhodecode.lib.ext_json import json
36 from rhodecode.lib.utils2 import str2bool, safe_int
35 from rhodecode.lib.utils2 import str2bool, safe_int
37 from rhodecode.model.db import UserApiKeys, CacheKey
36 from rhodecode.model.db import UserApiKeys, CacheKey
38
37
@@ -23,8 +23,7 b' import logging'
23 from pyramid.view import view_config
23 from pyramid.view import view_config
24
24
25 from rhodecode.apps._base import RepoAppView
25 from rhodecode.apps._base import RepoAppView
26 from rhodecode.lib.auth import (LoginRequired, HasRepoPermissionAnyDecorator,
26 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
27 NotAnonymous)
28 from rhodecode.lib import repo_maintenance
27 from rhodecode.lib import repo_maintenance
29
28
30 log = logging.getLogger(__name__)
29 log = logging.getLogger(__name__)
@@ -43,7 +42,7 b' class RepoMaintenanceView(RepoAppView):'
43 @LoginRequired()
42 @LoginRequired()
44 @HasRepoPermissionAnyDecorator('repository.admin')
43 @HasRepoPermissionAnyDecorator('repository.admin')
45 @view_config(
44 @view_config(
46 route_name='repo_maintenance', request_method='GET',
45 route_name='edit_repo_maintenance', request_method='GET',
47 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
46 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
48 def repo_maintenance(self):
47 def repo_maintenance(self):
49 c = self.load_default_context()
48 c = self.load_default_context()
@@ -55,7 +54,7 b' class RepoMaintenanceView(RepoAppView):'
55 @LoginRequired()
54 @LoginRequired()
56 @HasRepoPermissionAnyDecorator('repository.admin')
55 @HasRepoPermissionAnyDecorator('repository.admin')
57 @view_config(
56 @view_config(
58 route_name='repo_maintenance_execute', request_method='GET',
57 route_name='edit_repo_maintenance_execute', request_method='GET',
59 renderer='json', xhr=True)
58 renderer='json', xhr=True)
60 def repo_maintenance_execute(self):
59 def repo_maintenance_execute(self):
61 c = self.load_default_context()
60 c = self.load_default_context()
@@ -20,23 +20,17 b''
20
20
21 import logging
21 import logging
22
22
23 import deform
24 from pyramid.httpexceptions import HTTPFound
23 from pyramid.httpexceptions import HTTPFound
25 from pyramid.view import view_config
24 from pyramid.view import view_config
26
25
27 from rhodecode.apps._base import RepoAppView
26 from rhodecode.apps._base import RepoAppView
28 from rhodecode.forms import RcForm
29 from rhodecode.lib import helpers as h
27 from rhodecode.lib import helpers as h
30 from rhodecode.lib import audit_logger
28 from rhodecode.lib import audit_logger
31 from rhodecode.lib.auth import (
29 from rhodecode.lib.auth import (
32 LoginRequired, HasRepoPermissionAnyDecorator,
30 LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired)
33 HasRepoPermissionAllDecorator, CSRFRequired)
34 from rhodecode.model.db import RepositoryField, RepoGroup
35 from rhodecode.model.forms import RepoPermsForm
31 from rhodecode.model.forms import RepoPermsForm
36 from rhodecode.model.meta import Session
32 from rhodecode.model.meta import Session
37 from rhodecode.model.repo import RepoModel
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 log = logging.getLogger(__name__)
35 log = logging.getLogger(__name__)
42
36
@@ -63,7 +57,7 b' class RepoSettingsPermissionsView(RepoAp'
63 return self._get_template_context(c)
57 return self._get_template_context(c)
64
58
65 @LoginRequired()
59 @LoginRequired()
66 @HasRepoPermissionAllDecorator('repository.admin')
60 @HasRepoPermissionAnyDecorator('repository.admin')
67 @CSRFRequired()
61 @CSRFRequired()
68 @view_config(
62 @view_config(
69 route_name='edit_repo_perms', request_method='POST',
63 route_name='edit_repo_perms', request_method='POST',
@@ -74,7 +68,7 b' class RepoSettingsPermissionsView(RepoAp'
74 c.active = 'permissions'
68 c.active = 'permissions'
75 data = self.request.POST
69 data = self.request.POST
76 # store private flag outside of HTML to verify if we can modify
70 # store private flag outside of HTML to verify if we can modify
77 # default user permissions, prevents submition of FAKE post data
71 # default user permissions, prevents submission of FAKE post data
78 # into the form for private repos
72 # into the form for private repos
79 data['repo_private'] = self.db_repo.private
73 data['repo_private'] = self.db_repo.private
80 form = RepoPermsForm()().to_python(data)
74 form = RepoPermsForm()().to_python(data)
@@ -95,4 +89,4 b' class RepoSettingsPermissionsView(RepoAp'
95 h.flash(_('Repository permissions updated'), category='success')
89 h.flash(_('Repository permissions updated'), category='success')
96
90
97 raise HTTPFound(
91 raise HTTPFound(
98 self.request.route_path('edit_repo_perms', repo_name=self.db_repo_name))
92 h.route_path('edit_repo_perms', repo_name=self.db_repo_name))
@@ -29,9 +29,8 b' from rhodecode.forms import RcForm'
29 from rhodecode.lib import helpers as h
29 from rhodecode.lib import helpers as h
30 from rhodecode.lib import audit_logger
30 from rhodecode.lib import audit_logger
31 from rhodecode.lib.auth import (
31 from rhodecode.lib.auth import (
32 LoginRequired, HasRepoPermissionAnyDecorator,
32 LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired)
33 HasRepoPermissionAllDecorator, CSRFRequired)
33 from rhodecode.model.db import RepositoryField, RepoGroup, Repository
34 from rhodecode.model.db import RepositoryField, RepoGroup
35 from rhodecode.model.meta import Session
34 from rhodecode.model.meta import Session
36 from rhodecode.model.repo import RepoModel
35 from rhodecode.model.repo import RepoModel
37 from rhodecode.model.scm import RepoGroupList, ScmModel
36 from rhodecode.model.scm import RepoGroupList, ScmModel
@@ -109,7 +108,7 b' class RepoSettingsView(RepoAppView):'
109 return self._get_template_context(c)
108 return self._get_template_context(c)
110
109
111 @LoginRequired()
110 @LoginRequired()
112 @HasRepoPermissionAllDecorator('repository.admin')
111 @HasRepoPermissionAnyDecorator('repository.admin')
113 @CSRFRequired()
112 @CSRFRequired()
114 @view_config(
113 @view_config(
115 route_name='edit_repo', request_method='POST',
114 route_name='edit_repo', request_method='POST',
@@ -176,4 +175,80 b' class RepoSettingsView(RepoAppView):'
176 old_repo_name), category='error')
175 old_repo_name), category='error')
177
176
178 raise HTTPFound(
177 raise HTTPFound(
179 self.request.route_path('edit_repo', repo_name=new_repo_name))
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 c.default_user_id = User.get_default_user().user_id
61 c.default_user_id = User.get_default_user().user_id
62 c.in_public_journal = UserFollowing.query() \
62 c.in_public_journal = UserFollowing.query() \
63 .filter(UserFollowing.user_id == c.default_user_id) \
63 .filter(UserFollowing.user_id == c.default_user_id) \
64 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
64 .filter(UserFollowing.follows_repository == self.db_repo).scalar()
65
65
66 c.has_origin_repo_read_perm = False
66 c.has_origin_repo_read_perm = False
67 if self.db_repo.fork:
67 if self.db_repo.fork:
@@ -24,8 +24,8 b' from pyramid.view import view_config'
24 from rhodecode.apps._base import RepoAppView
24 from rhodecode.apps._base import RepoAppView
25 from rhodecode.lib import audit_logger
25 from rhodecode.lib import audit_logger
26 from rhodecode.lib import helpers as h
26 from rhodecode.lib import helpers as h
27 from rhodecode.lib.auth import (LoginRequired, HasRepoPermissionAnyDecorator,
27 from rhodecode.lib.auth import (
28 NotAnonymous, CSRFRequired)
28 LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired)
29 from rhodecode.lib.ext_json import json
29 from rhodecode.lib.ext_json import json
30
30
31 log = logging.getLogger(__name__)
31 log = logging.getLogger(__name__)
@@ -44,7 +44,7 b' class StripView(RepoAppView):'
44 @LoginRequired()
44 @LoginRequired()
45 @HasRepoPermissionAnyDecorator('repository.admin')
45 @HasRepoPermissionAnyDecorator('repository.admin')
46 @view_config(
46 @view_config(
47 route_name='strip', request_method='GET',
47 route_name='edit_repo_strip', request_method='GET',
48 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
48 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
49 def strip(self):
49 def strip(self):
50 c = self.load_default_context()
50 c = self.load_default_context()
@@ -99,10 +99,10 b' class StripView(RepoAppView):'
99 continue
99 continue
100 try:
100 try:
101 ScmModel().strip(
101 ScmModel().strip(
102 repo=c.repo_info,
102 repo=self.db_repo,
103 commit_id=commit['rev'], branch=commit['branch'])
103 commit_id=commit['rev'], branch=commit['branch'])
104 log.info('Stripped commit %s from repo `%s` by %s' % (
104 log.info('Stripped commit %s from repo `%s` by %s' % (
105 commit['rev'], c.repo_info.repo_name, user))
105 commit['rev'], self.db_repo_name, user))
106 data[commit['rev']] = True
106 data[commit['rev']] = True
107
107
108 audit_logger.store_web(
108 audit_logger.store_web(
@@ -22,12 +22,9 b' import logging'
22 import string
22 import string
23
23
24 from pyramid.view import view_config
24 from pyramid.view import view_config
25
26 from beaker.cache import cache_region
25 from beaker.cache import cache_region
27
26
28
29 from rhodecode.controllers import utils
27 from rhodecode.controllers import utils
30
31 from rhodecode.apps._base import RepoAppView
28 from rhodecode.apps._base import RepoAppView
32 from rhodecode.config.conf import (LANGUAGES_EXTENSIONS_MAP)
29 from rhodecode.config.conf import (LANGUAGES_EXTENSIONS_MAP)
33 from rhodecode.lib import caches, helpers as h
30 from rhodecode.lib import caches, helpers as h
@@ -74,11 +71,16 b' class RepoSummaryView(RepoAppView):'
74 log.debug("Searching for a README file.")
71 log.debug("Searching for a README file.")
75 readme_node = ReadmeFinder(default_renderer).search(commit)
72 readme_node = ReadmeFinder(default_renderer).search(commit)
76 if readme_node:
73 if readme_node:
77 relative_url = h.route_path(
74 relative_urls = {
78 'repo_file_raw', repo_name=repo_name,
75 'raw': h.route_path(
79 commit_id=commit.raw_id, f_path=readme_node.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 readme_data = self._render_readme_or_none(
82 readme_data = self._render_readme_or_none(
81 commit, readme_node, relative_url)
83 commit, readme_node, relative_urls)
82 readme_filename = readme_node.path
84 readme_filename = readme_node.path
83 return readme_data, readme_filename
85 return readme_data, readme_filename
84
86
@@ -103,15 +105,15 b' class RepoSummaryView(RepoAppView):'
103 log.exception(
105 log.exception(
104 "Problem getting commit when trying to render the README.")
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 log.debug(
109 log.debug(
108 'Found README file `%s` rendering...', readme_node.path)
110 'Found README file `%s` rendering...', readme_node.path)
109 renderer = MarkupRenderer()
111 renderer = MarkupRenderer()
110 try:
112 try:
111 html_source = renderer.render(
113 html_source = renderer.render(
112 readme_node.content, filename=readme_node.path)
114 readme_node.content, filename=readme_node.path)
113 if relative_url:
115 if relative_urls:
114 return relative_links(html_source, relative_url)
116 return relative_links(html_source, relative_urls)
115 return html_source
117 return html_source
116 except Exception:
118 except Exception:
117 log.exception(
119 log.exception(
@@ -143,6 +143,8 b' def load_pyramid_environment(global_conf'
143
143
144 # Store the settings to make them available to other modules.
144 # Store the settings to make them available to other modules.
145 rhodecode.PYRAMID_SETTINGS = settings_merged
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 # If this is a test run we prepare the test environment like
149 # If this is a test run we prepare the test environment like
148 # creating a test database, test search index and test repositories.
150 # creating a test database, test search index and test repositories.
@@ -176,23 +176,6 b' def make_map(config):'
176 rmap.connect('ping', '%s/ping' % (ADMIN_PREFIX,), controller='home', action='ping')
176 rmap.connect('ping', '%s/ping' % (ADMIN_PREFIX,), controller='home', action='ping')
177 rmap.connect('error_test', '%s/error_test' % (ADMIN_PREFIX,), controller='home', action='error_test')
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 # ADMIN REPOSITORY GROUPS ROUTES
179 # ADMIN REPOSITORY GROUPS ROUTES
197 with rmap.submapper(path_prefix=ADMIN_PREFIX,
180 with rmap.submapper(path_prefix=ADMIN_PREFIX,
198 controller='admin/repo_groups') as m:
181 controller='admin/repo_groups') as m:
@@ -406,81 +389,5 b' def make_map(config):'
406 m.connect('my_account_password', '/my_account/password',
389 m.connect('my_account_password', '/my_account/password',
407 action='my_account_password', conditions={'method': ['GET']})
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 return rmap
393 return rmap
@@ -32,6 +32,7 b' from formencode import htmlfill'
32 from pylons import request, tmpl_context as c, url, config
32 from pylons import request, tmpl_context as c, url, config
33 from pylons.controllers.util import redirect
33 from pylons.controllers.util import redirect
34 from pylons.i18n.translation import _
34 from pylons.i18n.translation import _
35 from pylons.decorators import jsonify
35 from pyramid.threadlocal import get_current_registry
36 from pyramid.threadlocal import get_current_registry
36 from webob.exc import HTTPBadRequest
37 from webob.exc import HTTPBadRequest
37
38
@@ -47,7 +48,6 b' from rhodecode.lib.utils import repo2db_'
47 from rhodecode.lib.utils2 import (
48 from rhodecode.lib.utils2 import (
48 str2bool, safe_unicode, AttributeDict, safe_int)
49 str2bool, safe_unicode, AttributeDict, safe_int)
49 from rhodecode.lib.compat import OrderedDict
50 from rhodecode.lib.compat import OrderedDict
50 from rhodecode.lib.utils import jsonify
51
51
52 from rhodecode.model.db import RhodeCodeUi, Repository
52 from rhodecode.model.db import RhodeCodeUi, Repository
53 from rhodecode.model.forms import ApplicationSettingsForm, \
53 from rhodecode.model.forms import ApplicationSettingsForm, \
@@ -31,25 +31,19 b' from pylons import request, tmpl_context'
31 from pylons.controllers.util import redirect
31 from pylons.controllers.util import redirect
32 from pylons.i18n.translation import _
32 from pylons.i18n.translation import _
33
33
34 from sqlalchemy.orm import joinedload
35
36 from rhodecode.lib import auth
34 from rhodecode.lib import auth
37 from rhodecode.lib import helpers as h
35 from rhodecode.lib import helpers as h
38 from rhodecode.lib import audit_logger
36 from rhodecode.lib import audit_logger
39 from rhodecode.lib.ext_json import json
40 from rhodecode.lib.exceptions import UserGroupAssignedException,\
37 from rhodecode.lib.exceptions import UserGroupAssignedException,\
41 RepoGroupAssignmentError
38 RepoGroupAssignmentError
42 from rhodecode.lib.utils import jsonify
43 from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int
39 from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int
44 from rhodecode.lib.auth import (
40 from rhodecode.lib.auth import (
45 LoginRequired, NotAnonymous, HasUserGroupPermissionAnyDecorator,
41 LoginRequired, NotAnonymous, HasUserGroupPermissionAnyDecorator,
46 HasPermissionAnyDecorator)
42 HasPermissionAnyDecorator)
47 from rhodecode.lib.base import BaseController, render
43 from rhodecode.lib.base import BaseController, render
48 from rhodecode.model.permission import PermissionModel
44 from rhodecode.model.permission import PermissionModel
49 from rhodecode.model.scm import UserGroupList
50 from rhodecode.model.user_group import UserGroupModel
45 from rhodecode.model.user_group import UserGroupModel
51 from rhodecode.model.db import (
46 from rhodecode.model.db import User, UserGroup
52 User, UserGroup, UserGroupRepoToPerm, UserGroupRepoGroupToPerm)
53 from rhodecode.model.forms import (
47 from rhodecode.model.forms import (
54 UserGroupForm, UserGroupPermsForm, UserIndividualPermissionsForm,
48 UserGroupForm, UserGroupPermsForm, UserIndividualPermissionsForm,
55 UserPermissionsForm)
49 UserPermissionsForm)
@@ -34,15 +34,6 b' import pyramid.threadlocal'
34 from paste.auth.basic import AuthBasicAuthenticator
34 from paste.auth.basic import AuthBasicAuthenticator
35 from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden, get_exception
35 from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden, get_exception
36 from paste.httpheaders import WWW_AUTHENTICATE, AUTHORIZATION
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 import rhodecode
38 import rhodecode
48 from rhodecode.authentication.base import VCS_TYPE
39 from rhodecode.authentication.base import VCS_TYPE
@@ -55,13 +46,15 b' from rhodecode.lib.utils import ('
55 get_enabled_hook_classes)
46 get_enabled_hook_classes)
56 from rhodecode.lib.utils2 import (
47 from rhodecode.lib.utils2 import (
57 str2bool, safe_unicode, AttributeDict, safe_int, md5, aslist)
48 str2bool, safe_unicode, AttributeDict, safe_int, md5, aslist)
58 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
59 from rhodecode.model import meta
49 from rhodecode.model import meta
60 from rhodecode.model.db import Repository, User, ChangesetComment
50 from rhodecode.model.db import Repository, User, ChangesetComment
61 from rhodecode.model.notification import NotificationModel
51 from rhodecode.model.notification import NotificationModel
62 from rhodecode.model.scm import ScmModel
52 from rhodecode.model.scm import ScmModel
63 from rhodecode.model.settings import VcsSettingsModel, SettingsModel
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 log = logging.getLogger(__name__)
59 log = logging.getLogger(__name__)
67
60
@@ -75,6 +68,9 b' def render(template_name, extra_vars=Non'
75 ``cache_expire``.
68 ``cache_expire``.
76
69
77 """
70 """
71 from pylons.templating import literal
72 from pylons.templating import cached_template, pylons_globals
73
78 # Create a render callable for the cache function
74 # Create a render callable for the cache function
79 def render_template():
75 def render_template():
80 # Pull in extra vars if needed
76 # Pull in extra vars if needed
@@ -411,10 +407,6 b' def attach_context_attributes(context, r'
411 'refresh_time': 120 * 1000,
407 'refresh_time': 120 * 1000,
412 'cutoff_limit': 1000 * 60 * 60 * 24 * 7
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 'pyramid_dispatch': {
410 'pyramid_dispatch': {
419
411
420 },
412 },
@@ -512,6 +504,7 b' class BaseController(WSGIController):'
512 """
504 """
513 # on each call propagate settings calls into global settings.
505 # on each call propagate settings calls into global settings.
514 from pylons import config
506 from pylons import config
507 from pylons import tmpl_context as c, request, url
515 set_rhodecode_config(config)
508 set_rhodecode_config(config)
516 attach_context_attributes(c, request, self._rhodecode_user.user_id)
509 attach_context_attributes(c, request, self._rhodecode_user.user_id)
517
510
@@ -531,6 +524,7 b' class BaseController(WSGIController):'
531 user_lang, self._rhodecode_user)
524 user_lang, self._rhodecode_user)
532
525
533 def _dispatch_redirect(self, with_url, environ, start_response):
526 def _dispatch_redirect(self, with_url, environ, start_response):
527 from webob.exc import HTTPFound
534 resp = HTTPFound(with_url)
528 resp = HTTPFound(with_url)
535 environ['SCRIPT_NAME'] = '' # handle prefix middleware
529 environ['SCRIPT_NAME'] = '' # handle prefix middleware
536 environ['PATH_INFO'] = with_url
530 environ['PATH_INFO'] = with_url
@@ -542,6 +536,7 b' class BaseController(WSGIController):'
542 # the request is routed to. This routing information is
536 # the request is routed to. This routing information is
543 # available in environ['pylons.routes_dict']
537 # available in environ['pylons.routes_dict']
544 from rhodecode.lib import helpers as h
538 from rhodecode.lib import helpers as h
539 from pylons import tmpl_context as c, request, url
545
540
546 # Provide the Pylons context to Pyramid's debugtoolbar if it asks
541 # Provide the Pylons context to Pyramid's debugtoolbar if it asks
547 if environ.get('debugtoolbar.wants_pylons_context', False):
542 if environ.get('debugtoolbar.wants_pylons_context', False):
@@ -620,93 +615,3 b' def bootstrap_request(**kwargs):'
620
615
621 config = pyramid.testing.setUp(request=request)
616 config = pyramid.testing.setUp(request=request)
622 add_events_routes(config)
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 initialize_database(config)
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 class PartialRenderer(object):
855 class PartialRenderer(object):
880 """
856 """
881 Partial renderer used to render chunks of html used in datagrids
857 Partial renderer used to render chunks of html used in datagrids
@@ -18,10 +18,6 b''
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
22 Repository model for rhodecode
23 """
24
25 import logging
21 import logging
26 import os
22 import os
27 import re
23 import re
@@ -55,7 +55,7 b' function setRCMouseBindings(repoName, re'
55 window.location = pyroutes.url('gists_new');
55 window.location = pyroutes.url('gists_new');
56 });
56 });
57 Mousetrap.bind(['n r'], function(e) {
57 Mousetrap.bind(['n r'], function(e) {
58 window.location = pyroutes.url('new_repo');
58 window.location = pyroutes.url('repo_new');
59 });
59 });
60
60
61 if (repoName && repoName != '') {
61 if (repoName && repoName != '') {
@@ -12,7 +12,6 b''
12 ******************************************************************************/
12 ******************************************************************************/
13 function registerRCRoutes() {
13 function registerRCRoutes() {
14 // routes registration
14 // routes registration
15 pyroutes.register('new_repo', '/_admin/create_repository', []);
16 pyroutes.register('edit_user', '/_admin/users/%(user_id)s/edit', ['user_id']);
15 pyroutes.register('edit_user', '/_admin/users/%(user_id)s/edit', ['user_id']);
17 pyroutes.register('favicon', '/favicon.ico', []);
16 pyroutes.register('favicon', '/favicon.ico', []);
18 pyroutes.register('robots', '/robots.txt', []);
17 pyroutes.register('robots', '/robots.txt', []);
@@ -82,6 +81,9 b' function registerRCRoutes() {'
82 pyroutes.register('user_group_members_data', '/_admin/user_groups/%(user_group_id)s/members', ['user_group_id']);
81 pyroutes.register('user_group_members_data', '/_admin/user_groups/%(user_group_id)s/members', ['user_group_id']);
83 pyroutes.register('edit_user_group_perms_summary', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary', ['user_group_id']);
82 pyroutes.register('edit_user_group_perms_summary', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary', ['user_group_id']);
84 pyroutes.register('edit_user_group_perms_summary_json', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary/json', ['user_group_id']);
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 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
87 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
86 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
88 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
87 pyroutes.register('channelstream_proxy', '/_channelstream', []);
89 pyroutes.register('channelstream_proxy', '/_channelstream', []);
@@ -177,11 +179,26 b' function registerRCRoutes() {'
177 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
179 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
178 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
180 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
179 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
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 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
199 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
181 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/settings/review/default-reviewers', ['repo_name']);
200 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/settings/review/default-reviewers', ['repo_name']);
182 pyroutes.register('repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
201 pyroutes.register('edit_repo_strip', '/%(repo_name)s/settings/strip', ['repo_name']);
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']);
185 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
202 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
186 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
203 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
187 pyroutes.register('rss_feed_home', '/%(repo_name)s/feed/rss', ['repo_name']);
204 pyroutes.register('rss_feed_home', '/%(repo_name)s/feed/rss', ['repo_name']);
@@ -10,9 +10,9 b''
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 %if c.rhodecode_user.is_admin:
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 &raquo;
14 &raquo;
15 ${h.link_to(_('Repositories'),h.url('repos'))}
15 ${h.link_to(_('Repositories'), h.route_path('repos'))}
16 %else:
16 %else:
17 ${_('Admin')}
17 ${_('Admin')}
18 &raquo;
18 &raquo;
@@ -1,6 +1,6 b''
1 ## -*- coding: utf-8 -*-
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 <div class="form">
4 <div class="form">
5 <!-- fields -->
5 <!-- fields -->
6 <div class="fields">
6 <div class="fields">
@@ -52,24 +52,24 b''
52 <a href="${h.route_path('edit_repo_advanced', repo_name=c.repo_name)}">${_('Advanced')}</a>
52 <a href="${h.route_path('edit_repo_advanced', repo_name=c.repo_name)}">${_('Advanced')}</a>
53 </li>
53 </li>
54 <li class="${'active' if c.active=='vcs' else ''}">
54 <li class="${'active' if c.active=='vcs' else ''}">
55 <a href="${h.url('repo_vcs_settings', repo_name=c.repo_name)}">${_('VCS')}</a>
55 <a href="${h.route_path('edit_repo_vcs', repo_name=c.repo_name)}">${_('VCS')}</a>
56 </li>
56 </li>
57 <li class="${'active' if c.active=='fields' else ''}">
57 <li class="${'active' if c.active=='fields' else ''}">
58 <a href="${h.url('edit_repo_fields', repo_name=c.repo_name)}">${_('Extra Fields')}</a>
58 <a href="${h.route_path('edit_repo_fields', repo_name=c.repo_name)}">${_('Extra Fields')}</a>
59 </li>
59 </li>
60 <li class="${'active' if c.active=='issuetracker' else ''}">
60 <li class="${'active' if c.active=='issuetracker' else ''}">
61 <a href="${h.url('repo_settings_issuetracker', repo_name=c.repo_name)}">${_('Issue Tracker')}</a>
61 <a href="${h.route_path('edit_repo_issuetracker', repo_name=c.repo_name)}">${_('Issue Tracker')}</a>
62 </li>
62 </li>
63 <li class="${'active' if c.active=='caches' else ''}">
63 <li class="${'active' if c.active=='caches' else ''}">
64 <a href="${h.route_path('edit_repo_caches', repo_name=c.repo_name)}">${_('Caches')}</a>
64 <a href="${h.route_path('edit_repo_caches', repo_name=c.repo_name)}">${_('Caches')}</a>
65 </li>
65 </li>
66 %if c.repo_info.repo_type != 'svn':
66 %if c.repo_info.repo_type != 'svn':
67 <li class="${'active' if c.active=='remote' else ''}">
67 <li class="${'active' if c.active=='remote' else ''}">
68 <a href="${h.url('edit_repo_remote', repo_name=c.repo_name)}">${_('Remote')}</a>
68 <a href="${h.route_path('edit_repo_remote', repo_name=c.repo_name)}">${_('Remote')}</a>
69 </li>
69 </li>
70 %endif
70 %endif
71 <li class="${'active' if c.active=='statistics' else ''}">
71 <li class="${'active' if c.active=='statistics' else ''}">
72 <a href="${h.url('edit_repo_statistics', repo_name=c.repo_name)}">${_('Statistics')}</a>
72 <a href="${h.route_path('edit_repo_statistics', repo_name=c.repo_name)}">${_('Statistics')}</a>
73 </li>
73 </li>
74 <li class="${'active' if c.active=='integrations' else ''}">
74 <li class="${'active' if c.active=='integrations' else ''}">
75 <a href="${h.route_path('repo_integrations_home', repo_name=c.repo_name)}">${_('Integrations')}</a>
75 <a href="${h.route_path('repo_integrations_home', repo_name=c.repo_name)}">${_('Integrations')}</a>
@@ -80,10 +80,10 b''
80 </li>
80 </li>
81 %endif
81 %endif
82 <li class="${'active' if c.active=='maintenance' else ''}">
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 </li>
84 </li>
85 <li class="${'active' if c.active=='strip' else ''}">
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 </li>
87 </li>
88
88
89 </ul>
89 </ul>
@@ -18,7 +18,7 b''
18 <td class="td-hash">${field.field_key}</td>
18 <td class="td-hash">${field.field_key}</td>
19 <td class="td-type">${field.field_type}</td>
19 <td class="td-type">${field.field_type}</td>
20 <td class="td-action">
20 <td class="td-action">
21 ${h.secure_form(h.url('delete_repo_fields', repo_name=c.repo_info.repo_name, field_id=field.repo_field_id),method='delete')}
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 ${h.hidden('del_repo_field',field.repo_field_id)}
22 ${h.hidden('del_repo_field',field.repo_field_id)}
23 <button class="btn btn-link btn-danger" type="submit"
23 <button class="btn btn-link btn-danger" type="submit"
24 onclick="return confirm('${_('Confirm to delete this field: %s') % field.field_key}');">
24 onclick="return confirm('${_('Confirm to delete this field: %s') % field.field_key}');">
@@ -31,7 +31,7 b''
31 </table>
31 </table>
32 </div>
32 </div>
33 %endif
33 %endif
34 ${h.secure_form(h.url('create_repo_fields', repo_name=c.repo_name),method='put')}
34 ${h.secure_form(h.route_path('edit_repo_fields_create', repo_name=c.repo_name), method='POST', request=request)}
35 <div class="form">
35 <div class="form">
36 <!-- fields -->
36 <!-- fields -->
37 <div class="fields">
37 <div class="fields">
@@ -1,7 +1,7 b''
1 <%namespace name="its" file="/base/issue_tracker_settings.mako"/>
1 <%namespace name="its" file="/base/issue_tracker_settings.mako"/>
2
2
3 <div id="repo_issue_tracker" class="${'inherited' if c.settings_model.inherit_global_settings else ''}">
3 <div id="repo_issue_tracker" class="${'inherited' if c.settings_model.inherit_global_settings else ''}">
4 ${h.secure_form(h.url('repo_issuetracker_save', repo_name=c.repo_name), method='post', id="inherit-form")}
4 ${h.secure_form(h.route_path('edit_repo_issuetracker_update', repo_name=c.repo_name), id="inherit-form", method='POST', request=request)}
5 <div class="panel panel-default panel-body">
5 <div class="panel panel-default panel-body">
6 <div class="fields">
6 <div class="fields">
7 <div class="field">
7 <div class="field">
@@ -72,8 +72,8 b''
72 <div class="panel-body">
72 <div class="panel-body">
73 ${its.issue_tracker_settings_table(
73 ${its.issue_tracker_settings_table(
74 patterns=c.repo_patterns.items(),
74 patterns=c.repo_patterns.items(),
75 form_url=h.url('repo_settings_issuetracker', repo_name=c.repo_info.repo_name),
75 form_url=h.route_path('edit_repo_issuetracker', repo_name=c.repo_info.repo_name),
76 delete_url=h.url('repo_issuetracker_delete', 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 <div class="buttons">
78 <div class="buttons">
79 <button type="submit" class="btn btn-primary save-inheritance" id="save">${_('Save')}</button>
79 <button type="submit" class="btn btn-primary save-inheritance" id="save">${_('Save')}</button>
@@ -92,7 +92,7 b''
92 </div>
92 </div>
93 <div class="panel-body">
93 <div class="panel-body">
94 ${its.issue_tracker_new_row()}
94 ${its.issue_tracker_new_row()}
95 ${its.issue_tracker_settings_test(test_url=h.url('repo_issuetracker_test', repo_name=c.repo_info.repo_name))}
95 ${its.issue_tracker_settings_test(test_url=h.route_path('edit_repo_issuetracker_test', repo_name=c.repo_info.repo_name))}
96 </div>
96 </div>
97 </div>
97 </div>
98
98
@@ -19,7 +19,7 b''
19 </code>
19 </code>
20 </p>
20 </p>
21
21
22 ${h.secure_form(h.url('edit_repo_remote', repo_name=c.repo_name), method='put')}
22 ${h.secure_form(h.route_path('edit_repo_remote_pull', repo_name=c.repo_name), method='POST', request=request)}
23 <div class="form">
23 <div class="form">
24 <div class="fields">
24 <div class="fields">
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')+"');")}
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 ${h.hidden('repo_clone_uri_change', 'NEW')}
83 ${h.hidden('repo_clone_uri_change', 'NEW')}
84 %endif
84 %endif
85 <p id="alter_clone_uri_help_block" class="help-block">
85 <p id="alter_clone_uri_help_block" class="help-block">
86 <% pull_link = h.literal(h.link_to('remote sync', h.url('edit_repo_remote', repo_name=c.repo_name))) %>
86 <% pull_link = h.literal(h.link_to('remote sync', h.route_path('edit_repo_remote', repo_name=c.repo_name))) %>
87 ${_('http[s] url where from repository was imported, this field can used for doing {pull_link}.').format(pull_link=pull_link)|n} <br/>
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 ${_('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.')}
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 </p>
89 </p>
@@ -3,7 +3,7 b''
3 <h3 class="panel-title">${_('Repository statistics')}</h3>
3 <h3 class="panel-title">${_('Repository statistics')}</h3>
4 </div>
4 </div>
5 <div class="panel-body">
5 <div class="panel-body">
6 ${h.secure_form(h.url('edit_repo_statistics', repo_name=c.repo_info.repo_name), method='put')}
6 ${h.secure_form(h.route_path('edit_repo_statistics_reset', repo_name=c.repo_info.repo_name), method='POST', request=request)}
7 <div class="form">
7 <div class="form">
8 <div class="fields">
8 <div class="fields">
9 <div class="field" >
9 <div class="field" >
@@ -1,7 +1,7 b''
1 <%namespace name="vcss" file="/base/vcs_settings.mako"/>
1 <%namespace name="vcss" file="/base/vcs_settings.mako"/>
2
2
3 <div id="repo_vcs_settings" class="${'inherited' if c.inherit_global_settings else ''}">
3 <div id="repo_vcs_settings" class="${'inherited' if c.inherit_global_settings else ''}">
4 ${h.secure_form(h.url('repo_vcs_settings', repo_name=c.repo_info.repo_name), method='post')}
4 ${h.secure_form(h.route_path('edit_repo_vcs_update', repo_name=c.repo_info.repo_name), method='POST', request=request)}
5 <div class="form panel panel-default">
5 <div class="form panel panel-default">
6 <div class="fields panel-body">
6 <div class="fields panel-body">
7 <div class="field">
7 <div class="field">
@@ -50,13 +50,12 b''
50 <script type="text/javascript">
50 <script type="text/javascript">
51
51
52 function ajaxDeletePattern(pattern_id, field_id) {
52 function ajaxDeletePattern(pattern_id, field_id) {
53 var sUrl = "${h.url('repo_vcs_settings', repo_name=c.repo_info.repo_name)}";
53 var sUrl = "${h.route_path('edit_repo_vcs_svn_pattern_delete', repo_name=c.repo_info.repo_name)}";
54 var callback = function (o) {
54 var callback = function (o) {
55 var elem = $("#"+field_id);
55 var elem = $("#"+field_id);
56 elem.remove();
56 elem.remove();
57 };
57 };
58 var postData = {
58 var postData = {
59 '_method': 'delete',
60 'delete_svn_pattern': pattern_id,
59 'delete_svn_pattern': pattern_id,
61 'csrf_token': CSRF_TOKEN
60 'csrf_token': CSRF_TOKEN
62 };
61 };
@@ -24,7 +24,7 b''
24 <ul class="links">
24 <ul class="links">
25 %if h.HasPermissionAny('hg.admin','hg.create.repository')():
25 %if h.HasPermissionAny('hg.admin','hg.create.repository')():
26 <li>
26 <li>
27 <a href="${h.url('new_repo')}" class="btn btn-small btn-success">${_(u'Add Repository')}</a>
27 <a href="${h.route_path('repo_new')}" class="btn btn-small btn-success">${_(u'Add Repository')}</a>
28 </li>
28 </li>
29 %endif
29 %endif
30 </ul>
30 </ul>
@@ -73,7 +73,7 b''
73 <%def name="admin_menu()">
73 <%def name="admin_menu()">
74 <ul class="admin_menu submenu">
74 <ul class="admin_menu submenu">
75 <li><a href="${h.route_path('admin_audit_logs')}">${_('Admin audit logs')}</a></li>
75 <li><a href="${h.route_path('admin_audit_logs')}">${_('Admin audit logs')}</a></li>
76 <li><a href="${h.url('repos')}">${_('Repositories')}</a></li>
76 <li><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
77 <li><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
77 <li><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
78 <li><a href="${h.route_path('users')}">${_('Users')}</a></li>
78 <li><a href="${h.route_path('users')}">${_('Users')}</a></li>
79 <li><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
79 <li><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
@@ -145,7 +145,7 b''
145 <%def name="admin_menu_simple(repositories=None, repository_groups=None, user_groups=None)">
145 <%def name="admin_menu_simple(repositories=None, repository_groups=None, user_groups=None)">
146 <ul class="submenu">
146 <ul class="submenu">
147 %if repositories:
147 %if repositories:
148 <li class="local-admin-repos"><a href="${h.url('repos')}">${_('Repositories')}</a></li>
148 <li class="local-admin-repos"><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
149 %endif
149 %endif
150 %if repository_groups:
150 %if repository_groups:
151 <li class="local-admin-repo-groups"><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
151 <li class="local-admin-repo-groups"><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
@@ -268,9 +268,9 b''
268
268
269 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking:
269 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking:
270 %if c.rhodecode_db_repo.locked[0]:
270 %if c.rhodecode_db_repo.locked[0]:
271 <li><a class="locking_del" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Unlock')}</a></li>
271 <li><a class="locking_del" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Unlock')}</a></li>
272 %else:
272 %else:
273 <li><a class="locking_add" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Lock')}</a></li>
273 <li><a class="locking_add" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Lock')}</a></li>
274 %endif
274 %endif
275 %endif
275 %endif
276 %if c.rhodecode_user.username != h.DEFAULT_USER:
276 %if c.rhodecode_user.username != h.DEFAULT_USER:
@@ -108,8 +108,8 b''
108 alert("Error while deleting entry.\nError code {0} ({1}). URL: {2}".format(data.status,data.statusText,$(entry)[0].url));
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 };
111 }
112 }
112 };
113
113
114 $('.delete_issuetracker_entry').on('click', function(e){
114 $('.delete_issuetracker_entry').on('click', function(e){
115 e.preventDefault();
115 e.preventDefault();
@@ -74,6 +74,8 b''
74 %else:
74 %else:
75 % if c.file.size < c.visual.cut_off_limit_file:
75 % if c.file.size < c.visual.cut_off_limit_file:
76 %if c.renderer and not c.annotate:
76 %if c.renderer and not c.annotate:
77 ## pick relative url based on renderer
78 ${c.renderer} XXXX
77 <%
79 <%
78 relative_urls = {
80 relative_urls = {
79 'raw': h.route_path('repo_file_raw',repo_name=c.repo_name,commit_id=c.commit.raw_id,f_path=c.f_path),
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 %if not c.repo_group:
27 %if not c.repo_group:
28 ## no repository group context here
28 ## no repository group context here
29 %if is_admin or create_repo:
29 %if is_admin or create_repo:
30 <a href="${h.url('new_repo')}" class="btn btn-small btn-success btn-primary">${_('Add Repository')}</a>
30 <a href="${h.route_path('repo_new')}" class="btn btn-small btn-success btn-primary">${_('Add Repository')}</a>
31 %endif
31 %endif
32
32
33 %if is_admin or create_repo_group:
33 %if is_admin or create_repo_group:
@@ -36,7 +36,7 b''
36 %else:
36 %else:
37 ##we're inside other repository group other terms apply
37 ##we're inside other repository group other terms apply
38 %if is_admin or group_admin or (group_write and create_on_write):
38 %if is_admin or group_admin or (group_write and create_on_write):
39 <a href="${h.url('new_repo',parent_group=c.repo_group.group_id)}" class="btn btn-small btn-success btn-primary">${_('Add Repository')}</a>
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 %endif
40 %endif
41 %if is_admin or group_admin:
41 %if is_admin or group_admin:
42 <a href="${h.url('new_repo_group', parent_group=c.repo_group.group_id)}" class="btn btn-small btn-default">${_(u'Add Repository Group')}</a>
42 <a href="${h.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 <div class="alert alert-dismissable alert-warning">
15 <div class="alert alert-dismissable alert-warning">
16 <strong>Missing requirements</strong>
16 <strong>Missing requirements</strong>
17 Commits cannot be displayed, because this repository uses one or more extensions, which was not enabled. <br/>
17 Commits cannot be displayed, because this repository uses one or more extensions, which was not enabled. <br/>
18 Please <a href="${h.url('repo_vcs_settings', repo_name=c.repo_name)}">enable extension in settings</a>, or contact the repository owner for help.
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 Missing extensions could be:
19 Missing extensions could be:
20 <pre>
20 <pre>
21
21
@@ -20,10 +20,7 b''
20
20
21 import pytest
21 import pytest
22
22
23 from rhodecode.tests import (
23 from rhodecode.tests import TestController, url
24 TestController, url, assert_session_flash, link_to)
25 from rhodecode.model.db import User, UserGroup
26 from rhodecode.model.meta import Session
27 from rhodecode.tests.fixture import Fixture
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 base_url = {
31 base_url = {
35 'home': '/',
32 'home': '/',
33 'repos':
34 ADMIN_PREFIX + '/repos',
36 'user_groups':
35 'user_groups':
37 ADMIN_PREFIX + '/user_groups',
36 ADMIN_PREFIX + '/user_groups',
38 'user_groups_data':
37 'user_groups_data':
@@ -63,7 +62,7 b' class TestAdminDelegatedUser(TestControl'
63 assert_response.no_element_exists('li.local-admin-repo-groups')
62 assert_response.no_element_exists('li.local-admin-repo-groups')
64 assert_response.no_element_exists('li.local-admin-user-groups')
63 assert_response.no_element_exists('li.local-admin-user-groups')
65
64
66 response = self.app.get(url('repos'), status=200)
65 response = self.app.get(route_path('repos'), status=200)
67 response.mustcontain('data: []')
66 response.mustcontain('data: []')
68
67
69 response = self.app.get(url('repo_groups'), status=200)
68 response = self.app.get(url('repo_groups'), status=200)
@@ -98,7 +97,7 b' class TestAdminDelegatedUser(TestControl'
98 assert_response.one_element_exists('li.local-admin-user-groups')
97 assert_response.one_element_exists('li.local-admin-user-groups')
99
98
100 # admin interfaces have visible elements
99 # admin interfaces have visible elements
101 response = self.app.get(url('repos'), status=200)
100 response = self.app.get(route_path('repos'), status=200)
102 response.mustcontain('"name_raw": "{}"'.format(repo_name))
101 response.mustcontain('"name_raw": "{}"'.format(repo_name))
103
102
104 response = self.app.get(url('repo_groups'), status=200)
103 response = self.app.get(url('repo_groups'), status=200)
@@ -140,7 +139,7 b' class TestAdminDelegatedUser(TestControl'
140 assert_response.one_element_exists('li.local-admin-user-groups')
139 assert_response.one_element_exists('li.local-admin-user-groups')
141
140
142 # admin interfaces have visible elements
141 # admin interfaces have visible elements
143 response = self.app.get(url('repos'), status=200)
142 response = self.app.get(route_path('repos'), status=200)
144 response.mustcontain('"name_raw": "{}"'.format(repo_name))
143 response.mustcontain('"name_raw": "{}"'.format(repo_name))
145
144
146 response = self.app.get(url('repo_groups'), status=200)
145 response = self.app.get(url('repo_groups'), status=200)
@@ -22,8 +22,6 b' import pytest'
22 from mock import Mock, patch
22 from mock import Mock, patch
23
23
24 from rhodecode.lib import base
24 from rhodecode.lib import base
25 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
26 from rhodecode.lib import helpers as h
27 from rhodecode.model import db
25 from rhodecode.model import db
28
26
29
27
@@ -94,162 +92,3 b' def call_vcs_operation_context(**kwargs_'
94 return result
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 def test_cache_is_marked_for_invalidation(self, repo_stub):
1045 def test_cache_is_marked_for_invalidation(self, repo_stub):
1046 model = VcsSettingsModel(repo=repo_stub)
1046 model = VcsSettingsModel(repo=repo_stub)
1047 invalidation_patcher = mock.patch(
1047 invalidation_patcher = mock.patch(
1048 'rhodecode.controllers.admin.repos.ScmModel.mark_for_invalidation')
1048 'rhodecode.model.scm.ScmModel.mark_for_invalidation')
1049 with invalidation_patcher as invalidation_mock:
1049 with invalidation_patcher as invalidation_mock:
1050 model.create_or_update_repo_settings(
1050 model.create_or_update_repo_settings(
1051 data=self.FORM_DATA, inherit_global_settings=True)
1051 data=self.FORM_DATA, inherit_global_settings=True)
1 NO CONTENT: file was removed
NO CONTENT: file was removed
This diff has been collapsed as it changes many lines, (563 lines changed) Show them Hide them
General Comments 0
You need to be logged in to leave comments. Login now