##// END OF EJS Templates
repo-caches: moved view into pyramid.
marcink -
r1722:1a4c1af0 default
parent child Browse files
Show More
@@ -0,0 +1,76 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.httpexceptions import HTTPFound
24 from pyramid.view import view_config
25
26 from rhodecode.apps._base import RepoAppView
27 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
28 from rhodecode.lib import helpers as h
29 from rhodecode.model.meta import Session
30 from rhodecode.model.scm import ScmModel
31
32 log = logging.getLogger(__name__)
33
34
35 class RepoCachesView(RepoAppView):
36 def load_default_context(self):
37 c = self._get_local_tmpl_context()
38
39 # TODO(marcink): remove repo_info and use c.rhodecode_db_repo instead
40 c.repo_info = self.db_repo
41
42 self._register_global_c(c)
43 return c
44
45 @LoginRequired()
46 @HasRepoPermissionAnyDecorator('repository.admin')
47 @view_config(
48 route_name='edit_repo_caches', request_method='GET',
49 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
50 def repo_caches(self):
51 c = self.load_default_context()
52 c.active = 'caches'
53
54 return self._get_template_context(c)
55
56 @LoginRequired()
57 @HasRepoPermissionAnyDecorator('repository.admin')
58 @view_config(
59 route_name='edit_repo_caches', request_method='POST')
60 def repo_caches_purge(self):
61 _ = self.request.translate
62 c = self.load_default_context()
63 c.active = 'caches'
64
65 try:
66 ScmModel().mark_for_invalidation(self.db_repo_name, delete=True)
67 Session().commit()
68 h.flash(_('Cache invalidation successful'),
69 category='success')
70 except Exception:
71 log.exception("Exception during cache invalidation")
72 h.flash(_('An error occurred during cache invalidation'),
73 category='error')
74
75 raise HTTPFound(h.route_path(
76 'edit_repo_caches', repo_name=self.db_repo_name)) No newline at end of file
@@ -1,50 +1,56 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21
22 22 def includeme(config):
23 23
24 24 # Settings
25 25 config.add_route(
26 26 name='edit_repo',
27 27 pattern='/{repo_name:.*?[^/]}/settings', repo_route=True)
28 28
29 # Caches
30 config.add_route(
31 name='edit_repo_caches',
32 pattern='/{repo_name:.*?[^/]}/settings/caches', repo_route=True)
33
34 # Maintenance
29 35 config.add_route(
30 36 name='repo_maintenance',
31 37 pattern='/{repo_name:.*?[^/]}/maintenance', repo_route=True)
32 38
33 39 config.add_route(
34 40 name='repo_maintenance_execute',
35 41 pattern='/{repo_name:.*?[^/]}/maintenance/execute', repo_route=True)
36 42
37 43 # Strip
38 44 config.add_route(
39 45 name='strip',
40 46 pattern='/{repo_name:.*?[^/]}/strip', repo_route=True)
41 47
42 48 config.add_route(
43 49 name='strip_check',
44 50 pattern='/{repo_name:.*?[^/]}/strip_check', repo_route=True)
45 51
46 52 config.add_route(
47 53 name='strip_execute',
48 54 pattern='/{repo_name:.*?[^/]}/strip_execute', repo_route=True)
49 55 # Scan module for configuration decorators.
50 56 config.scan()
@@ -1,230 +1,231 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import mock
22 22 import pytest
23 23
24 24 from rhodecode.lib.utils2 import str2bool
25 25 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
26 26 from rhodecode.model.db import Repository, UserRepoToPerm, Permission, User
27 27 from rhodecode.model.meta import Session
28 28 from rhodecode.tests import (
29 29 url, HG_REPO, TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN,
30 30 assert_session_flash)
31 31 from rhodecode.tests.fixture import Fixture
32 32
33 33 fixture = Fixture()
34 34
35 35
36 36 def route_path(name, params=None, **kwargs):
37 37 import urllib
38 38
39 39 base_url = {
40 40 'edit_repo': '/{repo_name}/settings',
41 'edit_repo_caches': '/{repo_name}/settings/caches',
41 42 }[name].format(**kwargs)
42 43
43 44 if params:
44 45 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
45 46 return base_url
46 47
47 48
48 49 def _get_permission_for_user(user, repo):
49 50 perm = UserRepoToPerm.query()\
50 51 .filter(UserRepoToPerm.repository ==
51 52 Repository.get_by_repo_name(repo))\
52 53 .filter(UserRepoToPerm.user == User.get_by_username(user))\
53 54 .all()
54 55 return perm
55 56
56 57
57 58 @pytest.mark.usefixtures('autologin_user', 'app')
58 59 class TestAdminRepoSettings(object):
59 60 @pytest.mark.parametrize('urlname', [
60 61 'edit_repo',
62 'edit_repo_caches'
61 63 ])
62 64 def test_show_page(self, urlname, app, backend):
63 65 app.get(route_path(urlname, repo_name=backend.repo_name), status=200)
64 66
65 67 def test_edit_accessible_when_missing_requirements(
66 68 self, backend_hg, autologin_user):
67 69 scm_patcher = mock.patch.object(
68 70 Repository, 'scm_instance', side_effect=RepositoryRequirementError)
69 71 with scm_patcher:
70 72 self.app.get(route_path('edit_repo', repo_name=backend_hg.repo_name))
71 73
72 74 @pytest.mark.parametrize('urlname', [
73 75 'edit_repo_perms',
74 76 'edit_repo_advanced',
75 77 'repo_vcs_settings',
76 78 'edit_repo_fields',
77 79 'repo_settings_issuetracker',
78 'edit_repo_caches',
79 80 'edit_repo_remote',
80 81 'edit_repo_statistics',
81 82 ])
82 83 def test_show_page_pylons(self, urlname, app):
83 84 app.get(url(urlname, repo_name=HG_REPO))
84 85
85 86 @pytest.mark.parametrize('update_settings', [
86 87 {'repo_description': 'alter-desc'},
87 88 {'repo_owner': TEST_USER_REGULAR_LOGIN},
88 89 {'repo_private': 'true'},
89 90 {'repo_enable_locking': 'true'},
90 91 {'repo_enable_downloads': 'true'},
91 92 ])
92 93 def test_update_repo_settings(self, update_settings, csrf_token, backend, user_util):
93 94 repo = user_util.create_repo(repo_type=backend.alias)
94 95 repo_name = repo.repo_name
95 96
96 97 params = fixture._get_repo_create_params(
97 98 csrf_token=csrf_token,
98 99 repo_name=repo_name,
99 100 repo_type=backend.alias,
100 101 repo_owner=TEST_USER_ADMIN_LOGIN,
101 102 repo_description='DESC',
102 103
103 104 repo_private='false',
104 105 repo_enable_locking='false',
105 106 repo_enable_downloads='false')
106 107 params.update(update_settings)
107 108 self.app.post(
108 109 route_path('edit_repo', repo_name=repo_name),
109 110 params=params, status=302)
110 111
111 112 repo = Repository.get_by_repo_name(repo_name)
112 113 assert repo.user.username == \
113 114 update_settings.get('repo_owner', repo.user.username)
114 115
115 116 assert repo.description == \
116 117 update_settings.get('repo_description', repo.description)
117 118
118 119 assert repo.private == \
119 120 str2bool(update_settings.get(
120 121 'repo_private', repo.private))
121 122
122 123 assert repo.enable_locking == \
123 124 str2bool(update_settings.get(
124 125 'repo_enable_locking', repo.enable_locking))
125 126
126 127 assert repo.enable_downloads == \
127 128 str2bool(update_settings.get(
128 129 'repo_enable_downloads', repo.enable_downloads))
129 130
130 131 def test_update_repo_name_via_settings(self, csrf_token, user_util, backend):
131 132 repo = user_util.create_repo(repo_type=backend.alias)
132 133 repo_name = repo.repo_name
133 134
134 135 repo_group = user_util.create_repo_group()
135 136 repo_group_name = repo_group.group_name
136 137 new_name = repo_group_name + '_' + repo_name
137 138
138 139 params = fixture._get_repo_create_params(
139 140 csrf_token=csrf_token,
140 141 repo_name=new_name,
141 142 repo_type=backend.alias,
142 143 repo_owner=TEST_USER_ADMIN_LOGIN,
143 144 repo_description='DESC',
144 145 repo_private='false',
145 146 repo_enable_locking='false',
146 147 repo_enable_downloads='false')
147 148 self.app.post(
148 149 route_path('edit_repo', repo_name=repo_name),
149 150 params=params, status=302)
150 151 repo = Repository.get_by_repo_name(new_name)
151 152 assert repo.repo_name == new_name
152 153
153 154 def test_update_repo_group_via_settings(self, csrf_token, user_util, backend):
154 155 repo = user_util.create_repo(repo_type=backend.alias)
155 156 repo_name = repo.repo_name
156 157
157 158 repo_group = user_util.create_repo_group()
158 159 repo_group_name = repo_group.group_name
159 160 repo_group_id = repo_group.group_id
160 161
161 162 new_name = repo_group_name + '/' + repo_name
162 163 params = fixture._get_repo_create_params(
163 164 csrf_token=csrf_token,
164 165 repo_name=repo_name,
165 166 repo_type=backend.alias,
166 167 repo_owner=TEST_USER_ADMIN_LOGIN,
167 168 repo_description='DESC',
168 169 repo_group=repo_group_id,
169 170 repo_private='false',
170 171 repo_enable_locking='false',
171 172 repo_enable_downloads='false')
172 173 self.app.post(
173 174 route_path('edit_repo', repo_name=repo_name),
174 175 params=params, status=302)
175 176 repo = Repository.get_by_repo_name(new_name)
176 177 assert repo.repo_name == new_name
177 178
178 179 def test_set_private_flag_sets_default_user_permissions_to_none(
179 180 self, autologin_user, backend, csrf_token):
180 181
181 182 # initially repository perm should be read
182 183 perm = _get_permission_for_user(user='default', repo=backend.repo_name)
183 184 assert len(perm) == 1
184 185 assert perm[0].permission.permission_name == 'repository.read'
185 186 assert not backend.repo.private
186 187
187 188 response = self.app.post(
188 189 route_path('edit_repo', repo_name=backend.repo_name),
189 190 params=fixture._get_repo_create_params(
190 191 repo_private='true',
191 192 repo_name=backend.repo_name,
192 193 repo_type=backend.alias,
193 194 repo_owner=TEST_USER_ADMIN_LOGIN,
194 195 csrf_token=csrf_token), status=302)
195 196
196 197 assert_session_flash(
197 198 response,
198 199 msg='Repository %s updated successfully' % (backend.repo_name))
199 200
200 201 repo = Repository.get_by_repo_name(backend.repo_name)
201 202 assert repo.private is True
202 203
203 204 # now the repo default permission should be None
204 205 perm = _get_permission_for_user(user='default', repo=backend.repo_name)
205 206 assert len(perm) == 1
206 207 assert perm[0].permission.permission_name == 'repository.none'
207 208
208 209 response = self.app.post(
209 210 route_path('edit_repo', repo_name=backend.repo_name),
210 211 params=fixture._get_repo_create_params(
211 212 repo_private='false',
212 213 repo_name=backend.repo_name,
213 214 repo_type=backend.alias,
214 215 repo_owner=TEST_USER_ADMIN_LOGIN,
215 216 csrf_token=csrf_token), status=302)
216 217
217 218 assert_session_flash(
218 219 response,
219 220 msg='Repository %s updated successfully' % (backend.repo_name))
220 221 assert backend.repo.private is False
221 222
222 223 # we turn off private now the repo default permission should stay None
223 224 perm = _get_permission_for_user(user='default', repo=backend.repo_name)
224 225 assert len(perm) == 1
225 226 assert perm[0].permission.permission_name == 'repository.none'
226 227
227 228 # update this permission back
228 229 perm[0].permission = Permission.get_by_key('repository.read')
229 230 Session().add(perm[0])
230 231 Session().commit()
@@ -1,1107 +1,1098 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 Routes configuration
23 23
24 24 The more specific and detailed routes should be defined first so they
25 25 may take precedent over the more generic routes. For more information
26 26 refer to the routes manual at http://routes.groovie.org/docs/
27 27
28 28 IMPORTANT: if you change any routing here, make sure to take a look at lib/base.py
29 29 and _route_name variable which uses some of stored naming here to do redirects.
30 30 """
31 31 import os
32 32 import re
33 33 from routes import Mapper
34 34
35 35 # prefix for non repository related links needs to be prefixed with `/`
36 36 ADMIN_PREFIX = '/_admin'
37 37 STATIC_FILE_PREFIX = '/_static'
38 38
39 39 # Default requirements for URL parts
40 40 URL_NAME_REQUIREMENTS = {
41 41 # group name can have a slash in them, but they must not end with a slash
42 42 'group_name': r'.*?[^/]',
43 43 'repo_group_name': r'.*?[^/]',
44 44 # repo names can have a slash in them, but they must not end with a slash
45 45 'repo_name': r'.*?[^/]',
46 46 # file path eats up everything at the end
47 47 'f_path': r'.*',
48 48 # reference types
49 49 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
50 50 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
51 51 }
52 52
53 53
54 54 def add_route_requirements(route_path, requirements):
55 55 """
56 56 Adds regex requirements to pyramid routes using a mapping dict
57 57
58 58 >>> add_route_requirements('/{action}/{id}', {'id': r'\d+'})
59 59 '/{action}/{id:\d+}'
60 60
61 61 """
62 62 for key, regex in requirements.items():
63 63 route_path = route_path.replace('{%s}' % key, '{%s:%s}' % (key, regex))
64 64 return route_path
65 65
66 66
67 67 class JSRoutesMapper(Mapper):
68 68 """
69 69 Wrapper for routes.Mapper to make pyroutes compatible url definitions
70 70 """
71 71 _named_route_regex = re.compile(r'^[a-z-_0-9A-Z]+$')
72 72 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
73 73 def __init__(self, *args, **kw):
74 74 super(JSRoutesMapper, self).__init__(*args, **kw)
75 75 self._jsroutes = []
76 76
77 77 def connect(self, *args, **kw):
78 78 """
79 79 Wrapper for connect to take an extra argument jsroute=True
80 80
81 81 :param jsroute: boolean, if True will add the route to the pyroutes list
82 82 """
83 83 if kw.pop('jsroute', False):
84 84 if not self._named_route_regex.match(args[0]):
85 85 raise Exception('only named routes can be added to pyroutes')
86 86 self._jsroutes.append(args[0])
87 87
88 88 super(JSRoutesMapper, self).connect(*args, **kw)
89 89
90 90 def _extract_route_information(self, route):
91 91 """
92 92 Convert a route into tuple(name, path, args), eg:
93 93 ('show_user', '/profile/%(username)s', ['username'])
94 94 """
95 95 routepath = route.routepath
96 96 def replace(matchobj):
97 97 if matchobj.group(1):
98 98 return "%%(%s)s" % matchobj.group(1).split(':')[0]
99 99 else:
100 100 return "%%(%s)s" % matchobj.group(2)
101 101
102 102 routepath = self._argument_prog.sub(replace, routepath)
103 103 return (
104 104 route.name,
105 105 routepath,
106 106 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
107 107 for arg in self._argument_prog.findall(route.routepath)]
108 108 )
109 109
110 110 def jsroutes(self):
111 111 """
112 112 Return a list of pyroutes.js compatible routes
113 113 """
114 114 for route_name in self._jsroutes:
115 115 yield self._extract_route_information(self._routenames[route_name])
116 116
117 117
118 118 def make_map(config):
119 119 """Create, configure and return the routes Mapper"""
120 120 rmap = JSRoutesMapper(directory=config['pylons.paths']['controllers'],
121 121 always_scan=config['debug'])
122 122 rmap.minimization = False
123 123 rmap.explicit = False
124 124
125 125 from rhodecode.lib.utils2 import str2bool
126 126 from rhodecode.model import repo, repo_group
127 127
128 128 def check_repo(environ, match_dict):
129 129 """
130 130 check for valid repository for proper 404 handling
131 131
132 132 :param environ:
133 133 :param match_dict:
134 134 """
135 135 repo_name = match_dict.get('repo_name')
136 136
137 137 if match_dict.get('f_path'):
138 138 # fix for multiple initial slashes that causes errors
139 139 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
140 140 repo_model = repo.RepoModel()
141 141 by_name_match = repo_model.get_by_repo_name(repo_name)
142 142 # if we match quickly from database, short circuit the operation,
143 143 # and validate repo based on the type.
144 144 if by_name_match:
145 145 return True
146 146
147 147 by_id_match = repo_model.get_repo_by_id(repo_name)
148 148 if by_id_match:
149 149 repo_name = by_id_match.repo_name
150 150 match_dict['repo_name'] = repo_name
151 151 return True
152 152
153 153 return False
154 154
155 155 def check_group(environ, match_dict):
156 156 """
157 157 check for valid repository group path for proper 404 handling
158 158
159 159 :param environ:
160 160 :param match_dict:
161 161 """
162 162 repo_group_name = match_dict.get('group_name')
163 163 repo_group_model = repo_group.RepoGroupModel()
164 164 by_name_match = repo_group_model.get_by_group_name(repo_group_name)
165 165 if by_name_match:
166 166 return True
167 167
168 168 return False
169 169
170 170 def check_user_group(environ, match_dict):
171 171 """
172 172 check for valid user group for proper 404 handling
173 173
174 174 :param environ:
175 175 :param match_dict:
176 176 """
177 177 return True
178 178
179 179 def check_int(environ, match_dict):
180 180 return match_dict.get('id').isdigit()
181 181
182 182
183 183 #==========================================================================
184 184 # CUSTOM ROUTES HERE
185 185 #==========================================================================
186 186
187 187 # MAIN PAGE
188 188 rmap.connect('home', '/', controller='home', action='index', jsroute=True)
189 189
190 190 # ping and pylons error test
191 191 rmap.connect('ping', '%s/ping' % (ADMIN_PREFIX,), controller='home', action='ping')
192 192 rmap.connect('error_test', '%s/error_test' % (ADMIN_PREFIX,), controller='home', action='error_test')
193 193
194 194 # ADMIN REPOSITORY ROUTES
195 195 with rmap.submapper(path_prefix=ADMIN_PREFIX,
196 196 controller='admin/repos') as m:
197 197 m.connect('repos', '/repos',
198 198 action='create', conditions={'method': ['POST']})
199 199 m.connect('repos', '/repos',
200 200 action='index', conditions={'method': ['GET']})
201 201 m.connect('new_repo', '/create_repository', jsroute=True,
202 202 action='create_repository', conditions={'method': ['GET']})
203 203 m.connect('delete_repo', '/repos/{repo_name}',
204 204 action='delete', conditions={'method': ['DELETE']},
205 205 requirements=URL_NAME_REQUIREMENTS)
206 206 m.connect('repo', '/repos/{repo_name}',
207 207 action='show', conditions={'method': ['GET'],
208 208 'function': check_repo},
209 209 requirements=URL_NAME_REQUIREMENTS)
210 210
211 211 # ADMIN REPOSITORY GROUPS ROUTES
212 212 with rmap.submapper(path_prefix=ADMIN_PREFIX,
213 213 controller='admin/repo_groups') as m:
214 214 m.connect('repo_groups', '/repo_groups',
215 215 action='create', conditions={'method': ['POST']})
216 216 m.connect('repo_groups', '/repo_groups',
217 217 action='index', conditions={'method': ['GET']})
218 218 m.connect('new_repo_group', '/repo_groups/new',
219 219 action='new', conditions={'method': ['GET']})
220 220 m.connect('update_repo_group', '/repo_groups/{group_name}',
221 221 action='update', conditions={'method': ['PUT'],
222 222 'function': check_group},
223 223 requirements=URL_NAME_REQUIREMENTS)
224 224
225 225 # EXTRAS REPO GROUP ROUTES
226 226 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
227 227 action='edit',
228 228 conditions={'method': ['GET'], 'function': check_group},
229 229 requirements=URL_NAME_REQUIREMENTS)
230 230 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
231 231 action='edit',
232 232 conditions={'method': ['PUT'], 'function': check_group},
233 233 requirements=URL_NAME_REQUIREMENTS)
234 234
235 235 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
236 236 action='edit_repo_group_advanced',
237 237 conditions={'method': ['GET'], 'function': check_group},
238 238 requirements=URL_NAME_REQUIREMENTS)
239 239 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
240 240 action='edit_repo_group_advanced',
241 241 conditions={'method': ['PUT'], 'function': check_group},
242 242 requirements=URL_NAME_REQUIREMENTS)
243 243
244 244 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
245 245 action='edit_repo_group_perms',
246 246 conditions={'method': ['GET'], 'function': check_group},
247 247 requirements=URL_NAME_REQUIREMENTS)
248 248 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
249 249 action='update_perms',
250 250 conditions={'method': ['PUT'], 'function': check_group},
251 251 requirements=URL_NAME_REQUIREMENTS)
252 252
253 253 m.connect('delete_repo_group', '/repo_groups/{group_name}',
254 254 action='delete', conditions={'method': ['DELETE'],
255 255 'function': check_group},
256 256 requirements=URL_NAME_REQUIREMENTS)
257 257
258 258 # ADMIN USER ROUTES
259 259 with rmap.submapper(path_prefix=ADMIN_PREFIX,
260 260 controller='admin/users') as m:
261 261 m.connect('users', '/users',
262 262 action='create', conditions={'method': ['POST']})
263 263 m.connect('new_user', '/users/new',
264 264 action='new', conditions={'method': ['GET']})
265 265 m.connect('update_user', '/users/{user_id}',
266 266 action='update', conditions={'method': ['PUT']})
267 267 m.connect('delete_user', '/users/{user_id}',
268 268 action='delete', conditions={'method': ['DELETE']})
269 269 m.connect('edit_user', '/users/{user_id}/edit',
270 270 action='edit', conditions={'method': ['GET']}, jsroute=True)
271 271 m.connect('user', '/users/{user_id}',
272 272 action='show', conditions={'method': ['GET']})
273 273 m.connect('force_password_reset_user', '/users/{user_id}/password_reset',
274 274 action='reset_password', conditions={'method': ['POST']})
275 275 m.connect('create_personal_repo_group', '/users/{user_id}/create_repo_group',
276 276 action='create_personal_repo_group', conditions={'method': ['POST']})
277 277
278 278 # EXTRAS USER ROUTES
279 279 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
280 280 action='edit_advanced', conditions={'method': ['GET']})
281 281 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
282 282 action='update_advanced', conditions={'method': ['PUT']})
283 283
284 284 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
285 285 action='edit_global_perms', conditions={'method': ['GET']})
286 286 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
287 287 action='update_global_perms', conditions={'method': ['PUT']})
288 288
289 289 m.connect('edit_user_perms_summary', '/users/{user_id}/edit/permissions_summary',
290 290 action='edit_perms_summary', conditions={'method': ['GET']})
291 291
292 292 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
293 293 action='edit_emails', conditions={'method': ['GET']})
294 294 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
295 295 action='add_email', conditions={'method': ['PUT']})
296 296 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
297 297 action='delete_email', conditions={'method': ['DELETE']})
298 298
299 299 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
300 300 action='edit_ips', conditions={'method': ['GET']})
301 301 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
302 302 action='add_ip', conditions={'method': ['PUT']})
303 303 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
304 304 action='delete_ip', conditions={'method': ['DELETE']})
305 305
306 306 # ADMIN USER GROUPS REST ROUTES
307 307 with rmap.submapper(path_prefix=ADMIN_PREFIX,
308 308 controller='admin/user_groups') as m:
309 309 m.connect('users_groups', '/user_groups',
310 310 action='create', conditions={'method': ['POST']})
311 311 m.connect('users_groups', '/user_groups',
312 312 action='index', conditions={'method': ['GET']})
313 313 m.connect('new_users_group', '/user_groups/new',
314 314 action='new', conditions={'method': ['GET']})
315 315 m.connect('update_users_group', '/user_groups/{user_group_id}',
316 316 action='update', conditions={'method': ['PUT']})
317 317 m.connect('delete_users_group', '/user_groups/{user_group_id}',
318 318 action='delete', conditions={'method': ['DELETE']})
319 319 m.connect('edit_users_group', '/user_groups/{user_group_id}/edit',
320 320 action='edit', conditions={'method': ['GET']},
321 321 function=check_user_group)
322 322
323 323 # EXTRAS USER GROUP ROUTES
324 324 m.connect('edit_user_group_global_perms',
325 325 '/user_groups/{user_group_id}/edit/global_permissions',
326 326 action='edit_global_perms', conditions={'method': ['GET']})
327 327 m.connect('edit_user_group_global_perms',
328 328 '/user_groups/{user_group_id}/edit/global_permissions',
329 329 action='update_global_perms', conditions={'method': ['PUT']})
330 330 m.connect('edit_user_group_perms_summary',
331 331 '/user_groups/{user_group_id}/edit/permissions_summary',
332 332 action='edit_perms_summary', conditions={'method': ['GET']})
333 333
334 334 m.connect('edit_user_group_perms',
335 335 '/user_groups/{user_group_id}/edit/permissions',
336 336 action='edit_perms', conditions={'method': ['GET']})
337 337 m.connect('edit_user_group_perms',
338 338 '/user_groups/{user_group_id}/edit/permissions',
339 339 action='update_perms', conditions={'method': ['PUT']})
340 340
341 341 m.connect('edit_user_group_advanced',
342 342 '/user_groups/{user_group_id}/edit/advanced',
343 343 action='edit_advanced', conditions={'method': ['GET']})
344 344
345 345 m.connect('edit_user_group_advanced_sync',
346 346 '/user_groups/{user_group_id}/edit/advanced/sync',
347 347 action='edit_advanced_set_synchronization', conditions={'method': ['POST']})
348 348
349 349 m.connect('edit_user_group_members',
350 350 '/user_groups/{user_group_id}/edit/members', jsroute=True,
351 351 action='user_group_members', conditions={'method': ['GET']})
352 352
353 353 # ADMIN PERMISSIONS ROUTES
354 354 with rmap.submapper(path_prefix=ADMIN_PREFIX,
355 355 controller='admin/permissions') as m:
356 356 m.connect('admin_permissions_application', '/permissions/application',
357 357 action='permission_application_update', conditions={'method': ['POST']})
358 358 m.connect('admin_permissions_application', '/permissions/application',
359 359 action='permission_application', conditions={'method': ['GET']})
360 360
361 361 m.connect('admin_permissions_global', '/permissions/global',
362 362 action='permission_global_update', conditions={'method': ['POST']})
363 363 m.connect('admin_permissions_global', '/permissions/global',
364 364 action='permission_global', conditions={'method': ['GET']})
365 365
366 366 m.connect('admin_permissions_object', '/permissions/object',
367 367 action='permission_objects_update', conditions={'method': ['POST']})
368 368 m.connect('admin_permissions_object', '/permissions/object',
369 369 action='permission_objects', conditions={'method': ['GET']})
370 370
371 371 m.connect('admin_permissions_ips', '/permissions/ips',
372 372 action='permission_ips', conditions={'method': ['POST']})
373 373 m.connect('admin_permissions_ips', '/permissions/ips',
374 374 action='permission_ips', conditions={'method': ['GET']})
375 375
376 376 m.connect('admin_permissions_overview', '/permissions/overview',
377 377 action='permission_perms', conditions={'method': ['GET']})
378 378
379 379 # ADMIN DEFAULTS REST ROUTES
380 380 with rmap.submapper(path_prefix=ADMIN_PREFIX,
381 381 controller='admin/defaults') as m:
382 382 m.connect('admin_defaults_repositories', '/defaults/repositories',
383 383 action='update_repository_defaults', conditions={'method': ['POST']})
384 384 m.connect('admin_defaults_repositories', '/defaults/repositories',
385 385 action='index', conditions={'method': ['GET']})
386 386
387 387 # ADMIN DEBUG STYLE ROUTES
388 388 if str2bool(config.get('debug_style')):
389 389 with rmap.submapper(path_prefix=ADMIN_PREFIX + '/debug_style',
390 390 controller='debug_style') as m:
391 391 m.connect('debug_style_home', '',
392 392 action='index', conditions={'method': ['GET']})
393 393 m.connect('debug_style_template', '/t/{t_path}',
394 394 action='template', conditions={'method': ['GET']})
395 395
396 396 # ADMIN SETTINGS ROUTES
397 397 with rmap.submapper(path_prefix=ADMIN_PREFIX,
398 398 controller='admin/settings') as m:
399 399
400 400 # default
401 401 m.connect('admin_settings', '/settings',
402 402 action='settings_global_update',
403 403 conditions={'method': ['POST']})
404 404 m.connect('admin_settings', '/settings',
405 405 action='settings_global', conditions={'method': ['GET']})
406 406
407 407 m.connect('admin_settings_vcs', '/settings/vcs',
408 408 action='settings_vcs_update',
409 409 conditions={'method': ['POST']})
410 410 m.connect('admin_settings_vcs', '/settings/vcs',
411 411 action='settings_vcs',
412 412 conditions={'method': ['GET']})
413 413 m.connect('admin_settings_vcs', '/settings/vcs',
414 414 action='delete_svn_pattern',
415 415 conditions={'method': ['DELETE']})
416 416
417 417 m.connect('admin_settings_mapping', '/settings/mapping',
418 418 action='settings_mapping_update',
419 419 conditions={'method': ['POST']})
420 420 m.connect('admin_settings_mapping', '/settings/mapping',
421 421 action='settings_mapping', conditions={'method': ['GET']})
422 422
423 423 m.connect('admin_settings_global', '/settings/global',
424 424 action='settings_global_update',
425 425 conditions={'method': ['POST']})
426 426 m.connect('admin_settings_global', '/settings/global',
427 427 action='settings_global', conditions={'method': ['GET']})
428 428
429 429 m.connect('admin_settings_visual', '/settings/visual',
430 430 action='settings_visual_update',
431 431 conditions={'method': ['POST']})
432 432 m.connect('admin_settings_visual', '/settings/visual',
433 433 action='settings_visual', conditions={'method': ['GET']})
434 434
435 435 m.connect('admin_settings_issuetracker',
436 436 '/settings/issue-tracker', action='settings_issuetracker',
437 437 conditions={'method': ['GET']})
438 438 m.connect('admin_settings_issuetracker_save',
439 439 '/settings/issue-tracker/save',
440 440 action='settings_issuetracker_save',
441 441 conditions={'method': ['POST']})
442 442 m.connect('admin_issuetracker_test', '/settings/issue-tracker/test',
443 443 action='settings_issuetracker_test',
444 444 conditions={'method': ['POST']})
445 445 m.connect('admin_issuetracker_delete',
446 446 '/settings/issue-tracker/delete',
447 447 action='settings_issuetracker_delete',
448 448 conditions={'method': ['DELETE']})
449 449
450 450 m.connect('admin_settings_email', '/settings/email',
451 451 action='settings_email_update',
452 452 conditions={'method': ['POST']})
453 453 m.connect('admin_settings_email', '/settings/email',
454 454 action='settings_email', conditions={'method': ['GET']})
455 455
456 456 m.connect('admin_settings_hooks', '/settings/hooks',
457 457 action='settings_hooks_update',
458 458 conditions={'method': ['POST', 'DELETE']})
459 459 m.connect('admin_settings_hooks', '/settings/hooks',
460 460 action='settings_hooks', conditions={'method': ['GET']})
461 461
462 462 m.connect('admin_settings_search', '/settings/search',
463 463 action='settings_search', conditions={'method': ['GET']})
464 464
465 465 m.connect('admin_settings_supervisor', '/settings/supervisor',
466 466 action='settings_supervisor', conditions={'method': ['GET']})
467 467 m.connect('admin_settings_supervisor_log', '/settings/supervisor/{procid}/log',
468 468 action='settings_supervisor_log', conditions={'method': ['GET']})
469 469
470 470 m.connect('admin_settings_labs', '/settings/labs',
471 471 action='settings_labs_update',
472 472 conditions={'method': ['POST']})
473 473 m.connect('admin_settings_labs', '/settings/labs',
474 474 action='settings_labs', conditions={'method': ['GET']})
475 475
476 476 # ADMIN MY ACCOUNT
477 477 with rmap.submapper(path_prefix=ADMIN_PREFIX,
478 478 controller='admin/my_account') as m:
479 479
480 480 m.connect('my_account_edit', '/my_account/edit',
481 481 action='my_account_edit', conditions={'method': ['GET']})
482 482 m.connect('my_account', '/my_account/update',
483 483 action='my_account_update', conditions={'method': ['POST']})
484 484
485 485 # NOTE(marcink): this needs to be kept for password force flag to be
486 486 # handler, remove after migration to pyramid
487 487 m.connect('my_account_password', '/my_account/password',
488 488 action='my_account_password', conditions={'method': ['GET']})
489 489
490 490 m.connect('my_account_repos', '/my_account/repos',
491 491 action='my_account_repos', conditions={'method': ['GET']})
492 492
493 493 m.connect('my_account_watched', '/my_account/watched',
494 494 action='my_account_watched', conditions={'method': ['GET']})
495 495
496 496 m.connect('my_account_pullrequests', '/my_account/pull_requests',
497 497 action='my_account_pullrequests', conditions={'method': ['GET']})
498 498
499 499 m.connect('my_account_perms', '/my_account/perms',
500 500 action='my_account_perms', conditions={'method': ['GET']})
501 501
502 502 m.connect('my_account_emails', '/my_account/emails',
503 503 action='my_account_emails', conditions={'method': ['GET']})
504 504 m.connect('my_account_emails', '/my_account/emails',
505 505 action='my_account_emails_add', conditions={'method': ['POST']})
506 506 m.connect('my_account_emails', '/my_account/emails',
507 507 action='my_account_emails_delete', conditions={'method': ['DELETE']})
508 508
509 509 m.connect('my_account_notifications', '/my_account/notifications',
510 510 action='my_notifications',
511 511 conditions={'method': ['GET']})
512 512 m.connect('my_account_notifications_toggle_visibility',
513 513 '/my_account/toggle_visibility',
514 514 action='my_notifications_toggle_visibility',
515 515 conditions={'method': ['POST']})
516 516 m.connect('my_account_notifications_test_channelstream',
517 517 '/my_account/test_channelstream',
518 518 action='my_account_notifications_test_channelstream',
519 519 conditions={'method': ['POST']})
520 520
521 521 # NOTIFICATION REST ROUTES
522 522 with rmap.submapper(path_prefix=ADMIN_PREFIX,
523 523 controller='admin/notifications') as m:
524 524 m.connect('notifications', '/notifications',
525 525 action='index', conditions={'method': ['GET']})
526 526 m.connect('notifications_mark_all_read', '/notifications/mark_all_read',
527 527 action='mark_all_read', conditions={'method': ['POST']})
528 528 m.connect('/notifications/{notification_id}',
529 529 action='update', conditions={'method': ['PUT']})
530 530 m.connect('/notifications/{notification_id}',
531 531 action='delete', conditions={'method': ['DELETE']})
532 532 m.connect('notification', '/notifications/{notification_id}',
533 533 action='show', conditions={'method': ['GET']})
534 534
535 535 # ADMIN GIST
536 536 with rmap.submapper(path_prefix=ADMIN_PREFIX,
537 537 controller='admin/gists') as m:
538 538 m.connect('gists', '/gists',
539 539 action='create', conditions={'method': ['POST']})
540 540 m.connect('gists', '/gists', jsroute=True,
541 541 action='index', conditions={'method': ['GET']})
542 542 m.connect('new_gist', '/gists/new', jsroute=True,
543 543 action='new', conditions={'method': ['GET']})
544 544
545 545 m.connect('/gists/{gist_id}',
546 546 action='delete', conditions={'method': ['DELETE']})
547 547 m.connect('edit_gist', '/gists/{gist_id}/edit',
548 548 action='edit_form', conditions={'method': ['GET']})
549 549 m.connect('edit_gist', '/gists/{gist_id}/edit',
550 550 action='edit', conditions={'method': ['POST']})
551 551 m.connect(
552 552 'edit_gist_check_revision', '/gists/{gist_id}/edit/check_revision',
553 553 action='check_revision', conditions={'method': ['GET']})
554 554
555 555 m.connect('gist', '/gists/{gist_id}',
556 556 action='show', conditions={'method': ['GET']})
557 557 m.connect('gist_rev', '/gists/{gist_id}/{revision}',
558 558 revision='tip',
559 559 action='show', conditions={'method': ['GET']})
560 560 m.connect('formatted_gist', '/gists/{gist_id}/{revision}/{format}',
561 561 revision='tip',
562 562 action='show', conditions={'method': ['GET']})
563 563 m.connect('formatted_gist_file', '/gists/{gist_id}/{revision}/{format}/{f_path}',
564 564 revision='tip',
565 565 action='show', conditions={'method': ['GET']},
566 566 requirements=URL_NAME_REQUIREMENTS)
567 567
568 568 # ADMIN MAIN PAGES
569 569 with rmap.submapper(path_prefix=ADMIN_PREFIX,
570 570 controller='admin/admin') as m:
571 571 m.connect('admin_home', '', action='index')
572 572 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
573 573 action='add_repo')
574 574 m.connect(
575 575 'pull_requests_global_0', '/pull_requests/{pull_request_id:[0-9]+}',
576 576 action='pull_requests')
577 577 m.connect(
578 578 'pull_requests_global_1', '/pull-requests/{pull_request_id:[0-9]+}',
579 579 action='pull_requests')
580 580 m.connect(
581 581 'pull_requests_global', '/pull-request/{pull_request_id:[0-9]+}',
582 582 action='pull_requests')
583 583
584 584 # USER JOURNAL
585 585 rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,),
586 586 controller='journal', action='index')
587 587 rmap.connect('journal_rss', '%s/journal/rss' % (ADMIN_PREFIX,),
588 588 controller='journal', action='journal_rss')
589 589 rmap.connect('journal_atom', '%s/journal/atom' % (ADMIN_PREFIX,),
590 590 controller='journal', action='journal_atom')
591 591
592 592 rmap.connect('public_journal', '%s/public_journal' % (ADMIN_PREFIX,),
593 593 controller='journal', action='public_journal')
594 594
595 595 rmap.connect('public_journal_rss', '%s/public_journal/rss' % (ADMIN_PREFIX,),
596 596 controller='journal', action='public_journal_rss')
597 597
598 598 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % (ADMIN_PREFIX,),
599 599 controller='journal', action='public_journal_rss')
600 600
601 601 rmap.connect('public_journal_atom',
602 602 '%s/public_journal/atom' % (ADMIN_PREFIX,), controller='journal',
603 603 action='public_journal_atom')
604 604
605 605 rmap.connect('public_journal_atom_old',
606 606 '%s/public_journal_atom' % (ADMIN_PREFIX,), controller='journal',
607 607 action='public_journal_atom')
608 608
609 609 rmap.connect('toggle_following', '%s/toggle_following' % (ADMIN_PREFIX,),
610 610 controller='journal', action='toggle_following', jsroute=True,
611 611 conditions={'method': ['POST']})
612 612
613 613 # FEEDS
614 614 rmap.connect('rss_feed_home', '/{repo_name}/feed/rss',
615 615 controller='feed', action='rss',
616 616 conditions={'function': check_repo},
617 617 requirements=URL_NAME_REQUIREMENTS)
618 618
619 619 rmap.connect('atom_feed_home', '/{repo_name}/feed/atom',
620 620 controller='feed', action='atom',
621 621 conditions={'function': check_repo},
622 622 requirements=URL_NAME_REQUIREMENTS)
623 623
624 624 #==========================================================================
625 625 # REPOSITORY ROUTES
626 626 #==========================================================================
627 627
628 628 rmap.connect('repo_creating_home', '/{repo_name}/repo_creating',
629 629 controller='admin/repos', action='repo_creating',
630 630 requirements=URL_NAME_REQUIREMENTS)
631 631 rmap.connect('repo_check_home', '/{repo_name}/crepo_check',
632 632 controller='admin/repos', action='repo_check',
633 633 requirements=URL_NAME_REQUIREMENTS)
634 634
635 635 rmap.connect('repo_stats', '/{repo_name}/repo_stats/{commit_id}',
636 636 controller='summary', action='repo_stats',
637 637 conditions={'function': check_repo},
638 638 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
639 639
640 640 rmap.connect('repo_refs_data', '/{repo_name}/refs-data',
641 641 controller='summary', action='repo_refs_data',
642 642 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
643 643 rmap.connect('repo_refs_changelog_data', '/{repo_name}/refs-data-changelog',
644 644 controller='summary', action='repo_refs_changelog_data',
645 645 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
646 646 rmap.connect('repo_default_reviewers_data', '/{repo_name}/default-reviewers',
647 647 controller='summary', action='repo_default_reviewers_data',
648 648 jsroute=True, requirements=URL_NAME_REQUIREMENTS)
649 649
650 650 rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}',
651 651 controller='changeset', revision='tip',
652 652 conditions={'function': check_repo},
653 653 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
654 654 rmap.connect('changeset_children', '/{repo_name}/changeset_children/{revision}',
655 655 controller='changeset', revision='tip', action='changeset_children',
656 656 conditions={'function': check_repo},
657 657 requirements=URL_NAME_REQUIREMENTS)
658 658 rmap.connect('changeset_parents', '/{repo_name}/changeset_parents/{revision}',
659 659 controller='changeset', revision='tip', action='changeset_parents',
660 660 conditions={'function': check_repo},
661 661 requirements=URL_NAME_REQUIREMENTS)
662 662
663 663 # repo edit options
664 664 rmap.connect('edit_repo_perms', '/{repo_name}/settings/permissions',
665 665 jsroute=True,
666 666 controller='admin/repos', action='edit_permissions',
667 667 conditions={'method': ['GET'], 'function': check_repo},
668 668 requirements=URL_NAME_REQUIREMENTS)
669 669 rmap.connect('edit_repo_perms_update', '/{repo_name}/settings/permissions',
670 670 controller='admin/repos', action='edit_permissions_update',
671 671 conditions={'method': ['PUT'], 'function': check_repo},
672 672 requirements=URL_NAME_REQUIREMENTS)
673 673
674 674 rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields',
675 675 controller='admin/repos', action='edit_fields',
676 676 conditions={'method': ['GET'], 'function': check_repo},
677 677 requirements=URL_NAME_REQUIREMENTS)
678 678 rmap.connect('create_repo_fields', '/{repo_name}/settings/fields/new',
679 679 controller='admin/repos', action='create_repo_field',
680 680 conditions={'method': ['PUT'], 'function': check_repo},
681 681 requirements=URL_NAME_REQUIREMENTS)
682 682 rmap.connect('delete_repo_fields', '/{repo_name}/settings/fields/{field_id}',
683 683 controller='admin/repos', action='delete_repo_field',
684 684 conditions={'method': ['DELETE'], 'function': check_repo},
685 685 requirements=URL_NAME_REQUIREMENTS)
686 686
687 687 rmap.connect('edit_repo_advanced', '/{repo_name}/settings/advanced',
688 688 controller='admin/repos', action='edit_advanced',
689 689 conditions={'method': ['GET'], 'function': check_repo},
690 690 requirements=URL_NAME_REQUIREMENTS)
691 691
692 692 rmap.connect('edit_repo_advanced_locking', '/{repo_name}/settings/advanced/locking',
693 693 controller='admin/repos', action='edit_advanced_locking',
694 694 conditions={'method': ['PUT'], 'function': check_repo},
695 695 requirements=URL_NAME_REQUIREMENTS)
696 696 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
697 697 controller='admin/repos', action='toggle_locking',
698 698 conditions={'method': ['GET'], 'function': check_repo},
699 699 requirements=URL_NAME_REQUIREMENTS)
700 700
701 701 rmap.connect('edit_repo_advanced_journal', '/{repo_name}/settings/advanced/journal',
702 702 controller='admin/repos', action='edit_advanced_journal',
703 703 conditions={'method': ['PUT'], 'function': check_repo},
704 704 requirements=URL_NAME_REQUIREMENTS)
705 705
706 706 rmap.connect('edit_repo_advanced_fork', '/{repo_name}/settings/advanced/fork',
707 707 controller='admin/repos', action='edit_advanced_fork',
708 708 conditions={'method': ['PUT'], 'function': check_repo},
709 709 requirements=URL_NAME_REQUIREMENTS)
710 710
711 rmap.connect('edit_repo_caches', '/{repo_name}/settings/caches',
712 controller='admin/repos', action='edit_caches_form',
713 conditions={'method': ['GET'], 'function': check_repo},
714 requirements=URL_NAME_REQUIREMENTS)
715 rmap.connect('edit_repo_caches', '/{repo_name}/settings/caches',
716 controller='admin/repos', action='edit_caches',
717 conditions={'method': ['PUT'], 'function': check_repo},
718 requirements=URL_NAME_REQUIREMENTS)
719
720 711 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
721 712 controller='admin/repos', action='edit_remote_form',
722 713 conditions={'method': ['GET'], 'function': check_repo},
723 714 requirements=URL_NAME_REQUIREMENTS)
724 715 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
725 716 controller='admin/repos', action='edit_remote',
726 717 conditions={'method': ['PUT'], 'function': check_repo},
727 718 requirements=URL_NAME_REQUIREMENTS)
728 719
729 720 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
730 721 controller='admin/repos', action='edit_statistics_form',
731 722 conditions={'method': ['GET'], 'function': check_repo},
732 723 requirements=URL_NAME_REQUIREMENTS)
733 724 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
734 725 controller='admin/repos', action='edit_statistics',
735 726 conditions={'method': ['PUT'], 'function': check_repo},
736 727 requirements=URL_NAME_REQUIREMENTS)
737 728 rmap.connect('repo_settings_issuetracker',
738 729 '/{repo_name}/settings/issue-tracker',
739 730 controller='admin/repos', action='repo_issuetracker',
740 731 conditions={'method': ['GET'], 'function': check_repo},
741 732 requirements=URL_NAME_REQUIREMENTS)
742 733 rmap.connect('repo_issuetracker_test',
743 734 '/{repo_name}/settings/issue-tracker/test',
744 735 controller='admin/repos', action='repo_issuetracker_test',
745 736 conditions={'method': ['POST'], 'function': check_repo},
746 737 requirements=URL_NAME_REQUIREMENTS)
747 738 rmap.connect('repo_issuetracker_delete',
748 739 '/{repo_name}/settings/issue-tracker/delete',
749 740 controller='admin/repos', action='repo_issuetracker_delete',
750 741 conditions={'method': ['DELETE'], 'function': check_repo},
751 742 requirements=URL_NAME_REQUIREMENTS)
752 743 rmap.connect('repo_issuetracker_save',
753 744 '/{repo_name}/settings/issue-tracker/save',
754 745 controller='admin/repos', action='repo_issuetracker_save',
755 746 conditions={'method': ['POST'], 'function': check_repo},
756 747 requirements=URL_NAME_REQUIREMENTS)
757 748 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
758 749 controller='admin/repos', action='repo_settings_vcs_update',
759 750 conditions={'method': ['POST'], 'function': check_repo},
760 751 requirements=URL_NAME_REQUIREMENTS)
761 752 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
762 753 controller='admin/repos', action='repo_settings_vcs',
763 754 conditions={'method': ['GET'], 'function': check_repo},
764 755 requirements=URL_NAME_REQUIREMENTS)
765 756 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
766 757 controller='admin/repos', action='repo_delete_svn_pattern',
767 758 conditions={'method': ['DELETE'], 'function': check_repo},
768 759 requirements=URL_NAME_REQUIREMENTS)
769 760 rmap.connect('repo_pullrequest_settings', '/{repo_name}/settings/pullrequest',
770 761 controller='admin/repos', action='repo_settings_pullrequest',
771 762 conditions={'method': ['GET', 'POST'], 'function': check_repo},
772 763 requirements=URL_NAME_REQUIREMENTS)
773 764
774 765 # still working url for backward compat.
775 766 rmap.connect('raw_changeset_home_depraced',
776 767 '/{repo_name}/raw-changeset/{revision}',
777 768 controller='changeset', action='changeset_raw',
778 769 revision='tip', conditions={'function': check_repo},
779 770 requirements=URL_NAME_REQUIREMENTS)
780 771
781 772 # new URLs
782 773 rmap.connect('changeset_raw_home',
783 774 '/{repo_name}/changeset-diff/{revision}',
784 775 controller='changeset', action='changeset_raw',
785 776 revision='tip', conditions={'function': check_repo},
786 777 requirements=URL_NAME_REQUIREMENTS)
787 778
788 779 rmap.connect('changeset_patch_home',
789 780 '/{repo_name}/changeset-patch/{revision}',
790 781 controller='changeset', action='changeset_patch',
791 782 revision='tip', conditions={'function': check_repo},
792 783 requirements=URL_NAME_REQUIREMENTS)
793 784
794 785 rmap.connect('changeset_download_home',
795 786 '/{repo_name}/changeset-download/{revision}',
796 787 controller='changeset', action='changeset_download',
797 788 revision='tip', conditions={'function': check_repo},
798 789 requirements=URL_NAME_REQUIREMENTS)
799 790
800 791 rmap.connect('changeset_comment',
801 792 '/{repo_name}/changeset/{revision}/comment', jsroute=True,
802 793 controller='changeset', revision='tip', action='comment',
803 794 conditions={'function': check_repo},
804 795 requirements=URL_NAME_REQUIREMENTS)
805 796
806 797 rmap.connect('changeset_comment_preview',
807 798 '/{repo_name}/changeset/comment/preview', jsroute=True,
808 799 controller='changeset', action='preview_comment',
809 800 conditions={'function': check_repo, 'method': ['POST']},
810 801 requirements=URL_NAME_REQUIREMENTS)
811 802
812 803 rmap.connect('changeset_comment_delete',
813 804 '/{repo_name}/changeset/comment/{comment_id}/delete',
814 805 controller='changeset', action='delete_comment',
815 806 conditions={'function': check_repo, 'method': ['DELETE']},
816 807 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
817 808
818 809 rmap.connect('changeset_info', '/{repo_name}/changeset_info/{revision}',
819 810 controller='changeset', action='changeset_info',
820 811 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
821 812
822 813 rmap.connect('compare_home',
823 814 '/{repo_name}/compare',
824 815 controller='compare', action='index',
825 816 conditions={'function': check_repo},
826 817 requirements=URL_NAME_REQUIREMENTS)
827 818
828 819 rmap.connect('compare_url',
829 820 '/{repo_name}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}',
830 821 controller='compare', action='compare',
831 822 conditions={'function': check_repo},
832 823 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
833 824
834 825 rmap.connect('pullrequest_home',
835 826 '/{repo_name}/pull-request/new', controller='pullrequests',
836 827 action='index', conditions={'function': check_repo,
837 828 'method': ['GET']},
838 829 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
839 830
840 831 rmap.connect('pullrequest',
841 832 '/{repo_name}/pull-request/new', controller='pullrequests',
842 833 action='create', conditions={'function': check_repo,
843 834 'method': ['POST']},
844 835 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
845 836
846 837 rmap.connect('pullrequest_repo_refs',
847 838 '/{repo_name}/pull-request/refs/{target_repo_name:.*?[^/]}',
848 839 controller='pullrequests',
849 840 action='get_repo_refs',
850 841 conditions={'function': check_repo, 'method': ['GET']},
851 842 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
852 843
853 844 rmap.connect('pullrequest_repo_destinations',
854 845 '/{repo_name}/pull-request/repo-destinations',
855 846 controller='pullrequests',
856 847 action='get_repo_destinations',
857 848 conditions={'function': check_repo, 'method': ['GET']},
858 849 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
859 850
860 851 rmap.connect('pullrequest_show',
861 852 '/{repo_name}/pull-request/{pull_request_id}',
862 853 controller='pullrequests',
863 854 action='show', conditions={'function': check_repo,
864 855 'method': ['GET']},
865 856 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
866 857
867 858 rmap.connect('pullrequest_update',
868 859 '/{repo_name}/pull-request/{pull_request_id}',
869 860 controller='pullrequests',
870 861 action='update', conditions={'function': check_repo,
871 862 'method': ['PUT']},
872 863 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
873 864
874 865 rmap.connect('pullrequest_merge',
875 866 '/{repo_name}/pull-request/{pull_request_id}',
876 867 controller='pullrequests',
877 868 action='merge', conditions={'function': check_repo,
878 869 'method': ['POST']},
879 870 requirements=URL_NAME_REQUIREMENTS)
880 871
881 872 rmap.connect('pullrequest_delete',
882 873 '/{repo_name}/pull-request/{pull_request_id}',
883 874 controller='pullrequests',
884 875 action='delete', conditions={'function': check_repo,
885 876 'method': ['DELETE']},
886 877 requirements=URL_NAME_REQUIREMENTS)
887 878
888 879 rmap.connect('pullrequest_show_all',
889 880 '/{repo_name}/pull-request',
890 881 controller='pullrequests',
891 882 action='show_all', conditions={'function': check_repo,
892 883 'method': ['GET']},
893 884 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
894 885
895 886 rmap.connect('pullrequest_comment',
896 887 '/{repo_name}/pull-request-comment/{pull_request_id}',
897 888 controller='pullrequests',
898 889 action='comment', conditions={'function': check_repo,
899 890 'method': ['POST']},
900 891 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
901 892
902 893 rmap.connect('pullrequest_comment_delete',
903 894 '/{repo_name}/pull-request-comment/{comment_id}/delete',
904 895 controller='pullrequests', action='delete_comment',
905 896 conditions={'function': check_repo, 'method': ['DELETE']},
906 897 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
907 898
908 899 rmap.connect('summary_home_explicit', '/{repo_name}/summary',
909 900 controller='summary', conditions={'function': check_repo},
910 901 requirements=URL_NAME_REQUIREMENTS)
911 902
912 903 rmap.connect('branches_home', '/{repo_name}/branches',
913 904 controller='branches', conditions={'function': check_repo},
914 905 requirements=URL_NAME_REQUIREMENTS)
915 906
916 907 rmap.connect('tags_home', '/{repo_name}/tags',
917 908 controller='tags', conditions={'function': check_repo},
918 909 requirements=URL_NAME_REQUIREMENTS)
919 910
920 911 rmap.connect('bookmarks_home', '/{repo_name}/bookmarks',
921 912 controller='bookmarks', conditions={'function': check_repo},
922 913 requirements=URL_NAME_REQUIREMENTS)
923 914
924 915 rmap.connect('changelog_home', '/{repo_name}/changelog', jsroute=True,
925 916 controller='changelog', conditions={'function': check_repo},
926 917 requirements=URL_NAME_REQUIREMENTS)
927 918
928 919 rmap.connect('changelog_summary_home', '/{repo_name}/changelog_summary',
929 920 controller='changelog', action='changelog_summary',
930 921 conditions={'function': check_repo},
931 922 requirements=URL_NAME_REQUIREMENTS)
932 923
933 924 rmap.connect('changelog_file_home',
934 925 '/{repo_name}/changelog/{revision}/{f_path}',
935 926 controller='changelog', f_path=None,
936 927 conditions={'function': check_repo},
937 928 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
938 929
939 930 rmap.connect('changelog_elements', '/{repo_name}/changelog_details',
940 931 controller='changelog', action='changelog_elements',
941 932 conditions={'function': check_repo},
942 933 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
943 934
944 935 rmap.connect('files_home', '/{repo_name}/files/{revision}/{f_path}',
945 936 controller='files', revision='tip', f_path='',
946 937 conditions={'function': check_repo},
947 938 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
948 939
949 940 rmap.connect('files_home_simple_catchrev',
950 941 '/{repo_name}/files/{revision}',
951 942 controller='files', revision='tip', f_path='',
952 943 conditions={'function': check_repo},
953 944 requirements=URL_NAME_REQUIREMENTS)
954 945
955 946 rmap.connect('files_home_simple_catchall',
956 947 '/{repo_name}/files',
957 948 controller='files', revision='tip', f_path='',
958 949 conditions={'function': check_repo},
959 950 requirements=URL_NAME_REQUIREMENTS)
960 951
961 952 rmap.connect('files_history_home',
962 953 '/{repo_name}/history/{revision}/{f_path}',
963 954 controller='files', action='history', revision='tip', f_path='',
964 955 conditions={'function': check_repo},
965 956 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
966 957
967 958 rmap.connect('files_authors_home',
968 959 '/{repo_name}/authors/{revision}/{f_path}',
969 960 controller='files', action='authors', revision='tip', f_path='',
970 961 conditions={'function': check_repo},
971 962 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
972 963
973 964 rmap.connect('files_diff_home', '/{repo_name}/diff/{f_path}',
974 965 controller='files', action='diff', f_path='',
975 966 conditions={'function': check_repo},
976 967 requirements=URL_NAME_REQUIREMENTS)
977 968
978 969 rmap.connect('files_diff_2way_home',
979 970 '/{repo_name}/diff-2way/{f_path}',
980 971 controller='files', action='diff_2way', f_path='',
981 972 conditions={'function': check_repo},
982 973 requirements=URL_NAME_REQUIREMENTS)
983 974
984 975 rmap.connect('files_rawfile_home',
985 976 '/{repo_name}/rawfile/{revision}/{f_path}',
986 977 controller='files', action='rawfile', revision='tip',
987 978 f_path='', conditions={'function': check_repo},
988 979 requirements=URL_NAME_REQUIREMENTS)
989 980
990 981 rmap.connect('files_raw_home',
991 982 '/{repo_name}/raw/{revision}/{f_path}',
992 983 controller='files', action='raw', revision='tip', f_path='',
993 984 conditions={'function': check_repo},
994 985 requirements=URL_NAME_REQUIREMENTS)
995 986
996 987 rmap.connect('files_render_home',
997 988 '/{repo_name}/render/{revision}/{f_path}',
998 989 controller='files', action='index', revision='tip', f_path='',
999 990 rendered=True, conditions={'function': check_repo},
1000 991 requirements=URL_NAME_REQUIREMENTS)
1001 992
1002 993 rmap.connect('files_annotate_home',
1003 994 '/{repo_name}/annotate/{revision}/{f_path}',
1004 995 controller='files', action='index', revision='tip',
1005 996 f_path='', annotate=True, conditions={'function': check_repo},
1006 997 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1007 998
1008 999 rmap.connect('files_annotate_previous',
1009 1000 '/{repo_name}/annotate-previous/{revision}/{f_path}',
1010 1001 controller='files', action='annotate_previous', revision='tip',
1011 1002 f_path='', annotate=True, conditions={'function': check_repo},
1012 1003 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1013 1004
1014 1005 rmap.connect('files_edit',
1015 1006 '/{repo_name}/edit/{revision}/{f_path}',
1016 1007 controller='files', action='edit', revision='tip',
1017 1008 f_path='',
1018 1009 conditions={'function': check_repo, 'method': ['POST']},
1019 1010 requirements=URL_NAME_REQUIREMENTS)
1020 1011
1021 1012 rmap.connect('files_edit_home',
1022 1013 '/{repo_name}/edit/{revision}/{f_path}',
1023 1014 controller='files', action='edit_home', revision='tip',
1024 1015 f_path='', conditions={'function': check_repo},
1025 1016 requirements=URL_NAME_REQUIREMENTS)
1026 1017
1027 1018 rmap.connect('files_add',
1028 1019 '/{repo_name}/add/{revision}/{f_path}',
1029 1020 controller='files', action='add', revision='tip',
1030 1021 f_path='',
1031 1022 conditions={'function': check_repo, 'method': ['POST']},
1032 1023 requirements=URL_NAME_REQUIREMENTS)
1033 1024
1034 1025 rmap.connect('files_add_home',
1035 1026 '/{repo_name}/add/{revision}/{f_path}',
1036 1027 controller='files', action='add_home', revision='tip',
1037 1028 f_path='', conditions={'function': check_repo},
1038 1029 requirements=URL_NAME_REQUIREMENTS)
1039 1030
1040 1031 rmap.connect('files_delete',
1041 1032 '/{repo_name}/delete/{revision}/{f_path}',
1042 1033 controller='files', action='delete', revision='tip',
1043 1034 f_path='',
1044 1035 conditions={'function': check_repo, 'method': ['POST']},
1045 1036 requirements=URL_NAME_REQUIREMENTS)
1046 1037
1047 1038 rmap.connect('files_delete_home',
1048 1039 '/{repo_name}/delete/{revision}/{f_path}',
1049 1040 controller='files', action='delete_home', revision='tip',
1050 1041 f_path='', conditions={'function': check_repo},
1051 1042 requirements=URL_NAME_REQUIREMENTS)
1052 1043
1053 1044 rmap.connect('files_archive_home', '/{repo_name}/archive/{fname}',
1054 1045 controller='files', action='archivefile',
1055 1046 conditions={'function': check_repo},
1056 1047 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1057 1048
1058 1049 rmap.connect('files_nodelist_home',
1059 1050 '/{repo_name}/nodelist/{revision}/{f_path}',
1060 1051 controller='files', action='nodelist',
1061 1052 conditions={'function': check_repo},
1062 1053 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1063 1054
1064 1055 rmap.connect('files_nodetree_full',
1065 1056 '/{repo_name}/nodetree_full/{commit_id}/{f_path}',
1066 1057 controller='files', action='nodetree_full',
1067 1058 conditions={'function': check_repo},
1068 1059 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1069 1060
1070 1061 rmap.connect('repo_fork_create_home', '/{repo_name}/fork',
1071 1062 controller='forks', action='fork_create',
1072 1063 conditions={'function': check_repo, 'method': ['POST']},
1073 1064 requirements=URL_NAME_REQUIREMENTS)
1074 1065
1075 1066 rmap.connect('repo_fork_home', '/{repo_name}/fork',
1076 1067 controller='forks', action='fork',
1077 1068 conditions={'function': check_repo},
1078 1069 requirements=URL_NAME_REQUIREMENTS)
1079 1070
1080 1071 rmap.connect('repo_forks_home', '/{repo_name}/forks',
1081 1072 controller='forks', action='forks',
1082 1073 conditions={'function': check_repo},
1083 1074 requirements=URL_NAME_REQUIREMENTS)
1084 1075
1085 1076 # must be here for proper group/repo catching pattern
1086 1077 _connect_with_slash(
1087 1078 rmap, 'repo_group_home', '/{group_name}',
1088 1079 controller='home', action='index_repo_group',
1089 1080 conditions={'function': check_group},
1090 1081 requirements=URL_NAME_REQUIREMENTS)
1091 1082
1092 1083 # catch all, at the end
1093 1084 _connect_with_slash(
1094 1085 rmap, 'summary_home', '/{repo_name}', jsroute=True,
1095 1086 controller='summary', action='index',
1096 1087 conditions={'function': check_repo},
1097 1088 requirements=URL_NAME_REQUIREMENTS)
1098 1089
1099 1090 return rmap
1100 1091
1101 1092
1102 1093 def _connect_with_slash(mapper, name, path, *args, **kwargs):
1103 1094 """
1104 1095 Connect a route with an optional trailing slash in `path`.
1105 1096 """
1106 1097 mapper.connect(name + '_slash', path + '/', *args, **kwargs)
1107 1098 mapper.connect(name, path, *args, **kwargs)
@@ -1,805 +1,781 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2013-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21
22 22 """
23 23 Repositories controller for RhodeCode
24 24 """
25 25
26 26 import logging
27 27 import traceback
28 28
29 29 import formencode
30 30 from formencode import htmlfill
31 31 from pylons import request, tmpl_context as c, url
32 32 from pylons.controllers.util import redirect
33 33 from pylons.i18n.translation import _
34 34 from webob.exc import HTTPForbidden, HTTPNotFound, HTTPBadRequest
35 35
36 36 import rhodecode
37 37 from rhodecode.lib import auth, helpers as h
38 38 from rhodecode.lib.auth import (
39 39 LoginRequired, HasPermissionAllDecorator,
40 40 HasRepoPermissionAllDecorator, NotAnonymous, HasPermissionAny,
41 41 HasRepoGroupPermissionAny, HasRepoPermissionAnyDecorator)
42 42 from rhodecode.lib.base import BaseRepoController, render
43 43 from rhodecode.lib.ext_json import json
44 44 from rhodecode.lib.exceptions import AttachedForksError
45 45 from rhodecode.lib.utils import action_logger, repo_name_slug, jsonify
46 46 from rhodecode.lib.utils2 import safe_int, str2bool
47 47 from rhodecode.lib.vcs import RepositoryError
48 48 from rhodecode.model.db import (
49 49 User, Repository, UserFollowing, RepoGroup, RepositoryField)
50 50 from rhodecode.model.forms import (
51 51 RepoForm, RepoFieldForm, RepoPermsForm, RepoVcsSettingsForm,
52 52 IssueTrackerPatternsForm)
53 53 from rhodecode.model.meta import Session
54 54 from rhodecode.model.repo import RepoModel
55 55 from rhodecode.model.scm import ScmModel, RepoGroupList, RepoList
56 56 from rhodecode.model.settings import (
57 57 SettingsModel, IssueTrackerSettingsModel, VcsSettingsModel,
58 58 SettingNotFound)
59 59
60 60 log = logging.getLogger(__name__)
61 61
62 62
63 63 class ReposController(BaseRepoController):
64 64 """
65 65 REST Controller styled on the Atom Publishing Protocol"""
66 66 # To properly map this controller, ensure your config/routing.py
67 67 # file has a resource setup:
68 68 # map.resource('repo', 'repos')
69 69
70 70 @LoginRequired()
71 71 def __before__(self):
72 72 super(ReposController, self).__before__()
73 73
74 74 def _load_repo(self, repo_name):
75 75 repo_obj = Repository.get_by_repo_name(repo_name)
76 76
77 77 if repo_obj is None:
78 78 h.not_mapped_error(repo_name)
79 79 return redirect(url('repos'))
80 80
81 81 return repo_obj
82 82
83 83 def __load_defaults(self, repo=None):
84 84 acl_groups = RepoGroupList(RepoGroup.query().all(),
85 85 perm_set=['group.write', 'group.admin'])
86 86 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
87 87 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
88 88
89 89 # in case someone no longer have a group.write access to a repository
90 90 # pre fill the list with this entry, we don't care if this is the same
91 91 # but it will allow saving repo data properly.
92 92
93 93 repo_group = None
94 94 if repo:
95 95 repo_group = repo.group
96 96 if repo_group and unicode(repo_group.group_id) not in c.repo_groups_choices:
97 97 c.repo_groups_choices.append(unicode(repo_group.group_id))
98 98 c.repo_groups.append(RepoGroup._generate_choice(repo_group))
99 99
100 100 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
101 101 c.landing_revs_choices = choices
102 102
103 103 def __load_data(self, repo_name=None):
104 104 """
105 105 Load defaults settings for edit, and update
106 106
107 107 :param repo_name:
108 108 """
109 109 c.repo_info = self._load_repo(repo_name)
110 110 self.__load_defaults(c.repo_info)
111 111
112 112 # override defaults for exact repo info here git/hg etc
113 113 if not c.repository_requirements_missing:
114 114 choices, c.landing_revs = ScmModel().get_repo_landing_revs(
115 115 c.repo_info)
116 116 c.landing_revs_choices = choices
117 117 defaults = RepoModel()._get_defaults(repo_name)
118 118
119 119 return defaults
120 120
121 121 def _log_creation_exception(self, e, repo_name):
122 122 reason = None
123 123 if len(e.args) == 2:
124 124 reason = e.args[1]
125 125
126 126 if reason == 'INVALID_CERTIFICATE':
127 127 log.exception(
128 128 'Exception creating a repository: invalid certificate')
129 129 msg = (_('Error creating repository %s: invalid certificate')
130 130 % repo_name)
131 131 else:
132 132 log.exception("Exception creating a repository")
133 133 msg = (_('Error creating repository %s')
134 134 % repo_name)
135 135
136 136 return msg
137 137
138 138 @NotAnonymous()
139 139 def index(self, format='html'):
140 140 """GET /repos: All items in the collection"""
141 141 # url('repos')
142 142
143 143 repo_list = Repository.get_all_repos()
144 144 c.repo_list = RepoList(repo_list, perm_set=['repository.admin'])
145 145 repos_data = RepoModel().get_repos_as_dict(
146 146 repo_list=c.repo_list, admin=True, super_user_actions=True)
147 147 # json used to render the grid
148 148 c.data = json.dumps(repos_data)
149 149
150 150 return render('admin/repos/repos.mako')
151 151
152 152 # perms check inside
153 153 @NotAnonymous()
154 154 @auth.CSRFRequired()
155 155 def create(self):
156 156 """
157 157 POST /repos: Create a new item"""
158 158 # url('repos')
159 159
160 160 self.__load_defaults()
161 161 form_result = {}
162 162 task_id = None
163 163 c.personal_repo_group = c.rhodecode_user.personal_repo_group
164 164 try:
165 165 # CanWriteToGroup validators checks permissions of this POST
166 166 form_result = RepoForm(repo_groups=c.repo_groups_choices,
167 167 landing_revs=c.landing_revs_choices)()\
168 168 .to_python(dict(request.POST))
169 169
170 170 # create is done sometimes async on celery, db transaction
171 171 # management is handled there.
172 172 task = RepoModel().create(form_result, c.rhodecode_user.user_id)
173 173 from celery.result import BaseAsyncResult
174 174 if isinstance(task, BaseAsyncResult):
175 175 task_id = task.task_id
176 176 except formencode.Invalid as errors:
177 177 return htmlfill.render(
178 178 render('admin/repos/repo_add.mako'),
179 179 defaults=errors.value,
180 180 errors=errors.error_dict or {},
181 181 prefix_error=False,
182 182 encoding="UTF-8",
183 183 force_defaults=False)
184 184
185 185 except Exception as e:
186 186 msg = self._log_creation_exception(e, form_result.get('repo_name'))
187 187 h.flash(msg, category='error')
188 188 return redirect(url('home'))
189 189
190 190 return redirect(h.url('repo_creating_home',
191 191 repo_name=form_result['repo_name_full'],
192 192 task_id=task_id))
193 193
194 194 # perms check inside
195 195 @NotAnonymous()
196 196 def create_repository(self):
197 197 """GET /_admin/create_repository: Form to create a new item"""
198 198 new_repo = request.GET.get('repo', '')
199 199 parent_group = safe_int(request.GET.get('parent_group'))
200 200 _gr = RepoGroup.get(parent_group)
201 201
202 202 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
203 203 # you're not super admin nor have global create permissions,
204 204 # but maybe you have at least write permission to a parent group ?
205 205
206 206 gr_name = _gr.group_name if _gr else None
207 207 # create repositories with write permission on group is set to true
208 208 create_on_write = HasPermissionAny('hg.create.write_on_repogroup.true')()
209 209 group_admin = HasRepoGroupPermissionAny('group.admin')(group_name=gr_name)
210 210 group_write = HasRepoGroupPermissionAny('group.write')(group_name=gr_name)
211 211 if not (group_admin or (group_write and create_on_write)):
212 212 raise HTTPForbidden
213 213
214 214 acl_groups = RepoGroupList(RepoGroup.query().all(),
215 215 perm_set=['group.write', 'group.admin'])
216 216 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
217 217 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
218 218 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
219 219 c.personal_repo_group = c.rhodecode_user.personal_repo_group
220 220 c.new_repo = repo_name_slug(new_repo)
221 221
222 222 # apply the defaults from defaults page
223 223 defaults = SettingsModel().get_default_repo_settings(strip_prefix=True)
224 224 # set checkbox to autochecked
225 225 defaults['repo_copy_permissions'] = True
226 226
227 227 parent_group_choice = '-1'
228 228 if not c.rhodecode_user.is_admin and c.rhodecode_user.personal_repo_group:
229 229 parent_group_choice = c.rhodecode_user.personal_repo_group
230 230
231 231 if parent_group and _gr:
232 232 if parent_group in [x[0] for x in c.repo_groups]:
233 233 parent_group_choice = unicode(parent_group)
234 234
235 235 defaults.update({'repo_group': parent_group_choice})
236 236
237 237 return htmlfill.render(
238 238 render('admin/repos/repo_add.mako'),
239 239 defaults=defaults,
240 240 errors={},
241 241 prefix_error=False,
242 242 encoding="UTF-8",
243 243 force_defaults=False
244 244 )
245 245
246 246 @NotAnonymous()
247 247 def repo_creating(self, repo_name):
248 248 c.repo = repo_name
249 249 c.task_id = request.GET.get('task_id')
250 250 if not c.repo:
251 251 raise HTTPNotFound()
252 252 return render('admin/repos/repo_creating.mako')
253 253
254 254 @NotAnonymous()
255 255 @jsonify
256 256 def repo_check(self, repo_name):
257 257 c.repo = repo_name
258 258 task_id = request.GET.get('task_id')
259 259
260 260 if task_id and task_id not in ['None']:
261 261 import rhodecode
262 262 from celery.result import AsyncResult
263 263 if rhodecode.CELERY_ENABLED:
264 264 task = AsyncResult(task_id)
265 265 if task.failed():
266 266 msg = self._log_creation_exception(task.result, c.repo)
267 267 h.flash(msg, category='error')
268 268 return redirect(url('home'), code=501)
269 269
270 270 repo = Repository.get_by_repo_name(repo_name)
271 271 if repo and repo.repo_state == Repository.STATE_CREATED:
272 272 if repo.clone_uri:
273 273 clone_uri = repo.clone_uri_hidden
274 274 h.flash(_('Created repository %s from %s')
275 275 % (repo.repo_name, clone_uri), category='success')
276 276 else:
277 277 repo_url = h.link_to(repo.repo_name,
278 278 h.url('summary_home',
279 279 repo_name=repo.repo_name))
280 280 fork = repo.fork
281 281 if fork:
282 282 fork_name = fork.repo_name
283 283 h.flash(h.literal(_('Forked repository %s as %s')
284 284 % (fork_name, repo_url)), category='success')
285 285 else:
286 286 h.flash(h.literal(_('Created repository %s') % repo_url),
287 287 category='success')
288 288 return {'result': True}
289 289 return {'result': False}
290 290
291 291 @HasRepoPermissionAllDecorator('repository.admin')
292 292 @auth.CSRFRequired()
293 293 def delete(self, repo_name):
294 294 """
295 295 DELETE /repos/repo_name: Delete an existing item"""
296 296 # Forms posted to this method should contain a hidden field:
297 297 # <input type="hidden" name="_method" value="DELETE" />
298 298 # Or using helpers:
299 299 # h.form(url('repo', repo_name=ID),
300 300 # method='delete')
301 301 # url('repo', repo_name=ID)
302 302
303 303 repo_model = RepoModel()
304 304 repo = repo_model.get_by_repo_name(repo_name)
305 305 if not repo:
306 306 h.not_mapped_error(repo_name)
307 307 return redirect(url('repos'))
308 308 try:
309 309 _forks = repo.forks.count()
310 310 handle_forks = None
311 311 if _forks and request.POST.get('forks'):
312 312 do = request.POST['forks']
313 313 if do == 'detach_forks':
314 314 handle_forks = 'detach'
315 315 h.flash(_('Detached %s forks') % _forks, category='success')
316 316 elif do == 'delete_forks':
317 317 handle_forks = 'delete'
318 318 h.flash(_('Deleted %s forks') % _forks, category='success')
319 319 repo_model.delete(repo, forks=handle_forks)
320 320 action_logger(c.rhodecode_user, 'admin_deleted_repo',
321 321 repo_name, self.ip_addr, self.sa)
322 322 ScmModel().mark_for_invalidation(repo_name)
323 323 h.flash(_('Deleted repository %s') % repo_name, category='success')
324 324 Session().commit()
325 325 except AttachedForksError:
326 326 h.flash(_('Cannot delete %s it still contains attached forks')
327 327 % repo_name, category='warning')
328 328
329 329 except Exception:
330 330 log.exception("Exception during deletion of repository")
331 331 h.flash(_('An error occurred during deletion of %s') % repo_name,
332 332 category='error')
333 333
334 334 return redirect(url('repos'))
335 335
336 336 @HasPermissionAllDecorator('hg.admin')
337 337 def show(self, repo_name, format='html'):
338 338 """GET /repos/repo_name: Show a specific item"""
339 339 # url('repo', repo_name=ID)
340 340
341 341 @HasRepoPermissionAllDecorator('repository.admin')
342 342 def edit_permissions(self, repo_name):
343 343 """GET /repo_name/settings: Form to edit an existing item"""
344 344 c.repo_info = self._load_repo(repo_name)
345 345 c.active = 'permissions'
346 346 defaults = RepoModel()._get_defaults(repo_name)
347 347
348 348 return htmlfill.render(
349 349 render('admin/repos/repo_edit.mako'),
350 350 defaults=defaults,
351 351 encoding="UTF-8",
352 352 force_defaults=False)
353 353
354 354 @HasRepoPermissionAllDecorator('repository.admin')
355 355 @auth.CSRFRequired()
356 356 def edit_permissions_update(self, repo_name):
357 357 form = RepoPermsForm()().to_python(request.POST)
358 358 RepoModel().update_permissions(repo_name,
359 359 form['perm_additions'], form['perm_updates'], form['perm_deletions'])
360 360
361 361 #TODO: implement this
362 362 #action_logger(c.rhodecode_user, 'admin_changed_repo_permissions',
363 363 # repo_name, self.ip_addr, self.sa)
364 364 Session().commit()
365 365 h.flash(_('Repository permissions updated'), category='success')
366 366 return redirect(url('edit_repo_perms', repo_name=repo_name))
367 367
368 368 @HasRepoPermissionAllDecorator('repository.admin')
369 369 def edit_fields(self, repo_name):
370 370 """GET /repo_name/settings: Form to edit an existing item"""
371 371 c.repo_info = self._load_repo(repo_name)
372 372 c.repo_fields = RepositoryField.query()\
373 373 .filter(RepositoryField.repository == c.repo_info).all()
374 374 c.active = 'fields'
375 375 if request.POST:
376 376
377 377 return redirect(url('repo_edit_fields'))
378 378 return render('admin/repos/repo_edit.mako')
379 379
380 380 @HasRepoPermissionAllDecorator('repository.admin')
381 381 @auth.CSRFRequired()
382 382 def create_repo_field(self, repo_name):
383 383 try:
384 384 form_result = RepoFieldForm()().to_python(dict(request.POST))
385 385 RepoModel().add_repo_field(
386 386 repo_name, form_result['new_field_key'],
387 387 field_type=form_result['new_field_type'],
388 388 field_value=form_result['new_field_value'],
389 389 field_label=form_result['new_field_label'],
390 390 field_desc=form_result['new_field_desc'])
391 391
392 392 Session().commit()
393 393 except Exception as e:
394 394 log.exception("Exception creating field")
395 395 msg = _('An error occurred during creation of field')
396 396 if isinstance(e, formencode.Invalid):
397 397 msg += ". " + e.msg
398 398 h.flash(msg, category='error')
399 399 return redirect(url('edit_repo_fields', repo_name=repo_name))
400 400
401 401 @HasRepoPermissionAllDecorator('repository.admin')
402 402 @auth.CSRFRequired()
403 403 def delete_repo_field(self, repo_name, field_id):
404 404 field = RepositoryField.get_or_404(field_id)
405 405 try:
406 406 RepoModel().delete_repo_field(repo_name, field.field_key)
407 407 Session().commit()
408 408 except Exception as e:
409 409 log.exception("Exception during removal of field")
410 410 msg = _('An error occurred during removal of field')
411 411 h.flash(msg, category='error')
412 412 return redirect(url('edit_repo_fields', repo_name=repo_name))
413 413
414 414 @HasRepoPermissionAllDecorator('repository.admin')
415 415 def edit_advanced(self, repo_name):
416 416 """GET /repo_name/settings: Form to edit an existing item"""
417 417 c.repo_info = self._load_repo(repo_name)
418 418 c.default_user_id = User.get_default_user().user_id
419 419 c.in_public_journal = UserFollowing.query()\
420 420 .filter(UserFollowing.user_id == c.default_user_id)\
421 421 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
422 422
423 423 c.active = 'advanced'
424 424 c.has_origin_repo_read_perm = False
425 425 if c.repo_info.fork:
426 426 c.has_origin_repo_read_perm = h.HasRepoPermissionAny(
427 427 'repository.write', 'repository.read', 'repository.admin')(
428 428 c.repo_info.fork.repo_name, 'repo set as fork page')
429 429
430 430 if request.POST:
431 431 return redirect(url('repo_edit_advanced'))
432 432 return render('admin/repos/repo_edit.mako')
433 433
434 434 @HasRepoPermissionAllDecorator('repository.admin')
435 435 @auth.CSRFRequired()
436 436 def edit_advanced_journal(self, repo_name):
437 437 """
438 438 Set's this repository to be visible in public journal,
439 439 in other words assing default user to follow this repo
440 440
441 441 :param repo_name:
442 442 """
443 443
444 444 try:
445 445 repo_id = Repository.get_by_repo_name(repo_name).repo_id
446 446 user_id = User.get_default_user().user_id
447 447 self.scm_model.toggle_following_repo(repo_id, user_id)
448 448 h.flash(_('Updated repository visibility in public journal'),
449 449 category='success')
450 450 Session().commit()
451 451 except Exception:
452 452 h.flash(_('An error occurred during setting this'
453 453 ' repository in public journal'),
454 454 category='error')
455 455
456 456 return redirect(url('edit_repo_advanced', repo_name=repo_name))
457 457
458 458 @HasRepoPermissionAllDecorator('repository.admin')
459 459 @auth.CSRFRequired()
460 460 def edit_advanced_fork(self, repo_name):
461 461 """
462 462 Mark given repository as a fork of another
463 463
464 464 :param repo_name:
465 465 """
466 466
467 467 new_fork_id = request.POST.get('id_fork_of')
468 468 try:
469 469
470 470 if new_fork_id and not new_fork_id.isdigit():
471 471 log.error('Given fork id %s is not an INT', new_fork_id)
472 472
473 473 fork_id = safe_int(new_fork_id)
474 474 repo = ScmModel().mark_as_fork(repo_name, fork_id,
475 475 c.rhodecode_user.username)
476 476 fork = repo.fork.repo_name if repo.fork else _('Nothing')
477 477 Session().commit()
478 478 h.flash(_('Marked repo %s as fork of %s') % (repo_name, fork),
479 479 category='success')
480 480 except RepositoryError as e:
481 481 log.exception("Repository Error occurred")
482 482 h.flash(str(e), category='error')
483 483 except Exception as e:
484 484 log.exception("Exception while editing fork")
485 485 h.flash(_('An error occurred during this operation'),
486 486 category='error')
487 487
488 488 return redirect(url('edit_repo_advanced', repo_name=repo_name))
489 489
490 490 @HasRepoPermissionAllDecorator('repository.admin')
491 491 @auth.CSRFRequired()
492 492 def edit_advanced_locking(self, repo_name):
493 493 """
494 494 Unlock repository when it is locked !
495 495
496 496 :param repo_name:
497 497 """
498 498 try:
499 499 repo = Repository.get_by_repo_name(repo_name)
500 500 if request.POST.get('set_lock'):
501 501 Repository.lock(repo, c.rhodecode_user.user_id,
502 502 lock_reason=Repository.LOCK_WEB)
503 503 h.flash(_('Locked repository'), category='success')
504 504 elif request.POST.get('set_unlock'):
505 505 Repository.unlock(repo)
506 506 h.flash(_('Unlocked repository'), category='success')
507 507 except Exception as e:
508 508 log.exception("Exception during unlocking")
509 509 h.flash(_('An error occurred during unlocking'),
510 510 category='error')
511 511 return redirect(url('edit_repo_advanced', repo_name=repo_name))
512 512
513 513 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
514 514 @auth.CSRFRequired()
515 515 def toggle_locking(self, repo_name):
516 516 """
517 517 Toggle locking of repository by simple GET call to url
518 518
519 519 :param repo_name:
520 520 """
521 521
522 522 try:
523 523 repo = Repository.get_by_repo_name(repo_name)
524 524
525 525 if repo.enable_locking:
526 526 if repo.locked[0]:
527 527 Repository.unlock(repo)
528 528 action = _('Unlocked')
529 529 else:
530 530 Repository.lock(repo, c.rhodecode_user.user_id,
531 531 lock_reason=Repository.LOCK_WEB)
532 532 action = _('Locked')
533 533
534 534 h.flash(_('Repository has been %s') % action,
535 535 category='success')
536 536 except Exception:
537 537 log.exception("Exception during unlocking")
538 538 h.flash(_('An error occurred during unlocking'),
539 539 category='error')
540 540 return redirect(url('summary_home', repo_name=repo_name))
541 541
542 542 @HasRepoPermissionAllDecorator('repository.admin')
543 543 @auth.CSRFRequired()
544 def edit_caches(self, repo_name):
545 """PUT /{repo_name}/settings/caches: invalidate the repo caches."""
546 try:
547 ScmModel().mark_for_invalidation(repo_name, delete=True)
548 Session().commit()
549 h.flash(_('Cache invalidation successful'),
550 category='success')
551 except Exception:
552 log.exception("Exception during cache invalidation")
553 h.flash(_('An error occurred during cache invalidation'),
554 category='error')
555
556 return redirect(url('edit_repo_caches', repo_name=c.repo_name))
557
558 @HasRepoPermissionAllDecorator('repository.admin')
559 def edit_caches_form(self, repo_name):
560 """GET /repo_name/settings: Form to edit an existing item"""
561 c.repo_info = self._load_repo(repo_name)
562 c.active = 'caches'
563
564 return render('admin/repos/repo_edit.mako')
565
566 @HasRepoPermissionAllDecorator('repository.admin')
567 @auth.CSRFRequired()
568 544 def edit_remote(self, repo_name):
569 545 """PUT /{repo_name}/settings/remote: edit the repo remote."""
570 546 try:
571 547 ScmModel().pull_changes(repo_name, c.rhodecode_user.username)
572 548 h.flash(_('Pulled from remote location'), category='success')
573 549 except Exception:
574 550 log.exception("Exception during pull from remote")
575 551 h.flash(_('An error occurred during pull from remote location'),
576 552 category='error')
577 553 return redirect(url('edit_repo_remote', repo_name=c.repo_name))
578 554
579 555 @HasRepoPermissionAllDecorator('repository.admin')
580 556 def edit_remote_form(self, repo_name):
581 557 """GET /repo_name/settings: Form to edit an existing item"""
582 558 c.repo_info = self._load_repo(repo_name)
583 559 c.active = 'remote'
584 560
585 561 return render('admin/repos/repo_edit.mako')
586 562
587 563 @HasRepoPermissionAllDecorator('repository.admin')
588 564 @auth.CSRFRequired()
589 565 def edit_statistics(self, repo_name):
590 566 """PUT /{repo_name}/settings/statistics: reset the repo statistics."""
591 567 try:
592 568 RepoModel().delete_stats(repo_name)
593 569 Session().commit()
594 570 except Exception as e:
595 571 log.error(traceback.format_exc())
596 572 h.flash(_('An error occurred during deletion of repository stats'),
597 573 category='error')
598 574 return redirect(url('edit_repo_statistics', repo_name=c.repo_name))
599 575
600 576 @HasRepoPermissionAllDecorator('repository.admin')
601 577 def edit_statistics_form(self, repo_name):
602 578 """GET /repo_name/settings: Form to edit an existing item"""
603 579 c.repo_info = self._load_repo(repo_name)
604 580 repo = c.repo_info.scm_instance()
605 581
606 582 if c.repo_info.stats:
607 583 # this is on what revision we ended up so we add +1 for count
608 584 last_rev = c.repo_info.stats.stat_on_revision + 1
609 585 else:
610 586 last_rev = 0
611 587 c.stats_revision = last_rev
612 588
613 589 c.repo_last_rev = repo.count()
614 590
615 591 if last_rev == 0 or c.repo_last_rev == 0:
616 592 c.stats_percentage = 0
617 593 else:
618 594 c.stats_percentage = '%.2f' % ((float((last_rev)) / c.repo_last_rev) * 100)
619 595
620 596 c.active = 'statistics'
621 597
622 598 return render('admin/repos/repo_edit.mako')
623 599
624 600 @HasRepoPermissionAllDecorator('repository.admin')
625 601 @auth.CSRFRequired()
626 602 def repo_issuetracker_test(self, repo_name):
627 603 if request.is_xhr:
628 604 return h.urlify_commit_message(
629 605 request.POST.get('test_text', ''),
630 606 repo_name)
631 607 else:
632 608 raise HTTPBadRequest()
633 609
634 610 @HasRepoPermissionAllDecorator('repository.admin')
635 611 @auth.CSRFRequired()
636 612 def repo_issuetracker_delete(self, repo_name):
637 613 uid = request.POST.get('uid')
638 614 repo_settings = IssueTrackerSettingsModel(repo=repo_name)
639 615 try:
640 616 repo_settings.delete_entries(uid)
641 617 except Exception:
642 618 h.flash(_('Error occurred during deleting issue tracker entry'),
643 619 category='error')
644 620 else:
645 621 h.flash(_('Removed issue tracker entry'), category='success')
646 622 return redirect(url('repo_settings_issuetracker',
647 623 repo_name=repo_name))
648 624
649 625 def _update_patterns(self, form, repo_settings):
650 626 for uid in form['delete_patterns']:
651 627 repo_settings.delete_entries(uid)
652 628
653 629 for pattern in form['patterns']:
654 630 for setting, value, type_ in pattern:
655 631 sett = repo_settings.create_or_update_setting(
656 632 setting, value, type_)
657 633 Session().add(sett)
658 634
659 635 Session().commit()
660 636
661 637 @HasRepoPermissionAllDecorator('repository.admin')
662 638 @auth.CSRFRequired()
663 639 def repo_issuetracker_save(self, repo_name):
664 640 # Save inheritance
665 641 repo_settings = IssueTrackerSettingsModel(repo=repo_name)
666 642 inherited = (request.POST.get('inherit_global_issuetracker')
667 643 == "inherited")
668 644 repo_settings.inherit_global_settings = inherited
669 645 Session().commit()
670 646
671 647 form = IssueTrackerPatternsForm()().to_python(request.POST)
672 648 if form:
673 649 self._update_patterns(form, repo_settings)
674 650
675 651 h.flash(_('Updated issue tracker entries'), category='success')
676 652 return redirect(url('repo_settings_issuetracker',
677 653 repo_name=repo_name))
678 654
679 655 @HasRepoPermissionAllDecorator('repository.admin')
680 656 def repo_issuetracker(self, repo_name):
681 657 """GET /admin/settings/issue-tracker: All items in the collection"""
682 658 c.active = 'issuetracker'
683 659 c.data = 'data'
684 660 c.repo_info = self._load_repo(repo_name)
685 661
686 662 repo = Repository.get_by_repo_name(repo_name)
687 663 c.settings_model = IssueTrackerSettingsModel(repo=repo)
688 664 c.global_patterns = c.settings_model.get_global_settings()
689 665 c.repo_patterns = c.settings_model.get_repo_settings()
690 666
691 667 return render('admin/repos/repo_edit.mako')
692 668
693 669 @HasRepoPermissionAllDecorator('repository.admin')
694 670 def repo_settings_vcs(self, repo_name):
695 671 """GET /{repo_name}/settings/vcs/: All items in the collection"""
696 672
697 673 model = VcsSettingsModel(repo=repo_name)
698 674
699 675 c.active = 'vcs'
700 676 c.global_svn_branch_patterns = model.get_global_svn_branch_patterns()
701 677 c.global_svn_tag_patterns = model.get_global_svn_tag_patterns()
702 678 c.svn_branch_patterns = model.get_repo_svn_branch_patterns()
703 679 c.svn_tag_patterns = model.get_repo_svn_tag_patterns()
704 680 c.repo_info = self._load_repo(repo_name)
705 681 defaults = self._vcs_form_defaults(repo_name)
706 682 c.inherit_global_settings = defaults['inherit_global_settings']
707 683 c.labs_active = str2bool(
708 684 rhodecode.CONFIG.get('labs_settings_active', 'true'))
709 685
710 686 return htmlfill.render(
711 687 render('admin/repos/repo_edit.mako'),
712 688 defaults=defaults,
713 689 encoding="UTF-8",
714 690 force_defaults=False)
715 691
716 692 @HasRepoPermissionAllDecorator('repository.admin')
717 693 @auth.CSRFRequired()
718 694 def repo_settings_vcs_update(self, repo_name):
719 695 """POST /{repo_name}/settings/vcs/: All items in the collection"""
720 696 c.active = 'vcs'
721 697
722 698 model = VcsSettingsModel(repo=repo_name)
723 699 c.global_svn_branch_patterns = model.get_global_svn_branch_patterns()
724 700 c.global_svn_tag_patterns = model.get_global_svn_tag_patterns()
725 701 c.svn_branch_patterns = model.get_repo_svn_branch_patterns()
726 702 c.svn_tag_patterns = model.get_repo_svn_tag_patterns()
727 703 c.repo_info = self._load_repo(repo_name)
728 704 defaults = self._vcs_form_defaults(repo_name)
729 705 c.inherit_global_settings = defaults['inherit_global_settings']
730 706
731 707 application_form = RepoVcsSettingsForm(repo_name)()
732 708 try:
733 709 form_result = application_form.to_python(dict(request.POST))
734 710 except formencode.Invalid as errors:
735 711 h.flash(
736 712 _("Some form inputs contain invalid data."),
737 713 category='error')
738 714 return htmlfill.render(
739 715 render('admin/repos/repo_edit.mako'),
740 716 defaults=errors.value,
741 717 errors=errors.error_dict or {},
742 718 prefix_error=False,
743 719 encoding="UTF-8",
744 720 force_defaults=False
745 721 )
746 722
747 723 try:
748 724 inherit_global_settings = form_result['inherit_global_settings']
749 725 model.create_or_update_repo_settings(
750 726 form_result, inherit_global_settings=inherit_global_settings)
751 727 except Exception:
752 728 log.exception("Exception while updating settings")
753 729 h.flash(
754 730 _('Error occurred during updating repository VCS settings'),
755 731 category='error')
756 732 else:
757 733 Session().commit()
758 734 h.flash(_('Updated VCS settings'), category='success')
759 735 return redirect(url('repo_vcs_settings', repo_name=repo_name))
760 736
761 737 return htmlfill.render(
762 738 render('admin/repos/repo_edit.mako'),
763 739 defaults=self._vcs_form_defaults(repo_name),
764 740 encoding="UTF-8",
765 741 force_defaults=False)
766 742
767 743 @HasRepoPermissionAllDecorator('repository.admin')
768 744 @auth.CSRFRequired()
769 745 @jsonify
770 746 def repo_delete_svn_pattern(self, repo_name):
771 747 if not request.is_xhr:
772 748 return False
773 749
774 750 delete_pattern_id = request.POST.get('delete_svn_pattern')
775 751 model = VcsSettingsModel(repo=repo_name)
776 752 try:
777 753 model.delete_repo_svn_pattern(delete_pattern_id)
778 754 except SettingNotFound:
779 755 raise HTTPBadRequest()
780 756
781 757 Session().commit()
782 758 return True
783 759
784 760 def _vcs_form_defaults(self, repo_name):
785 761 model = VcsSettingsModel(repo=repo_name)
786 762 global_defaults = model.get_global_settings()
787 763
788 764 repo_defaults = {}
789 765 repo_defaults.update(global_defaults)
790 766 repo_defaults.update(model.get_repo_settings())
791 767
792 768 global_defaults = {
793 769 '{}_inherited'.format(k): global_defaults[k]
794 770 for k in global_defaults}
795 771
796 772 defaults = {
797 773 'inherit_global_settings': model.inherit_global_settings
798 774 }
799 775 defaults.update(global_defaults)
800 776 defaults.update(repo_defaults)
801 777 defaults.update({
802 778 'new_svn_branch': '',
803 779 'new_svn_tag': '',
804 780 })
805 781 return defaults
@@ -1,116 +1,117 b''
1 1
2 2 /******************************************************************************
3 3 * *
4 4 * DO NOT CHANGE THIS FILE MANUALLY *
5 5 * *
6 6 * *
7 7 * This file is automatically generated when the app starts up with *
8 8 * generate_js_files = true *
9 9 * *
10 10 * To add a route here pass jsroute=True to the route definition in the app *
11 11 * *
12 12 ******************************************************************************/
13 13 function registerRCRoutes() {
14 14 // routes registration
15 15 pyroutes.register('home', '/', []);
16 16 pyroutes.register('new_repo', '/_admin/create_repository', []);
17 17 pyroutes.register('edit_user', '/_admin/users/%(user_id)s/edit', ['user_id']);
18 18 pyroutes.register('edit_user_group_members', '/_admin/user_groups/%(user_group_id)s/edit/members', ['user_group_id']);
19 19 pyroutes.register('gists', '/_admin/gists', []);
20 20 pyroutes.register('new_gist', '/_admin/gists/new', []);
21 21 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
22 22 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
23 23 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
24 24 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
25 25 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/default-reviewers', ['repo_name']);
26 26 pyroutes.register('changeset_home', '/%(repo_name)s/changeset/%(revision)s', ['repo_name', 'revision']);
27 27 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
28 28 pyroutes.register('changeset_comment', '/%(repo_name)s/changeset/%(revision)s/comment', ['repo_name', 'revision']);
29 29 pyroutes.register('changeset_comment_preview', '/%(repo_name)s/changeset/comment/preview', ['repo_name']);
30 30 pyroutes.register('changeset_comment_delete', '/%(repo_name)s/changeset/comment/%(comment_id)s/delete', ['repo_name', 'comment_id']);
31 31 pyroutes.register('changeset_info', '/%(repo_name)s/changeset_info/%(revision)s', ['repo_name', 'revision']);
32 32 pyroutes.register('compare_url', '/%(repo_name)s/compare/%(source_ref_type)s@%(source_ref)s...%(target_ref_type)s@%(target_ref)s', ['repo_name', 'source_ref_type', 'source_ref', 'target_ref_type', 'target_ref']);
33 33 pyroutes.register('pullrequest_home', '/%(repo_name)s/pull-request/new', ['repo_name']);
34 34 pyroutes.register('pullrequest', '/%(repo_name)s/pull-request/new', ['repo_name']);
35 35 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
36 36 pyroutes.register('pullrequest_repo_destinations', '/%(repo_name)s/pull-request/repo-destinations', ['repo_name']);
37 37 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
38 38 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
39 39 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
40 40 pyroutes.register('pullrequest_comment', '/%(repo_name)s/pull-request-comment/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
41 41 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request-comment/%(comment_id)s/delete', ['repo_name', 'comment_id']);
42 42 pyroutes.register('changelog_home', '/%(repo_name)s/changelog', ['repo_name']);
43 43 pyroutes.register('changelog_file_home', '/%(repo_name)s/changelog/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
44 44 pyroutes.register('changelog_elements', '/%(repo_name)s/changelog_details', ['repo_name']);
45 45 pyroutes.register('files_home', '/%(repo_name)s/files/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
46 46 pyroutes.register('files_history_home', '/%(repo_name)s/history/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
47 47 pyroutes.register('files_authors_home', '/%(repo_name)s/authors/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
48 48 pyroutes.register('files_annotate_home', '/%(repo_name)s/annotate/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
49 49 pyroutes.register('files_annotate_previous', '/%(repo_name)s/annotate-previous/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
50 50 pyroutes.register('files_archive_home', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
51 51 pyroutes.register('files_nodelist_home', '/%(repo_name)s/nodelist/%(revision)s/%(f_path)s', ['repo_name', 'revision', 'f_path']);
52 52 pyroutes.register('files_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
53 53 pyroutes.register('summary_home_slash', '/%(repo_name)s/', ['repo_name']);
54 54 pyroutes.register('summary_home', '/%(repo_name)s', ['repo_name']);
55 55 pyroutes.register('favicon', '/favicon.ico', []);
56 56 pyroutes.register('robots', '/robots.txt', []);
57 57 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
58 58 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
59 59 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
60 60 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
61 61 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
62 62 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
63 63 pyroutes.register('repo_group_integrations_home', '%(repo_group_name)s/settings/integrations', ['repo_group_name']);
64 64 pyroutes.register('repo_group_integrations_list', '%(repo_group_name)s/settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
65 65 pyroutes.register('repo_group_integrations_new', '%(repo_group_name)s/settings/integrations/new', ['repo_group_name']);
66 66 pyroutes.register('repo_group_integrations_create', '%(repo_group_name)s/settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
67 67 pyroutes.register('repo_group_integrations_edit', '%(repo_group_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
68 68 pyroutes.register('repo_integrations_home', '%(repo_name)s/settings/integrations', ['repo_name']);
69 69 pyroutes.register('repo_integrations_list', '%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
70 70 pyroutes.register('repo_integrations_new', '%(repo_name)s/settings/integrations/new', ['repo_name']);
71 71 pyroutes.register('repo_integrations_create', '%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
72 72 pyroutes.register('repo_integrations_edit', '%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
73 73 pyroutes.register('ops_ping', '_admin/ops/ping', []);
74 74 pyroutes.register('admin_settings_open_source', '_admin/settings/open_source', []);
75 75 pyroutes.register('admin_settings_vcs_svn_generate_cfg', '_admin/settings/vcs/svn_generate_cfg', []);
76 76 pyroutes.register('admin_settings_system', '_admin/settings/system', []);
77 77 pyroutes.register('admin_settings_system_update', '_admin/settings/system/updates', []);
78 78 pyroutes.register('admin_settings_sessions', '_admin/settings/sessions', []);
79 79 pyroutes.register('admin_settings_sessions_cleanup', '_admin/settings/sessions/cleanup', []);
80 80 pyroutes.register('users', '_admin/users', []);
81 81 pyroutes.register('users_data', '_admin/users_data', []);
82 82 pyroutes.register('edit_user_auth_tokens', '_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
83 83 pyroutes.register('edit_user_auth_tokens_add', '_admin/users/%(user_id)s/edit/auth_tokens/new', ['user_id']);
84 84 pyroutes.register('edit_user_auth_tokens_delete', '_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
85 85 pyroutes.register('edit_user_groups_management', '_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
86 86 pyroutes.register('edit_user_groups_management_updates', '_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
87 87 pyroutes.register('edit_user_audit_logs', '_admin/users/%(user_id)s/edit/audit', ['user_id']);
88 88 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
89 89 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
90 90 pyroutes.register('channelstream_proxy', '/_channelstream', []);
91 91 pyroutes.register('login', '/_admin/login', []);
92 92 pyroutes.register('logout', '/_admin/logout', []);
93 93 pyroutes.register('register', '/_admin/register', []);
94 94 pyroutes.register('reset_password', '/_admin/password_reset', []);
95 95 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
96 96 pyroutes.register('user_autocomplete_data', '/_users', []);
97 97 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
98 98 pyroutes.register('repo_list_data', '/_repos', []);
99 99 pyroutes.register('goto_switcher_data', '/_goto_data', []);
100 100 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
101 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
101 102 pyroutes.register('repo_maintenance', '/%(repo_name)s/maintenance', ['repo_name']);
102 103 pyroutes.register('repo_maintenance_execute', '/%(repo_name)s/maintenance/execute', ['repo_name']);
103 104 pyroutes.register('strip', '/%(repo_name)s/strip', ['repo_name']);
104 105 pyroutes.register('strip_check', '/%(repo_name)s/strip_check', ['repo_name']);
105 106 pyroutes.register('strip_execute', '/%(repo_name)s/strip_execute', ['repo_name']);
106 107 pyroutes.register('search', '/_admin/search', []);
107 108 pyroutes.register('search_repo', '/%(repo_name)s/search', ['repo_name']);
108 109 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
109 110 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
110 111 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
111 112 pyroutes.register('my_account_password_update', '/_admin/my_account/password', []);
112 113 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
113 114 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
114 115 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
115 116 pyroutes.register('apiv2', '/_admin/api', []);
116 117 }
@@ -1,106 +1,106 b''
1 1 ## -*- coding: utf-8 -*-
2 2 ##
3 3 ## See also repo_settings.html
4 4 ##
5 5 <%inherit file="/base/base.mako"/>
6 6
7 7 <%def name="title()">
8 8 ${_('%s repository settings') % c.repo_info.repo_name}
9 9 %if c.rhodecode_name:
10 10 &middot; ${h.branding(c.rhodecode_name)}
11 11 %endif
12 12 </%def>
13 13
14 14 <%def name="breadcrumbs_links()">
15 15 ${_('Settings')}
16 16 </%def>
17 17
18 18 <%def name="menu_bar_nav()">
19 19 ${self.menu_items(active='repositories')}
20 20 </%def>
21 21
22 22 <%def name="menu_bar_subnav()">
23 23 ${self.repo_menu(active='options')}
24 24 </%def>
25 25
26 26 <%def name="main_content()">
27 27 <%include file="/admin/repos/repo_edit_${c.active}.mako"/>
28 28 </%def>
29 29
30 30
31 31 <%def name="main()">
32 32 <div class="box">
33 33 <div class="title">
34 34 ${self.repo_page_title(c.rhodecode_db_repo)}
35 35 ${self.breadcrumbs()}
36 36 </div>
37 37
38 38 <div class="sidebar-col-wrapper scw-small">
39 39 ##main
40 40 <div class="sidebar">
41 41 <ul class="nav nav-pills nav-stacked">
42 42 <li class="${'active' if c.active=='settings' else ''}">
43 43 <a href="${h.route_path('edit_repo', repo_name=c.repo_name)}">${_('Settings')}</a>
44 44 </li>
45 45 <li class="${'active' if c.active=='permissions' else ''}">
46 46 <a href="${h.url('edit_repo_perms', repo_name=c.repo_name)}">${_('Permissions')}</a>
47 47 </li>
48 48 <li class="${'active' if c.active=='advanced' else ''}">
49 49 <a href="${h.url('edit_repo_advanced', repo_name=c.repo_name)}">${_('Advanced')}</a>
50 50 </li>
51 51 <li class="${'active' if c.active=='vcs' else ''}">
52 52 <a href="${h.url('repo_vcs_settings', repo_name=c.repo_name)}">${_('VCS')}</a>
53 53 </li>
54 54 <li class="${'active' if c.active=='fields' else ''}">
55 55 <a href="${h.url('edit_repo_fields', repo_name=c.repo_name)}">${_('Extra Fields')}</a>
56 56 </li>
57 57 <li class="${'active' if c.active=='issuetracker' else ''}">
58 58 <a href="${h.url('repo_settings_issuetracker', repo_name=c.repo_name)}">${_('Issue Tracker')}</a>
59 59 </li>
60 60 <li class="${'active' if c.active=='caches' else ''}">
61 <a href="${h.url('edit_repo_caches', repo_name=c.repo_name)}">${_('Caches')}</a>
61 <a href="${h.route_path('edit_repo_caches', repo_name=c.repo_name)}">${_('Caches')}</a>
62 62 </li>
63 63 %if c.repo_info.repo_type != 'svn':
64 64 <li class="${'active' if c.active=='remote' else ''}">
65 65 <a href="${h.url('edit_repo_remote', repo_name=c.repo_name)}">${_('Remote')}</a>
66 66 </li>
67 67 %endif
68 68 <li class="${'active' if c.active=='statistics' else ''}">
69 69 <a href="${h.url('edit_repo_statistics', repo_name=c.repo_name)}">${_('Statistics')}</a>
70 70 </li>
71 71 <li class="${'active' if c.active=='integrations' else ''}">
72 72 <a href="${h.route_path('repo_integrations_home', repo_name=c.repo_name)}">${_('Integrations')}</a>
73 73 </li>
74 74 <li class="${'active' if c.active=='maintenance' else ''}">
75 75 <a href="${h.route_path('repo_maintenance', repo_name=c.repo_name)}">${_('Maintenance')}</a>
76 76 </li>
77 77 <li class="${'active' if c.active=='strip' else ''}">
78 78 <a href="${h.route_path('strip', repo_name=c.repo_name)}">${_('Strip')}</a>
79 79 </li>
80 80 ## TODO: dan: replace repo navigation with navlist registry like with
81 81 ## admin menu. First must find way to allow runtime configuration
82 82 ## it to account for the c.repo_info.repo_type != 'svn' call above
83 83 <%
84 84 reviewer_settings = False
85 85 try:
86 86 import rc_reviewers
87 87 reviewer_settings = True
88 88 except ImportError:
89 89 pass
90 90 %>
91 91 %if reviewer_settings:
92 92 <li class="${'active' if c.active=='reviewers' else ''}">
93 93 <a href="${h.route_path('repo_reviewers_home', repo_name=c.repo_name)}">${_('Reviewers')}</a>
94 94 </li>
95 95 %endif
96 96 </ul>
97 97 </div>
98 98
99 99 <div class="main-content-full-width">
100 100 ${self.main_content()}
101 101 </div>
102 102
103 103 </div>
104 104 </div>
105 105
106 106 </%def> No newline at end of file
@@ -1,51 +1,55 b''
1 1 <div class="panel panel-default">
2 2 <div class="panel-heading">
3 3 <h3 class="panel-title">${_('Invalidate Cache for Repository')}</h3>
4 4 </div>
5 5 <div class="panel-body">
6 ${h.secure_form(url('edit_repo_caches', repo_name=c.repo_name), method='put')}
7 <div>
8 <div class="fields">
6
7 <h4>${_('Manually invalidate the repository cache. On the next access a repository cache will be recreated.')}</h4>
8
9 9 <p>
10 ${h.submit('reset_cache_%s' % c.repo_info.repo_name,_('Invalidate repository cache'),class_="btn btn-small",onclick="return confirm('"+_('Confirm to invalidate repository cache')+"');")}
10 ${_('Cache purge can be automated by such api call called periodically (in crontab etc)')}
11 <br/>
12 <code>
13 curl ${h.route_url('apiv2')} -X POST -H 'content-type:text/plain' --data-binary '{"id":1, "auth_token":"SECRET", "method":"invalidate_cache", "args":{"repoid":"${c.repo_info.repo_name}"}}'
14 </code>
11 15 </p>
12 <div class="field" >
13 <span class="help-block">
14 ${_('Manually invalidate the repository cache. On the next access a repository cache will be recreated.')}
15 </span>
16 </div>
17 16
17 ${h.secure_form(h.route_path('edit_repo_caches', repo_name=c.repo_name), method='POST')}
18 <div class="form">
19 <div class="fields">
20 ${h.submit('reset_cache_%s' % c.repo_info.repo_name,_('Invalidate repository cache'),class_="btn btn-small",onclick="return confirm('"+_('Confirm to invalidate repository cache')+"');")}
18 21 </div>
19 22 </div>
20 23 ${h.end_form()}
24
21 25 </div>
22 26 </div>
23 27
24 28
25 29 <div class="panel panel-default">
26 30 <div class="panel-heading">
27 31 <h3 class="panel-title">
28 ${(ungettext('List of repository caches (%(count)s entry)', 'List of repository caches (%(count)s entries)' ,len(c.repo_info.cache_keys)) % {'count': len(c.repo_info.cache_keys)})}
32 ${(_ungettext('List of repository caches (%(count)s entry)', 'List of repository caches (%(count)s entries)' ,len(c.repo_info.cache_keys)) % {'count': len(c.repo_info.cache_keys)})}
29 33 </h3>
30 34 </div>
31 35 <div class="panel-body">
32 36 <div class="field" >
33 37 <table class="rctable edit_cache">
34 38 <tr>
35 39 <th>${_('Prefix')}</th>
36 40 <th>${_('Key')}</th>
37 41 <th>${_('Active')}</th>
38 42 </tr>
39 43 %for cache in c.repo_info.cache_keys:
40 44 <tr>
41 45 <td class="td-prefix">${cache.get_prefix() or '-'}</td>
42 46 <td class="td-cachekey">${cache.cache_key}</td>
43 47 <td class="td-active">${h.bool2icon(cache.cache_active)}</td>
44 48 </tr>
45 49 %endfor
46 50 </table>
47 51 </div>
48 52 </div>
49 53 </div>
50 54
51 55
General Comments 0
You need to be logged in to leave comments. Login now