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' |
|
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. |
|
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. |
|
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( |
|
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( |
|
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( |
|
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( |
|
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. |
|
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. |
|
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