##// END OF EJS Templates
repo-settings: moved advanced setion into pyramid views....
marcink -
r1751:aa1d7f99 default
parent child Browse files
Show More
@@ -0,0 +1,150 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import pytest
22
23 from rhodecode.lib.utils2 import safe_unicode, safe_str
24 from rhodecode.model.db import Repository
25 from rhodecode.model.repo import RepoModel
26 from rhodecode.tests import (
27 HG_REPO, GIT_REPO, assert_session_flash, no_newline_id_generator)
28 from rhodecode.tests.fixture import Fixture
29 from rhodecode.tests.utils import repo_on_filesystem
30
31 fixture = Fixture()
32
33
34 def route_path(name, params=None, **kwargs):
35 import urllib
36
37 base_url = {
38 'repo_summary_explicit': '/{repo_name}/summary',
39 'repo_summary': '/{repo_name}',
40 'edit_repo_advanced': '/{repo_name}/settings/advanced',
41 'edit_repo_advanced_delete': '/{repo_name}/settings/advanced/delete',
42 'edit_repo_advanced_fork': '/{repo_name}/settings/advanced/fork',
43 'edit_repo_advanced_locking': '/{repo_name}/settings/advanced/locking',
44 'edit_repo_advanced_journal': '/{repo_name}/settings/advanced/journal',
45
46 }[name].format(**kwargs)
47
48 if params:
49 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
50 return base_url
51
52
53 @pytest.mark.usefixtures('autologin_user', 'app')
54 class TestAdminRepoSettingsAdvanced(object):
55
56 def test_set_repo_fork_has_no_self_id(self, autologin_user, backend):
57 repo = backend.repo
58 response = self.app.get(
59 route_path('edit_repo_advanced', repo_name=backend.repo_name))
60 opt = """<option value="%s">vcs_test_git</option>""" % repo.repo_id
61 response.mustcontain(no=[opt])
62
63 def test_set_fork_of_target_repo(
64 self, autologin_user, backend, csrf_token):
65 target_repo = 'target_%s' % backend.alias
66 fixture.create_repo(target_repo, repo_type=backend.alias)
67 repo2 = Repository.get_by_repo_name(target_repo)
68 response = self.app.post(
69 route_path('edit_repo_advanced_fork', repo_name=backend.repo_name),
70 params={'id_fork_of': repo2.repo_id,
71 'csrf_token': csrf_token})
72 repo = Repository.get_by_repo_name(backend.repo_name)
73 repo2 = Repository.get_by_repo_name(target_repo)
74 assert_session_flash(
75 response,
76 'Marked repo %s as fork of %s' % (repo.repo_name, repo2.repo_name))
77
78 assert repo.fork == repo2
79 response = response.follow()
80 # check if given repo is selected
81
82 opt = 'This repository is a fork of <a href="%s">%s</a>' % (
83 route_path('repo_summary', repo_name=repo2.repo_name),
84 repo2.repo_name)
85
86 response.mustcontain(opt)
87
88 fixture.destroy_repo(target_repo, forks='detach')
89
90 @pytest.mark.backends("hg", "git")
91 def test_set_fork_of_other_type_repo(
92 self, autologin_user, backend, csrf_token):
93 TARGET_REPO_MAP = {
94 'git': {
95 'type': 'hg',
96 'repo_name': HG_REPO},
97 'hg': {
98 'type': 'git',
99 'repo_name': GIT_REPO},
100 }
101 target_repo = TARGET_REPO_MAP[backend.alias]
102
103 repo2 = Repository.get_by_repo_name(target_repo['repo_name'])
104 response = self.app.post(
105 route_path('edit_repo_advanced_fork', repo_name=backend.repo_name),
106 params={'id_fork_of': repo2.repo_id,
107 'csrf_token': csrf_token})
108 assert_session_flash(
109 response,
110 'Cannot set repository as fork of repository with other type')
111
112 def test_set_fork_of_none(self, autologin_user, backend, csrf_token):
113 # mark it as None
114 response = self.app.post(
115 route_path('edit_repo_advanced_fork', repo_name=backend.repo_name),
116 params={'id_fork_of': None, '_method': 'put',
117 'csrf_token': csrf_token})
118 assert_session_flash(
119 response,
120 'Marked repo %s as fork of %s'
121 % (backend.repo_name, "Nothing"))
122 assert backend.repo.fork is None
123
124 def test_set_fork_of_same_repo(self, autologin_user, backend, csrf_token):
125 repo = Repository.get_by_repo_name(backend.repo_name)
126 response = self.app.post(
127 route_path('edit_repo_advanced_fork', repo_name=backend.repo_name),
128 params={'id_fork_of': repo.repo_id, 'csrf_token': csrf_token})
129 assert_session_flash(
130 response, 'An error occurred during this operation')
131
132 @pytest.mark.parametrize(
133 "suffix",
134 ['', u'ąęł' , '123'],
135 ids=no_newline_id_generator)
136 def test_advanced_delete(self, autologin_user, backend, suffix, csrf_token):
137 repo = backend.create_repo(name_suffix=suffix)
138 repo_name = repo.repo_name
139 repo_name_str = safe_str(repo.repo_name)
140
141 response = self.app.post(
142 route_path('edit_repo_advanced_delete', repo_name=repo_name_str),
143 params={'csrf_token': csrf_token})
144 assert_session_flash(response,
145 u'Deleted repository `{}`'.format(repo_name))
146 response.follow()
147
148 # check if repo was deleted from db
149 assert RepoModel().get_by_repo_name(repo_name) is None
150 assert not repo_on_filesystem(repo_name_str)
@@ -0,0 +1,225 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2011-2017 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import logging
22
23 from pyramid.view import view_config
24 from pyramid.httpexceptions import HTTPFound
25
26 from rhodecode.apps._base import RepoAppView
27 from rhodecode.lib import helpers as h
28 from rhodecode.lib import audit_logger
29 from rhodecode.lib.auth import (
30 LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired)
31 from rhodecode.lib.exceptions import AttachedForksError
32 from rhodecode.lib.utils2 import safe_int
33 from rhodecode.lib.vcs import RepositoryError
34 from rhodecode.model.db import Session, UserFollowing, User, Repository
35 from rhodecode.model.repo import RepoModel
36 from rhodecode.model.scm import ScmModel
37
38 log = logging.getLogger(__name__)
39
40
41 class RepoSettingsView(RepoAppView):
42
43 def load_default_context(self):
44 c = self._get_local_tmpl_context()
45
46 # TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead
47 c.repo_info = self.db_repo
48
49 self._register_global_c(c)
50 return c
51
52 @LoginRequired()
53 @HasRepoPermissionAnyDecorator('repository.admin')
54 @view_config(
55 route_name='edit_repo_advanced', request_method='GET',
56 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
57 def edit_advanced(self):
58 c = self.load_default_context()
59 c.active = 'advanced'
60
61 c.default_user_id = User.get_default_user().user_id
62 c.in_public_journal = UserFollowing.query() \
63 .filter(UserFollowing.user_id == c.default_user_id) \
64 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
65
66 c.has_origin_repo_read_perm = False
67 if self.db_repo.fork:
68 c.has_origin_repo_read_perm = h.HasRepoPermissionAny(
69 'repository.write', 'repository.read', 'repository.admin')(
70 self.db_repo.fork.repo_name, 'repo set as fork page')
71
72 return self._get_template_context(c)
73
74 @LoginRequired()
75 @HasRepoPermissionAnyDecorator('repository.admin')
76 @view_config(
77 route_name='edit_repo_advanced_delete', request_method='POST',
78 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
79 def edit_advanced_delete(self):
80 """
81 Deletes the repository, or shows warnings if deletion is not possible
82 because of attached forks or other errors.
83 """
84 _ = self.request.translate
85 handle_forks = self.request.POST.get('forks', None)
86
87 try:
88 _forks = self.db_repo.forks.count()
89 if _forks and handle_forks:
90 if handle_forks == 'detach_forks':
91 handle_forks = 'detach'
92 h.flash(_('Detached %s forks') % _forks, category='success')
93 elif handle_forks == 'delete_forks':
94 handle_forks = 'delete'
95 h.flash(_('Deleted %s forks') % _forks, category='success')
96
97 repo_data = self.db_repo.get_api_data()
98 RepoModel().delete(self.db_repo, forks=handle_forks)
99
100 repo = audit_logger.RepoWrap(repo_id=self.db_repo.repo_id,
101 repo_name=self.db_repo.repo_name)
102 audit_logger.store(
103 action='repo.delete', action_data={'repo_data': repo_data},
104 user=self._rhodecode_user, repo=repo, commit=False)
105
106 ScmModel().mark_for_invalidation(self.db_repo_name)
107 h.flash(
108 _('Deleted repository `%s`') % self.db_repo_name,
109 category='success')
110 Session().commit()
111 except AttachedForksError:
112 repo_advanced_url = h.route_path(
113 'edit_repo_advanced', repo_name=self.db_repo_name,
114 _anchor='advanced-delete')
115 delete_anchor = h.link_to(_('detach or delete'), repo_advanced_url)
116 h.flash(_('Cannot delete `{repo}` it still contains attached forks. '
117 'Try using {delete_or_detach} option.')
118 .format(repo=self.db_repo_name, delete_or_detach=delete_anchor),
119 category='warning')
120
121 # redirect to advanced for forks handle action ?
122 raise HTTPFound(repo_advanced_url)
123
124 except Exception:
125 log.exception("Exception during deletion of repository")
126 h.flash(_('An error occurred during deletion of `%s`')
127 % self.db_repo_name, category='error')
128 # redirect to advanced for more deletion options
129 raise HTTPFound(
130 h.route_path('edit_repo_advanced', repo_name=self.db_repo_name),
131 _anchor='advanced-delete')
132
133 raise HTTPFound(h.route_path('home'))
134
135 @LoginRequired()
136 @HasRepoPermissionAnyDecorator('repository.admin')
137 @CSRFRequired()
138 @view_config(
139 route_name='edit_repo_advanced_journal', request_method='POST',
140 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
141 def edit_advanced_journal(self):
142 """
143 Set's this repository to be visible in public journal,
144 in other words making default user to follow this repo
145 """
146 _ = self.request.translate
147
148 try:
149 user_id = User.get_default_user().user_id
150 ScmModel().toggle_following_repo(self.db_repo.repo_id, user_id)
151 h.flash(_('Updated repository visibility in public journal'),
152 category='success')
153 Session().commit()
154 except Exception:
155 h.flash(_('An error occurred during setting this '
156 'repository in public journal'),
157 category='error')
158
159 raise HTTPFound(
160 h.route_path('edit_repo_advanced', repo_name=self.db_repo_name))
161
162 @LoginRequired()
163 @HasRepoPermissionAnyDecorator('repository.admin')
164 @CSRFRequired()
165 @view_config(
166 route_name='edit_repo_advanced_fork', request_method='POST',
167 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
168 def edit_advanced_fork(self):
169 """
170 Mark given repository as a fork of another
171 """
172 _ = self.request.translate
173
174 new_fork_id = self.request.POST.get('id_fork_of')
175 try:
176
177 if new_fork_id and not new_fork_id.isdigit():
178 log.error('Given fork id %s is not an INT', new_fork_id)
179
180 fork_id = safe_int(new_fork_id)
181 repo = ScmModel().mark_as_fork(
182 self.db_repo_name, fork_id, self._rhodecode_user.user_id)
183 fork = repo.fork.repo_name if repo.fork else _('Nothing')
184 Session().commit()
185 h.flash(_('Marked repo %s as fork of %s') % (self.db_repo_name, fork),
186 category='success')
187 except RepositoryError as e:
188 log.exception("Repository Error occurred")
189 h.flash(str(e), category='error')
190 except Exception as e:
191 log.exception("Exception while editing fork")
192 h.flash(_('An error occurred during this operation'),
193 category='error')
194
195 raise HTTPFound(
196 h.route_path('edit_repo_advanced', repo_name=self.db_repo_name))
197
198 @LoginRequired()
199 @HasRepoPermissionAnyDecorator('repository.admin')
200 @CSRFRequired()
201 @view_config(
202 route_name='edit_repo_advanced_locking', request_method='POST',
203 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
204 def edit_advanced_locking(self):
205 """
206 Toggle locking of repository
207 """
208 _ = self.request.translate
209 set_lock = self.request.POST.get('set_lock')
210 set_unlock = self.request.POST.get('set_unlock')
211
212 try:
213 if set_lock:
214 Repository.lock(self.db_repo, self._rhodecode_user.user_id,
215 lock_reason=Repository.LOCK_WEB)
216 h.flash(_('Locked repository'), category='success')
217 elif set_unlock:
218 Repository.unlock(self.db_repo)
219 h.flash(_('Unlocked repository'), category='success')
220 except Exception as e:
221 log.exception("Exception during unlocking")
222 h.flash(_('An error occurred during unlocking'), category='error')
223
224 raise HTTPFound(
225 h.route_path('edit_repo_advanced', repo_name=self.db_repo_name))
@@ -21,6 +21,11 b''
21 21
22 22 def includeme(config):
23 23
24 # Summary
25 config.add_route(
26 name='repo_summary_explicit',
27 pattern='/{repo_name:.*?[^/]}/summary', repo_route=True)
28
24 29 # Tags
25 30 config.add_route(
26 31 name='tags_home',
@@ -41,6 +46,23 b' def includeme(config):'
41 46 name='edit_repo',
42 47 pattern='/{repo_name:.*?[^/]}/settings', repo_route=True)
43 48
49 # Settings advanced
50 config.add_route(
51 name='edit_repo_advanced',
52 pattern='/{repo_name:.*?[^/]}/settings/advanced', repo_route=True)
53 config.add_route(
54 name='edit_repo_advanced_delete',
55 pattern='/{repo_name:.*?[^/]}/settings/advanced/delete', repo_route=True)
56 config.add_route(
57 name='edit_repo_advanced_locking',
58 pattern='/{repo_name:.*?[^/]}/settings/advanced/locking', repo_route=True)
59 config.add_route(
60 name='edit_repo_advanced_journal',
61 pattern='/{repo_name:.*?[^/]}/settings/advanced/journal', repo_route=True)
62 config.add_route(
63 name='edit_repo_advanced_fork',
64 pattern='/{repo_name:.*?[^/]}/settings/advanced/fork', repo_route=True)
65
44 66 # Caches
45 67 config.add_route(
46 68 name='edit_repo_caches',
@@ -77,5 +99,11 b' def includeme(config):'
77 99 config.add_route(
78 100 name='strip_execute',
79 101 pattern='/{repo_name:.*?[^/]}/settings/strip_execute', repo_route=True)
102
103 # NOTE(marcink): needs to be at the end for catch-all
104 # config.add_route(
105 # name='repo_summary',
106 # pattern='/{repo_name:.*?[^/]}', repo_route=True)
107
80 108 # Scan module for configuration decorators.
81 109 config.scan()
@@ -38,6 +38,7 b' def route_path(name, params=None, **kwar'
38 38
39 39 base_url = {
40 40 'edit_repo': '/{repo_name}/settings',
41 'edit_repo_advanced': '/{repo_name}/settings/advanced',
41 42 'edit_repo_caches': '/{repo_name}/settings/caches',
42 43 'edit_repo_perms': '/{repo_name}/settings/permissions',
43 44 }[name].format(**kwargs)
@@ -62,6 +63,7 b' class TestAdminRepoSettings(object):'
62 63 'edit_repo',
63 64 'edit_repo_caches',
64 65 'edit_repo_perms',
66 'edit_repo_advanced',
65 67 ])
66 68 def test_show_page(self, urlname, app, backend):
67 69 app.get(route_path(urlname, repo_name=backend.repo_name), status=200)
@@ -74,10 +76,9 b' class TestAdminRepoSettings(object):'
74 76 self.app.get(route_path('edit_repo', repo_name=backend_hg.repo_name))
75 77
76 78 @pytest.mark.parametrize('urlname', [
77 'edit_repo_advanced',
78 79 'repo_vcs_settings',
80 'repo_settings_issuetracker',
79 81 'edit_repo_fields',
80 'repo_settings_issuetracker',
81 82 'edit_repo_remote',
82 83 'edit_repo_statistics',
83 84 ])
@@ -185,7 +185,7 b' def make_map(config):'
185 185 #==========================================================================
186 186
187 187 # MAIN PAGE
188 rmap.connect('home', '/', controller='home', action='index', jsroute=True)
188 rmap.connect('home', '/', controller='home', action='index')
189 189
190 190 # ping and pylons error test
191 191 rmap.connect('ping', '%s/ping' % (ADMIN_PREFIX,), controller='home', action='ping')
@@ -674,30 +674,11 b' def make_map(config):'
674 674 conditions={'method': ['DELETE'], 'function': check_repo},
675 675 requirements=URL_NAME_REQUIREMENTS)
676 676
677 rmap.connect('edit_repo_advanced', '/{repo_name}/settings/advanced',
678 controller='admin/repos', action='edit_advanced',
679 conditions={'method': ['GET'], 'function': check_repo},
680 requirements=URL_NAME_REQUIREMENTS)
681
682 rmap.connect('edit_repo_advanced_locking', '/{repo_name}/settings/advanced/locking',
683 controller='admin/repos', action='edit_advanced_locking',
684 conditions={'method': ['PUT'], 'function': check_repo},
685 requirements=URL_NAME_REQUIREMENTS)
686 677 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
687 678 controller='admin/repos', action='toggle_locking',
688 679 conditions={'method': ['GET'], 'function': check_repo},
689 680 requirements=URL_NAME_REQUIREMENTS)
690 681
691 rmap.connect('edit_repo_advanced_journal', '/{repo_name}/settings/advanced/journal',
692 controller='admin/repos', action='edit_advanced_journal',
693 conditions={'method': ['PUT'], 'function': check_repo},
694 requirements=URL_NAME_REQUIREMENTS)
695
696 rmap.connect('edit_repo_advanced_fork', '/{repo_name}/settings/advanced/fork',
697 controller='admin/repos', action='edit_advanced_fork',
698 conditions={'method': ['PUT'], 'function': check_repo},
699 requirements=URL_NAME_REQUIREMENTS)
700
701 682 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
702 683 controller='admin/repos', action='edit_remote_form',
703 684 conditions={'method': ['GET'], 'function': check_repo},
@@ -288,51 +288,6 b' class ReposController(BaseRepoController'
288 288 return {'result': True}
289 289 return {'result': False}
290 290
291 @HasRepoPermissionAllDecorator('repository.admin')
292 @auth.CSRFRequired()
293 def delete(self, repo_name):
294 """
295 DELETE /repos/repo_name: Delete an existing item"""
296 # Forms posted to this method should contain a hidden field:
297 # <input type="hidden" name="_method" value="DELETE" />
298 # Or using helpers:
299 # h.form(url('repo', repo_name=ID),
300 # method='delete')
301 # url('repo', repo_name=ID)
302
303 repo_model = RepoModel()
304 repo = repo_model.get_by_repo_name(repo_name)
305 if not repo:
306 h.not_mapped_error(repo_name)
307 return redirect(url('repos'))
308 try:
309 _forks = repo.forks.count()
310 handle_forks = None
311 if _forks and request.POST.get('forks'):
312 do = request.POST['forks']
313 if do == 'detach_forks':
314 handle_forks = 'detach'
315 h.flash(_('Detached %s forks') % _forks, category='success')
316 elif do == 'delete_forks':
317 handle_forks = 'delete'
318 h.flash(_('Deleted %s forks') % _forks, category='success')
319 repo_model.delete(repo, forks=handle_forks)
320 action_logger(c.rhodecode_user, 'admin_deleted_repo',
321 repo_name, self.ip_addr, self.sa)
322 ScmModel().mark_for_invalidation(repo_name)
323 h.flash(_('Deleted repository %s') % repo_name, category='success')
324 Session().commit()
325 except AttachedForksError:
326 h.flash(_('Cannot delete %s it still contains attached forks')
327 % repo_name, category='warning')
328
329 except Exception:
330 log.exception("Exception during deletion of repository")
331 h.flash(_('An error occurred during deletion of %s') % repo_name,
332 category='error')
333
334 return redirect(url('repos'))
335
336 291 @HasPermissionAllDecorator('hg.admin')
337 292 def show(self, repo_name, format='html'):
338 293 """GET /repos/repo_name: Show a specific item"""
@@ -384,105 +339,6 b' class ReposController(BaseRepoController'
384 339 h.flash(msg, category='error')
385 340 return redirect(url('edit_repo_fields', repo_name=repo_name))
386 341
387 @HasRepoPermissionAllDecorator('repository.admin')
388 def edit_advanced(self, repo_name):
389 """GET /repo_name/settings: Form to edit an existing item"""
390 c.repo_info = self._load_repo(repo_name)
391 c.default_user_id = User.get_default_user().user_id
392 c.in_public_journal = UserFollowing.query()\
393 .filter(UserFollowing.user_id == c.default_user_id)\
394 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
395
396 c.active = 'advanced'
397 c.has_origin_repo_read_perm = False
398 if c.repo_info.fork:
399 c.has_origin_repo_read_perm = h.HasRepoPermissionAny(
400 'repository.write', 'repository.read', 'repository.admin')(
401 c.repo_info.fork.repo_name, 'repo set as fork page')
402
403 if request.POST:
404 return redirect(url('repo_edit_advanced'))
405 return render('admin/repos/repo_edit.mako')
406
407 @HasRepoPermissionAllDecorator('repository.admin')
408 @auth.CSRFRequired()
409 def edit_advanced_journal(self, repo_name):
410 """
411 Set's this repository to be visible in public journal,
412 in other words assing default user to follow this repo
413
414 :param repo_name:
415 """
416
417 try:
418 repo_id = Repository.get_by_repo_name(repo_name).repo_id
419 user_id = User.get_default_user().user_id
420 self.scm_model.toggle_following_repo(repo_id, user_id)
421 h.flash(_('Updated repository visibility in public journal'),
422 category='success')
423 Session().commit()
424 except Exception:
425 h.flash(_('An error occurred during setting this'
426 ' repository in public journal'),
427 category='error')
428
429 return redirect(url('edit_repo_advanced', repo_name=repo_name))
430
431 @HasRepoPermissionAllDecorator('repository.admin')
432 @auth.CSRFRequired()
433 def edit_advanced_fork(self, repo_name):
434 """
435 Mark given repository as a fork of another
436
437 :param repo_name:
438 """
439
440 new_fork_id = request.POST.get('id_fork_of')
441 try:
442
443 if new_fork_id and not new_fork_id.isdigit():
444 log.error('Given fork id %s is not an INT', new_fork_id)
445
446 fork_id = safe_int(new_fork_id)
447 repo = ScmModel().mark_as_fork(repo_name, fork_id,
448 c.rhodecode_user.username)
449 fork = repo.fork.repo_name if repo.fork else _('Nothing')
450 Session().commit()
451 h.flash(_('Marked repo %s as fork of %s') % (repo_name, fork),
452 category='success')
453 except RepositoryError as e:
454 log.exception("Repository Error occurred")
455 h.flash(str(e), category='error')
456 except Exception as e:
457 log.exception("Exception while editing fork")
458 h.flash(_('An error occurred during this operation'),
459 category='error')
460
461 return redirect(url('edit_repo_advanced', repo_name=repo_name))
462
463 @HasRepoPermissionAllDecorator('repository.admin')
464 @auth.CSRFRequired()
465 def edit_advanced_locking(self, repo_name):
466 """
467 Unlock repository when it is locked !
468
469 :param repo_name:
470 """
471 try:
472 repo = Repository.get_by_repo_name(repo_name)
473 if request.POST.get('set_lock'):
474 Repository.lock(repo, c.rhodecode_user.user_id,
475 lock_reason=Repository.LOCK_WEB)
476 h.flash(_('Locked repository'), category='success')
477 elif request.POST.get('set_unlock'):
478 Repository.unlock(repo)
479 h.flash(_('Unlocked repository'), category='success')
480 except Exception as e:
481 log.exception("Exception during unlocking")
482 h.flash(_('An error occurred during unlocking'),
483 category='error')
484 return redirect(url('edit_repo_advanced', repo_name=repo_name))
485
486 342 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
487 343 @auth.CSRFRequired()
488 344 def toggle_locking(self, repo_name):
@@ -36,9 +36,10 b' ACTIONS = {'
36 36 'user.push': {},
37 37 'user.pull': {},
38 38
39 'repo.add': {},
39 'repo.create': {},
40 40 'repo.edit': {},
41 41 'repo.edit.permissions': {},
42 'repo.delete': {},
42 43 'repo.commit.strip': {},
43 44 'repo.archive.download': {},
44 45 }
@@ -101,7 +102,7 b' def store('
101 102 audit_logger.store(
102 103 action='repo.edit', user=self._rhodecode_user)
103 104 audit_logger.store(
104 action='repo.delete',
105 action='repo.delete', action_data={'repo_data': repo_data},
105 106 user=audit_logger.UserWrap(username='itried-login', ip_addr='8.8.8.8'))
106 107
107 108 # repo action
@@ -96,10 +96,16 b' function registerRCRoutes() {'
96 96 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
97 97 pyroutes.register('repo_list_data', '/_repos', []);
98 98 pyroutes.register('goto_switcher_data', '/_goto_data', []);
99 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
99 100 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
100 101 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
101 102 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
102 103 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
104 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
105 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
106 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
107 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
108 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
103 109 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
104 110 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
105 111 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
@@ -45,7 +45,7 b''
45 45 <a href="${h.route_path('edit_repo_perms', repo_name=c.repo_name)}">${_('Permissions')}</a>
46 46 </li>
47 47 <li class="${'active' if c.active=='advanced' else ''}">
48 <a href="${h.url('edit_repo_advanced', repo_name=c.repo_name)}">${_('Advanced')}</a>
48 <a href="${h.route_path('edit_repo_advanced', repo_name=c.repo_name)}">${_('Advanced')}</a>
49 49 </li>
50 50 <li class="${'active' if c.active=='vcs' else ''}">
51 51 <a href="${h.url('repo_vcs_settings', repo_name=c.repo_name)}">${_('VCS')}</a>
@@ -10,8 +10,8 b''
10 10 %>
11 11
12 12 <div class="panel panel-default">
13 <div class="panel-heading">
14 <h3 class="panel-title">${_('Repository: %s') % c.repo_info.repo_name}</h3>
13 <div class="panel-heading" id="advanced-info" >
14 <h3 class="panel-title">${_('Repository: %s') % c.repo_info.repo_name} <a class="permalink" href="#advanced-info"></a></h3>
15 15 </div>
16 16 <div class="panel-body">
17 17 ${base.dt_info_panel(elems)}
@@ -20,11 +20,11 b''
20 20
21 21
22 22 <div class="panel panel-default">
23 <div class="panel-heading">
24 <h3 class="panel-title">${_('Fork Reference')}</h3>
23 <div class="panel-heading" id="advanced-fork">
24 <h3 class="panel-title">${_('Fork Reference')} <a class="permalink" href="#advanced-fork"></a></h3>
25 25 </div>
26 26 <div class="panel-body">
27 ${h.secure_form(url('edit_repo_advanced_fork', repo_name=c.repo_info.repo_name), method='put')}
27 ${h.secure_form(h.route_path('edit_repo_advanced_fork', repo_name=c.repo_info.repo_name), method='POST')}
28 28
29 29 % if c.repo_info.fork:
30 30 <div class="panel-body-title-text">${h.literal(_('This repository is a fork of %(repo_link)s') % {'repo_link': h.link_to_if(c.has_origin_repo_read_perm,c.repo_info.fork.repo_name, h.url('summary_home', repo_name=c.repo_info.fork.repo_name))})}
@@ -44,15 +44,14 b''
44 44
45 45
46 46 <div class="panel panel-default">
47 <div class="panel-heading">
48 <h3 class="panel-title">${_('Public Journal Visibility')}</h3>
47 <div class="panel-heading" id="advanced-journal">
48 <h3 class="panel-title">${_('Public Journal Visibility')} <a class="permalink" href="#advanced-journal"></a></h3>
49 49 </div>
50 50 <div class="panel-body">
51 ${h.secure_form(url('edit_repo_advanced_journal', repo_name=c.repo_info.repo_name), method='put')}
51 ${h.secure_form(h.route_path('edit_repo_advanced_journal', repo_name=c.repo_info.repo_name), method='POST')}
52 52 <div class="field">
53 53 %if c.in_public_journal:
54 54 <button class="btn btn-small" type="submit">
55 <i class="icon-minus"></i>
56 55 ${_('Remove from Public Journal')}
57 56 </button>
58 57 %else:
@@ -70,11 +69,11 b''
70 69
71 70
72 71 <div class="panel panel-default">
73 <div class="panel-heading">
74 <h3 class="panel-title">${_('Locking state')}</h3>
72 <div class="panel-heading" id="advanced-locking">
73 <h3 class="panel-title">${_('Locking state')} <a class="permalink" href="#advanced-locking"></a></h3>
75 74 </div>
76 75 <div class="panel-body">
77 ${h.secure_form(url('edit_repo_advanced_locking', repo_name=c.repo_info.repo_name), method='put')}
76 ${h.secure_form(h.route_path('edit_repo_advanced_locking', repo_name=c.repo_info.repo_name), method='POST')}
78 77
79 78 %if c.repo_info.locked[0]:
80 79 <div class="panel-body-title-text">${'Locked by %s on %s. Lock reason: %s' % (h.person_by_id(c.repo_info.locked[0]),
@@ -110,15 +109,15 b''
110 109 </div>
111 110
112 111 <div class="panel panel-danger">
113 <div class="panel-heading">
114 <h3 class="panel-title">${_('Delete repository')}</h3>
112 <div class="panel-heading" id="advanced-delete">
113 <h3 class="panel-title">${_('Delete repository')} <a class="permalink" href="#advanced-delete"></a></h3>
115 114 </div>
116 115 <div class="panel-body">
117 ${h.secure_form(url('repo', repo_name=c.repo_name),method='delete')}
116 ${h.secure_form(h.route_path('edit_repo_advanced_delete', repo_name=c.repo_name), method='POST')}
118 117 <table class="display">
119 118 <tr>
120 119 <td>
121 ${ungettext('This repository has %s fork.', 'This repository has %s forks.', c.repo_info. forks.count()) % c.repo_info.forks.count()}
120 ${_ungettext('This repository has %s fork.', 'This repository has %s forks.', c.repo_info.forks.count()) % c.repo_info.forks.count()}
122 121 </td>
123 122 <td>
124 123 %if c.repo_info.forks.count():
@@ -143,7 +142,7 b''
143 142 </div>
144 143 <div class="field">
145 144 <span class="help-block">
146 ${_('This repository will be renamed in a special way in order to make it inaccessible to RhodeCode Enterprise and its VCS systems. If you need to fully delete it from the file system, please do it manually, or with rhodecode-cleanup-repos command.')}
145 ${_('This repository will be renamed in a special way in order to make it inaccessible to RhodeCode Enterprise and its VCS systems. If you need to fully delete it from the file system, please do it manually, or with rhodecode-cleanup-repos command available in rhodecode-tools.')}
147 146 </span>
148 147 </div>
149 148
@@ -119,7 +119,7 b''
119 119 <i class="icon-pencil"></i>Edit</a>
120 120 </div>
121 121 <div class="grid_delete">
122 ${h.secure_form(h.url('repo', repo_name=repo_name),method='delete')}
122 ${h.secure_form(h.route_path('edit_repo_advanced_delete', repo_name=repo_name), method='POST')}
123 123 ${h.submit('remove_%s' % repo_name,_('Delete'),class_="btn btn-link btn-danger",
124 124 onclick="return confirm('"+_('Confirm to delete this repository: %s') % repo_name+"');")}
125 125 ${h.end_form()}
@@ -382,21 +382,6 b' class TestAdminRepos(object):'
382 382 csrf_token=csrf_token))
383 383 response.mustcontain('Repository name cannot end with .git')
384 384
385 @pytest.mark.parametrize("suffix", [u'', u'ąęł'], ids=['', 'non-ascii'])
386 def test_delete(self, autologin_user, backend, suffix, csrf_token):
387 repo = backend.create_repo(name_suffix=suffix)
388 repo_name = repo.repo_name
389
390 response = self.app.post(url('repo', repo_name=repo_name),
391 params={'_method': 'delete',
392 'csrf_token': csrf_token})
393 assert_session_flash(response, 'Deleted repository %s' % (repo_name))
394 response.follow()
395
396 # check if repo was deleted from db
397 assert RepoModel().get_by_repo_name(repo_name) is None
398 assert not repo_on_filesystem(repo_name)
399
400 385 def test_show(self, autologin_user, backend):
401 386 self.app.get(url('repo', repo_name=backend.repo_name))
402 387
@@ -414,82 +399,6 b' class TestAdminRepos(object):'
414 399 assert permissions[0].permission.permission_name == 'repository.none'
415 400 assert permissions[0].repository.private is True
416 401
417 def test_set_repo_fork_has_no_self_id(self, autologin_user, backend):
418 repo = backend.repo
419 response = self.app.get(
420 url('edit_repo_advanced', repo_name=backend.repo_name))
421 opt = """<option value="%s">vcs_test_git</option>""" % repo.repo_id
422 response.mustcontain(no=[opt])
423
424 def test_set_fork_of_target_repo(
425 self, autologin_user, backend, csrf_token):
426 target_repo = 'target_%s' % backend.alias
427 fixture.create_repo(target_repo, repo_type=backend.alias)
428 repo2 = Repository.get_by_repo_name(target_repo)
429 response = self.app.post(
430 url('edit_repo_advanced_fork', repo_name=backend.repo_name),
431 params={'id_fork_of': repo2.repo_id, '_method': 'put',
432 'csrf_token': csrf_token})
433 repo = Repository.get_by_repo_name(backend.repo_name)
434 repo2 = Repository.get_by_repo_name(target_repo)
435 assert_session_flash(
436 response,
437 'Marked repo %s as fork of %s' % (repo.repo_name, repo2.repo_name))
438
439 assert repo.fork == repo2
440 response = response.follow()
441 # check if given repo is selected
442
443 opt = 'This repository is a fork of <a href="%s">%s</a>' % (
444 url('summary_home', repo_name=repo2.repo_name), repo2.repo_name)
445
446 response.mustcontain(opt)
447
448 fixture.destroy_repo(target_repo, forks='detach')
449
450 @pytest.mark.backends("hg", "git")
451 def test_set_fork_of_other_type_repo(self, autologin_user, backend,
452 csrf_token):
453 TARGET_REPO_MAP = {
454 'git': {
455 'type': 'hg',
456 'repo_name': HG_REPO},
457 'hg': {
458 'type': 'git',
459 'repo_name': GIT_REPO},
460 }
461 target_repo = TARGET_REPO_MAP[backend.alias]
462
463 repo2 = Repository.get_by_repo_name(target_repo['repo_name'])
464 response = self.app.post(
465 url('edit_repo_advanced_fork', repo_name=backend.repo_name),
466 params={'id_fork_of': repo2.repo_id, '_method': 'put',
467 'csrf_token': csrf_token})
468 assert_session_flash(
469 response,
470 'Cannot set repository as fork of repository with other type')
471
472 def test_set_fork_of_none(self, autologin_user, backend, csrf_token):
473 # mark it as None
474 response = self.app.post(
475 url('edit_repo_advanced_fork', repo_name=backend.repo_name),
476 params={'id_fork_of': None, '_method': 'put',
477 'csrf_token': csrf_token})
478 assert_session_flash(
479 response,
480 'Marked repo %s as fork of %s'
481 % (backend.repo_name, "Nothing"))
482 assert backend.repo.fork is None
483
484 def test_set_fork_of_same_repo(self, autologin_user, backend, csrf_token):
485 repo = Repository.get_by_repo_name(backend.repo_name)
486 response = self.app.post(
487 url('edit_repo_advanced_fork', repo_name=backend.repo_name),
488 params={'id_fork_of': repo.repo_id, '_method': 'put',
489 'csrf_token': csrf_token})
490 assert_session_flash(
491 response, 'An error occurred during this operation')
492
493 402 def test_create_on_top_level_without_permissions(self, backend):
494 403 session = login_user_session(
495 404 self.app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
@@ -106,9 +106,7 b' class _BaseTest(TestController):'
106 106 )
107 107
108 108 # remove this fork
109 response = self.app.post(
110 url('repo', repo_name=fork_name),
111 params={'_method': 'delete', 'csrf_token': self.csrf_token})
109 fixture.destroy_repo(fork_name)
112 110
113 111 def test_fork_create_into_group(self):
114 112 self.log_user()
General Comments 0
You need to be logged in to leave comments. Login now