##// 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 def includeme(config):
22 def includeme(config):
23
23
24 # Summary
25 config.add_route(
26 name='repo_summary_explicit',
27 pattern='/{repo_name:.*?[^/]}/summary', repo_route=True)
28
24 # Tags
29 # Tags
25 config.add_route(
30 config.add_route(
26 name='tags_home',
31 name='tags_home',
@@ -41,6 +46,23 b' def includeme(config):'
41 name='edit_repo',
46 name='edit_repo',
42 pattern='/{repo_name:.*?[^/]}/settings', repo_route=True)
47 pattern='/{repo_name:.*?[^/]}/settings', repo_route=True)
43
48
49 # Settings advanced
50 config.add_route(
51 name='edit_repo_advanced',
52 pattern='/{repo_name:.*?[^/]}/settings/advanced', repo_route=True)
53 config.add_route(
54 name='edit_repo_advanced_delete',
55 pattern='/{repo_name:.*?[^/]}/settings/advanced/delete', repo_route=True)
56 config.add_route(
57 name='edit_repo_advanced_locking',
58 pattern='/{repo_name:.*?[^/]}/settings/advanced/locking', repo_route=True)
59 config.add_route(
60 name='edit_repo_advanced_journal',
61 pattern='/{repo_name:.*?[^/]}/settings/advanced/journal', repo_route=True)
62 config.add_route(
63 name='edit_repo_advanced_fork',
64 pattern='/{repo_name:.*?[^/]}/settings/advanced/fork', repo_route=True)
65
44 # Caches
66 # Caches
45 config.add_route(
67 config.add_route(
46 name='edit_repo_caches',
68 name='edit_repo_caches',
@@ -77,5 +99,11 b' def includeme(config):'
77 config.add_route(
99 config.add_route(
78 name='strip_execute',
100 name='strip_execute',
79 pattern='/{repo_name:.*?[^/]}/settings/strip_execute', repo_route=True)
101 pattern='/{repo_name:.*?[^/]}/settings/strip_execute', repo_route=True)
102
103 # NOTE(marcink): needs to be at the end for catch-all
104 # config.add_route(
105 # name='repo_summary',
106 # pattern='/{repo_name:.*?[^/]}', repo_route=True)
107
80 # Scan module for configuration decorators.
108 # Scan module for configuration decorators.
81 config.scan()
109 config.scan()
@@ -38,6 +38,7 b' def route_path(name, params=None, **kwar'
38
38
39 base_url = {
39 base_url = {
40 'edit_repo': '/{repo_name}/settings',
40 'edit_repo': '/{repo_name}/settings',
41 'edit_repo_advanced': '/{repo_name}/settings/advanced',
41 'edit_repo_caches': '/{repo_name}/settings/caches',
42 'edit_repo_caches': '/{repo_name}/settings/caches',
42 'edit_repo_perms': '/{repo_name}/settings/permissions',
43 'edit_repo_perms': '/{repo_name}/settings/permissions',
43 }[name].format(**kwargs)
44 }[name].format(**kwargs)
@@ -62,6 +63,7 b' class TestAdminRepoSettings(object):'
62 'edit_repo',
63 'edit_repo',
63 'edit_repo_caches',
64 'edit_repo_caches',
64 'edit_repo_perms',
65 'edit_repo_perms',
66 'edit_repo_advanced',
65 ])
67 ])
66 def test_show_page(self, urlname, app, backend):
68 def test_show_page(self, urlname, app, backend):
67 app.get(route_path(urlname, repo_name=backend.repo_name), status=200)
69 app.get(route_path(urlname, repo_name=backend.repo_name), status=200)
@@ -74,10 +76,9 b' class TestAdminRepoSettings(object):'
74 self.app.get(route_path('edit_repo', repo_name=backend_hg.repo_name))
76 self.app.get(route_path('edit_repo', repo_name=backend_hg.repo_name))
75
77
76 @pytest.mark.parametrize('urlname', [
78 @pytest.mark.parametrize('urlname', [
77 'edit_repo_advanced',
78 'repo_vcs_settings',
79 'repo_vcs_settings',
80 'repo_settings_issuetracker',
79 'edit_repo_fields',
81 'edit_repo_fields',
80 'repo_settings_issuetracker',
81 'edit_repo_remote',
82 'edit_repo_remote',
82 'edit_repo_statistics',
83 'edit_repo_statistics',
83 ])
84 ])
@@ -185,7 +185,7 b' def make_map(config):'
185 #==========================================================================
185 #==========================================================================
186
186
187 # MAIN PAGE
187 # MAIN PAGE
188 rmap.connect('home', '/', controller='home', action='index', jsroute=True)
188 rmap.connect('home', '/', controller='home', action='index')
189
189
190 # ping and pylons error test
190 # ping and pylons error test
191 rmap.connect('ping', '%s/ping' % (ADMIN_PREFIX,), controller='home', action='ping')
191 rmap.connect('ping', '%s/ping' % (ADMIN_PREFIX,), controller='home', action='ping')
@@ -674,30 +674,11 b' def make_map(config):'
674 conditions={'method': ['DELETE'], 'function': check_repo},
674 conditions={'method': ['DELETE'], 'function': check_repo},
675 requirements=URL_NAME_REQUIREMENTS)
675 requirements=URL_NAME_REQUIREMENTS)
676
676
677 rmap.connect('edit_repo_advanced', '/{repo_name}/settings/advanced',
678 controller='admin/repos', action='edit_advanced',
679 conditions={'method': ['GET'], 'function': check_repo},
680 requirements=URL_NAME_REQUIREMENTS)
681
682 rmap.connect('edit_repo_advanced_locking', '/{repo_name}/settings/advanced/locking',
683 controller='admin/repos', action='edit_advanced_locking',
684 conditions={'method': ['PUT'], 'function': check_repo},
685 requirements=URL_NAME_REQUIREMENTS)
686 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
677 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
687 controller='admin/repos', action='toggle_locking',
678 controller='admin/repos', action='toggle_locking',
688 conditions={'method': ['GET'], 'function': check_repo},
679 conditions={'method': ['GET'], 'function': check_repo},
689 requirements=URL_NAME_REQUIREMENTS)
680 requirements=URL_NAME_REQUIREMENTS)
690
681
691 rmap.connect('edit_repo_advanced_journal', '/{repo_name}/settings/advanced/journal',
692 controller='admin/repos', action='edit_advanced_journal',
693 conditions={'method': ['PUT'], 'function': check_repo},
694 requirements=URL_NAME_REQUIREMENTS)
695
696 rmap.connect('edit_repo_advanced_fork', '/{repo_name}/settings/advanced/fork',
697 controller='admin/repos', action='edit_advanced_fork',
698 conditions={'method': ['PUT'], 'function': check_repo},
699 requirements=URL_NAME_REQUIREMENTS)
700
701 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
682 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
702 controller='admin/repos', action='edit_remote_form',
683 controller='admin/repos', action='edit_remote_form',
703 conditions={'method': ['GET'], 'function': check_repo},
684 conditions={'method': ['GET'], 'function': check_repo},
@@ -288,51 +288,6 b' class ReposController(BaseRepoController'
288 return {'result': True}
288 return {'result': True}
289 return {'result': False}
289 return {'result': False}
290
290
291 @HasRepoPermissionAllDecorator('repository.admin')
292 @auth.CSRFRequired()
293 def delete(self, repo_name):
294 """
295 DELETE /repos/repo_name: Delete an existing item"""
296 # Forms posted to this method should contain a hidden field:
297 # <input type="hidden" name="_method" value="DELETE" />
298 # Or using helpers:
299 # h.form(url('repo', repo_name=ID),
300 # method='delete')
301 # url('repo', repo_name=ID)
302
303 repo_model = RepoModel()
304 repo = repo_model.get_by_repo_name(repo_name)
305 if not repo:
306 h.not_mapped_error(repo_name)
307 return redirect(url('repos'))
308 try:
309 _forks = repo.forks.count()
310 handle_forks = None
311 if _forks and request.POST.get('forks'):
312 do = request.POST['forks']
313 if do == 'detach_forks':
314 handle_forks = 'detach'
315 h.flash(_('Detached %s forks') % _forks, category='success')
316 elif do == 'delete_forks':
317 handle_forks = 'delete'
318 h.flash(_('Deleted %s forks') % _forks, category='success')
319 repo_model.delete(repo, forks=handle_forks)
320 action_logger(c.rhodecode_user, 'admin_deleted_repo',
321 repo_name, self.ip_addr, self.sa)
322 ScmModel().mark_for_invalidation(repo_name)
323 h.flash(_('Deleted repository %s') % repo_name, category='success')
324 Session().commit()
325 except AttachedForksError:
326 h.flash(_('Cannot delete %s it still contains attached forks')
327 % repo_name, category='warning')
328
329 except Exception:
330 log.exception("Exception during deletion of repository")
331 h.flash(_('An error occurred during deletion of %s') % repo_name,
332 category='error')
333
334 return redirect(url('repos'))
335
336 @HasPermissionAllDecorator('hg.admin')
291 @HasPermissionAllDecorator('hg.admin')
337 def show(self, repo_name, format='html'):
292 def show(self, repo_name, format='html'):
338 """GET /repos/repo_name: Show a specific item"""
293 """GET /repos/repo_name: Show a specific item"""
@@ -384,105 +339,6 b' class ReposController(BaseRepoController'
384 h.flash(msg, category='error')
339 h.flash(msg, category='error')
385 return redirect(url('edit_repo_fields', repo_name=repo_name))
340 return redirect(url('edit_repo_fields', repo_name=repo_name))
386
341
387 @HasRepoPermissionAllDecorator('repository.admin')
388 def edit_advanced(self, repo_name):
389 """GET /repo_name/settings: Form to edit an existing item"""
390 c.repo_info = self._load_repo(repo_name)
391 c.default_user_id = User.get_default_user().user_id
392 c.in_public_journal = UserFollowing.query()\
393 .filter(UserFollowing.user_id == c.default_user_id)\
394 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
395
396 c.active = 'advanced'
397 c.has_origin_repo_read_perm = False
398 if c.repo_info.fork:
399 c.has_origin_repo_read_perm = h.HasRepoPermissionAny(
400 'repository.write', 'repository.read', 'repository.admin')(
401 c.repo_info.fork.repo_name, 'repo set as fork page')
402
403 if request.POST:
404 return redirect(url('repo_edit_advanced'))
405 return render('admin/repos/repo_edit.mako')
406
407 @HasRepoPermissionAllDecorator('repository.admin')
408 @auth.CSRFRequired()
409 def edit_advanced_journal(self, repo_name):
410 """
411 Set's this repository to be visible in public journal,
412 in other words assing default user to follow this repo
413
414 :param repo_name:
415 """
416
417 try:
418 repo_id = Repository.get_by_repo_name(repo_name).repo_id
419 user_id = User.get_default_user().user_id
420 self.scm_model.toggle_following_repo(repo_id, user_id)
421 h.flash(_('Updated repository visibility in public journal'),
422 category='success')
423 Session().commit()
424 except Exception:
425 h.flash(_('An error occurred during setting this'
426 ' repository in public journal'),
427 category='error')
428
429 return redirect(url('edit_repo_advanced', repo_name=repo_name))
430
431 @HasRepoPermissionAllDecorator('repository.admin')
432 @auth.CSRFRequired()
433 def edit_advanced_fork(self, repo_name):
434 """
435 Mark given repository as a fork of another
436
437 :param repo_name:
438 """
439
440 new_fork_id = request.POST.get('id_fork_of')
441 try:
442
443 if new_fork_id and not new_fork_id.isdigit():
444 log.error('Given fork id %s is not an INT', new_fork_id)
445
446 fork_id = safe_int(new_fork_id)
447 repo = ScmModel().mark_as_fork(repo_name, fork_id,
448 c.rhodecode_user.username)
449 fork = repo.fork.repo_name if repo.fork else _('Nothing')
450 Session().commit()
451 h.flash(_('Marked repo %s as fork of %s') % (repo_name, fork),
452 category='success')
453 except RepositoryError as e:
454 log.exception("Repository Error occurred")
455 h.flash(str(e), category='error')
456 except Exception as e:
457 log.exception("Exception while editing fork")
458 h.flash(_('An error occurred during this operation'),
459 category='error')
460
461 return redirect(url('edit_repo_advanced', repo_name=repo_name))
462
463 @HasRepoPermissionAllDecorator('repository.admin')
464 @auth.CSRFRequired()
465 def edit_advanced_locking(self, repo_name):
466 """
467 Unlock repository when it is locked !
468
469 :param repo_name:
470 """
471 try:
472 repo = Repository.get_by_repo_name(repo_name)
473 if request.POST.get('set_lock'):
474 Repository.lock(repo, c.rhodecode_user.user_id,
475 lock_reason=Repository.LOCK_WEB)
476 h.flash(_('Locked repository'), category='success')
477 elif request.POST.get('set_unlock'):
478 Repository.unlock(repo)
479 h.flash(_('Unlocked repository'), category='success')
480 except Exception as e:
481 log.exception("Exception during unlocking")
482 h.flash(_('An error occurred during unlocking'),
483 category='error')
484 return redirect(url('edit_repo_advanced', repo_name=repo_name))
485
486 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
342 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
487 @auth.CSRFRequired()
343 @auth.CSRFRequired()
488 def toggle_locking(self, repo_name):
344 def toggle_locking(self, repo_name):
@@ -36,9 +36,10 b' ACTIONS = {'
36 'user.push': {},
36 'user.push': {},
37 'user.pull': {},
37 'user.pull': {},
38
38
39 'repo.add': {},
39 'repo.create': {},
40 'repo.edit': {},
40 'repo.edit': {},
41 'repo.edit.permissions': {},
41 'repo.edit.permissions': {},
42 'repo.delete': {},
42 'repo.commit.strip': {},
43 'repo.commit.strip': {},
43 'repo.archive.download': {},
44 'repo.archive.download': {},
44 }
45 }
@@ -101,7 +102,7 b' def store('
101 audit_logger.store(
102 audit_logger.store(
102 action='repo.edit', user=self._rhodecode_user)
103 action='repo.edit', user=self._rhodecode_user)
103 audit_logger.store(
104 audit_logger.store(
104 action='repo.delete',
105 action='repo.delete', action_data={'repo_data': repo_data},
105 user=audit_logger.UserWrap(username='itried-login', ip_addr='8.8.8.8'))
106 user=audit_logger.UserWrap(username='itried-login', ip_addr='8.8.8.8'))
106
107
107 # repo action
108 # repo action
@@ -96,10 +96,16 b' function registerRCRoutes() {'
96 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
96 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
97 pyroutes.register('repo_list_data', '/_repos', []);
97 pyroutes.register('repo_list_data', '/_repos', []);
98 pyroutes.register('goto_switcher_data', '/_goto_data', []);
98 pyroutes.register('goto_switcher_data', '/_goto_data', []);
99 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
99 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
100 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
100 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
101 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
101 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
102 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
102 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
103 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
104 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
105 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
106 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
107 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
108 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
103 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
109 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
104 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
110 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
105 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
111 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
@@ -45,7 +45,7 b''
45 <a href="${h.route_path('edit_repo_perms', repo_name=c.repo_name)}">${_('Permissions')}</a>
45 <a href="${h.route_path('edit_repo_perms', repo_name=c.repo_name)}">${_('Permissions')}</a>
46 </li>
46 </li>
47 <li class="${'active' if c.active=='advanced' else ''}">
47 <li class="${'active' if c.active=='advanced' else ''}">
48 <a href="${h.url('edit_repo_advanced', repo_name=c.repo_name)}">${_('Advanced')}</a>
48 <a href="${h.route_path('edit_repo_advanced', repo_name=c.repo_name)}">${_('Advanced')}</a>
49 </li>
49 </li>
50 <li class="${'active' if c.active=='vcs' else ''}">
50 <li class="${'active' if c.active=='vcs' else ''}">
51 <a href="${h.url('repo_vcs_settings', repo_name=c.repo_name)}">${_('VCS')}</a>
51 <a href="${h.url('repo_vcs_settings', repo_name=c.repo_name)}">${_('VCS')}</a>
@@ -10,8 +10,8 b''
10 %>
10 %>
11
11
12 <div class="panel panel-default">
12 <div class="panel panel-default">
13 <div class="panel-heading">
13 <div class="panel-heading" id="advanced-info" >
14 <h3 class="panel-title">${_('Repository: %s') % c.repo_info.repo_name}</h3>
14 <h3 class="panel-title">${_('Repository: %s') % c.repo_info.repo_name} <a class="permalink" href="#advanced-info"></a></h3>
15 </div>
15 </div>
16 <div class="panel-body">
16 <div class="panel-body">
17 ${base.dt_info_panel(elems)}
17 ${base.dt_info_panel(elems)}
@@ -20,11 +20,11 b''
20
20
21
21
22 <div class="panel panel-default">
22 <div class="panel panel-default">
23 <div class="panel-heading">
23 <div class="panel-heading" id="advanced-fork">
24 <h3 class="panel-title">${_('Fork Reference')}</h3>
24 <h3 class="panel-title">${_('Fork Reference')} <a class="permalink" href="#advanced-fork"></a></h3>
25 </div>
25 </div>
26 <div class="panel-body">
26 <div class="panel-body">
27 ${h.secure_form(url('edit_repo_advanced_fork', repo_name=c.repo_info.repo_name), method='put')}
27 ${h.secure_form(h.route_path('edit_repo_advanced_fork', repo_name=c.repo_info.repo_name), method='POST')}
28
28
29 % if c.repo_info.fork:
29 % if c.repo_info.fork:
30 <div class="panel-body-title-text">${h.literal(_('This repository is a fork of %(repo_link)s') % {'repo_link': h.link_to_if(c.has_origin_repo_read_perm,c.repo_info.fork.repo_name, h.url('summary_home', repo_name=c.repo_info.fork.repo_name))})}
30 <div class="panel-body-title-text">${h.literal(_('This repository is a fork of %(repo_link)s') % {'repo_link': h.link_to_if(c.has_origin_repo_read_perm,c.repo_info.fork.repo_name, h.url('summary_home', repo_name=c.repo_info.fork.repo_name))})}
@@ -44,15 +44,14 b''
44
44
45
45
46 <div class="panel panel-default">
46 <div class="panel panel-default">
47 <div class="panel-heading">
47 <div class="panel-heading" id="advanced-journal">
48 <h3 class="panel-title">${_('Public Journal Visibility')}</h3>
48 <h3 class="panel-title">${_('Public Journal Visibility')} <a class="permalink" href="#advanced-journal"></a></h3>
49 </div>
49 </div>
50 <div class="panel-body">
50 <div class="panel-body">
51 ${h.secure_form(url('edit_repo_advanced_journal', repo_name=c.repo_info.repo_name), method='put')}
51 ${h.secure_form(h.route_path('edit_repo_advanced_journal', repo_name=c.repo_info.repo_name), method='POST')}
52 <div class="field">
52 <div class="field">
53 %if c.in_public_journal:
53 %if c.in_public_journal:
54 <button class="btn btn-small" type="submit">
54 <button class="btn btn-small" type="submit">
55 <i class="icon-minus"></i>
56 ${_('Remove from Public Journal')}
55 ${_('Remove from Public Journal')}
57 </button>
56 </button>
58 %else:
57 %else:
@@ -70,11 +69,11 b''
70
69
71
70
72 <div class="panel panel-default">
71 <div class="panel panel-default">
73 <div class="panel-heading">
72 <div class="panel-heading" id="advanced-locking">
74 <h3 class="panel-title">${_('Locking state')}</h3>
73 <h3 class="panel-title">${_('Locking state')} <a class="permalink" href="#advanced-locking"></a></h3>
75 </div>
74 </div>
76 <div class="panel-body">
75 <div class="panel-body">
77 ${h.secure_form(url('edit_repo_advanced_locking', repo_name=c.repo_info.repo_name), method='put')}
76 ${h.secure_form(h.route_path('edit_repo_advanced_locking', repo_name=c.repo_info.repo_name), method='POST')}
78
77
79 %if c.repo_info.locked[0]:
78 %if c.repo_info.locked[0]:
80 <div class="panel-body-title-text">${'Locked by %s on %s. Lock reason: %s' % (h.person_by_id(c.repo_info.locked[0]),
79 <div class="panel-body-title-text">${'Locked by %s on %s. Lock reason: %s' % (h.person_by_id(c.repo_info.locked[0]),
@@ -110,15 +109,15 b''
110 </div>
109 </div>
111
110
112 <div class="panel panel-danger">
111 <div class="panel panel-danger">
113 <div class="panel-heading">
112 <div class="panel-heading" id="advanced-delete">
114 <h3 class="panel-title">${_('Delete repository')}</h3>
113 <h3 class="panel-title">${_('Delete repository')} <a class="permalink" href="#advanced-delete"></a></h3>
115 </div>
114 </div>
116 <div class="panel-body">
115 <div class="panel-body">
117 ${h.secure_form(url('repo', repo_name=c.repo_name),method='delete')}
116 ${h.secure_form(h.route_path('edit_repo_advanced_delete', repo_name=c.repo_name), method='POST')}
118 <table class="display">
117 <table class="display">
119 <tr>
118 <tr>
120 <td>
119 <td>
121 ${ungettext('This repository has %s fork.', 'This repository has %s forks.', c.repo_info. forks.count()) % c.repo_info.forks.count()}
120 ${_ungettext('This repository has %s fork.', 'This repository has %s forks.', c.repo_info.forks.count()) % c.repo_info.forks.count()}
122 </td>
121 </td>
123 <td>
122 <td>
124 %if c.repo_info.forks.count():
123 %if c.repo_info.forks.count():
@@ -143,7 +142,7 b''
143 </div>
142 </div>
144 <div class="field">
143 <div class="field">
145 <span class="help-block">
144 <span class="help-block">
146 ${_('This repository will be renamed in a special way in order to make it inaccessible to RhodeCode Enterprise and its VCS systems. If you need to fully delete it from the file system, please do it manually, or with rhodecode-cleanup-repos command.')}
145 ${_('This repository will be renamed in a special way in order to make it inaccessible to RhodeCode Enterprise and its VCS systems. If you need to fully delete it from the file system, please do it manually, or with rhodecode-cleanup-repos command available in rhodecode-tools.')}
147 </span>
146 </span>
148 </div>
147 </div>
149
148
@@ -119,7 +119,7 b''
119 <i class="icon-pencil"></i>Edit</a>
119 <i class="icon-pencil"></i>Edit</a>
120 </div>
120 </div>
121 <div class="grid_delete">
121 <div class="grid_delete">
122 ${h.secure_form(h.url('repo', repo_name=repo_name),method='delete')}
122 ${h.secure_form(h.route_path('edit_repo_advanced_delete', repo_name=repo_name), method='POST')}
123 ${h.submit('remove_%s' % repo_name,_('Delete'),class_="btn btn-link btn-danger",
123 ${h.submit('remove_%s' % repo_name,_('Delete'),class_="btn btn-link btn-danger",
124 onclick="return confirm('"+_('Confirm to delete this repository: %s') % repo_name+"');")}
124 onclick="return confirm('"+_('Confirm to delete this repository: %s') % repo_name+"');")}
125 ${h.end_form()}
125 ${h.end_form()}
@@ -382,21 +382,6 b' class TestAdminRepos(object):'
382 csrf_token=csrf_token))
382 csrf_token=csrf_token))
383 response.mustcontain('Repository name cannot end with .git')
383 response.mustcontain('Repository name cannot end with .git')
384
384
385 @pytest.mark.parametrize("suffix", [u'', u'ąęł'], ids=['', 'non-ascii'])
386 def test_delete(self, autologin_user, backend, suffix, csrf_token):
387 repo = backend.create_repo(name_suffix=suffix)
388 repo_name = repo.repo_name
389
390 response = self.app.post(url('repo', repo_name=repo_name),
391 params={'_method': 'delete',
392 'csrf_token': csrf_token})
393 assert_session_flash(response, 'Deleted repository %s' % (repo_name))
394 response.follow()
395
396 # check if repo was deleted from db
397 assert RepoModel().get_by_repo_name(repo_name) is None
398 assert not repo_on_filesystem(repo_name)
399
400 def test_show(self, autologin_user, backend):
385 def test_show(self, autologin_user, backend):
401 self.app.get(url('repo', repo_name=backend.repo_name))
386 self.app.get(url('repo', repo_name=backend.repo_name))
402
387
@@ -414,82 +399,6 b' class TestAdminRepos(object):'
414 assert permissions[0].permission.permission_name == 'repository.none'
399 assert permissions[0].permission.permission_name == 'repository.none'
415 assert permissions[0].repository.private is True
400 assert permissions[0].repository.private is True
416
401
417 def test_set_repo_fork_has_no_self_id(self, autologin_user, backend):
418 repo = backend.repo
419 response = self.app.get(
420 url('edit_repo_advanced', repo_name=backend.repo_name))
421 opt = """<option value="%s">vcs_test_git</option>""" % repo.repo_id
422 response.mustcontain(no=[opt])
423
424 def test_set_fork_of_target_repo(
425 self, autologin_user, backend, csrf_token):
426 target_repo = 'target_%s' % backend.alias
427 fixture.create_repo(target_repo, repo_type=backend.alias)
428 repo2 = Repository.get_by_repo_name(target_repo)
429 response = self.app.post(
430 url('edit_repo_advanced_fork', repo_name=backend.repo_name),
431 params={'id_fork_of': repo2.repo_id, '_method': 'put',
432 'csrf_token': csrf_token})
433 repo = Repository.get_by_repo_name(backend.repo_name)
434 repo2 = Repository.get_by_repo_name(target_repo)
435 assert_session_flash(
436 response,
437 'Marked repo %s as fork of %s' % (repo.repo_name, repo2.repo_name))
438
439 assert repo.fork == repo2
440 response = response.follow()
441 # check if given repo is selected
442
443 opt = 'This repository is a fork of <a href="%s">%s</a>' % (
444 url('summary_home', repo_name=repo2.repo_name), repo2.repo_name)
445
446 response.mustcontain(opt)
447
448 fixture.destroy_repo(target_repo, forks='detach')
449
450 @pytest.mark.backends("hg", "git")
451 def test_set_fork_of_other_type_repo(self, autologin_user, backend,
452 csrf_token):
453 TARGET_REPO_MAP = {
454 'git': {
455 'type': 'hg',
456 'repo_name': HG_REPO},
457 'hg': {
458 'type': 'git',
459 'repo_name': GIT_REPO},
460 }
461 target_repo = TARGET_REPO_MAP[backend.alias]
462
463 repo2 = Repository.get_by_repo_name(target_repo['repo_name'])
464 response = self.app.post(
465 url('edit_repo_advanced_fork', repo_name=backend.repo_name),
466 params={'id_fork_of': repo2.repo_id, '_method': 'put',
467 'csrf_token': csrf_token})
468 assert_session_flash(
469 response,
470 'Cannot set repository as fork of repository with other type')
471
472 def test_set_fork_of_none(self, autologin_user, backend, csrf_token):
473 # mark it as None
474 response = self.app.post(
475 url('edit_repo_advanced_fork', repo_name=backend.repo_name),
476 params={'id_fork_of': None, '_method': 'put',
477 'csrf_token': csrf_token})
478 assert_session_flash(
479 response,
480 'Marked repo %s as fork of %s'
481 % (backend.repo_name, "Nothing"))
482 assert backend.repo.fork is None
483
484 def test_set_fork_of_same_repo(self, autologin_user, backend, csrf_token):
485 repo = Repository.get_by_repo_name(backend.repo_name)
486 response = self.app.post(
487 url('edit_repo_advanced_fork', repo_name=backend.repo_name),
488 params={'id_fork_of': repo.repo_id, '_method': 'put',
489 'csrf_token': csrf_token})
490 assert_session_flash(
491 response, 'An error occurred during this operation')
492
493 def test_create_on_top_level_without_permissions(self, backend):
402 def test_create_on_top_level_without_permissions(self, backend):
494 session = login_user_session(
403 session = login_user_session(
495 self.app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
404 self.app, TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
@@ -106,9 +106,7 b' class _BaseTest(TestController):'
106 )
106 )
107
107
108 # remove this fork
108 # remove this fork
109 response = self.app.post(
109 fixture.destroy_repo(fork_name)
110 url('repo', repo_name=fork_name),
111 params={'_method': 'delete', 'csrf_token': self.csrf_token})
112
110
113 def test_fork_create_into_group(self):
111 def test_fork_create_into_group(self):
114 self.log_user()
112 self.log_user()
General Comments 0
You need to be logged in to leave comments. Login now