##// END OF EJS Templates
routing: switched static redirection links to pyramid....
marcink -
r1679:c94a1c49 default
parent child Browse files
Show More
@@ -1,41 +1,45 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
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
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
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/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 from rhodecode.config import routing_links
20
21
21
22
22 def includeme(config):
23 def includeme(config):
23
24
24 config.add_route(
25 config.add_route(
25 name='user_autocomplete_data',
26 name='user_autocomplete_data',
26 pattern='/_users')
27 pattern='/_users')
27
28
28 config.add_route(
29 config.add_route(
29 name='user_group_autocomplete_data',
30 name='user_group_autocomplete_data',
30 pattern='/_user_groups')
31 pattern='/_user_groups')
31
32
32 config.add_route(
33 config.add_route(
33 name='repo_list_data',
34 name='repo_list_data',
34 pattern='/_repos')
35 pattern='/_repos')
35
36
36 config.add_route(
37 config.add_route(
37 name='goto_switcher_data',
38 name='goto_switcher_data',
38 pattern='/_goto_data')
39 pattern='/_goto_data')
39
40
41 # register our static links via redirection mechanismy
42 routing_links.connect_redirection_links(config)
43
40 # Scan module for configuration decorators.
44 # Scan module for configuration decorators.
41 config.scan()
45 config.scan()
@@ -1,1144 +1,1125 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
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
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
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/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 Routes configuration
22 Routes configuration
23
23
24 The more specific and detailed routes should be defined first so they
24 The more specific and detailed routes should be defined first so they
25 may take precedent over the more generic routes. For more information
25 may take precedent over the more generic routes. For more information
26 refer to the routes manual at http://routes.groovie.org/docs/
26 refer to the routes manual at http://routes.groovie.org/docs/
27
27
28 IMPORTANT: if you change any routing here, make sure to take a look at lib/base.py
28 IMPORTANT: if you change any routing here, make sure to take a look at lib/base.py
29 and _route_name variable which uses some of stored naming here to do redirects.
29 and _route_name variable which uses some of stored naming here to do redirects.
30 """
30 """
31 import os
31 import os
32 import re
32 import re
33 from routes import Mapper
33 from routes import Mapper
34
34
35 from rhodecode.config import routing_links
36
37 # prefix for non repository related links needs to be prefixed with `/`
35 # prefix for non repository related links needs to be prefixed with `/`
38 ADMIN_PREFIX = '/_admin'
36 ADMIN_PREFIX = '/_admin'
39 STATIC_FILE_PREFIX = '/_static'
37 STATIC_FILE_PREFIX = '/_static'
40
38
41 # Default requirements for URL parts
39 # Default requirements for URL parts
42 URL_NAME_REQUIREMENTS = {
40 URL_NAME_REQUIREMENTS = {
43 # group name can have a slash in them, but they must not end with a slash
41 # group name can have a slash in them, but they must not end with a slash
44 'group_name': r'.*?[^/]',
42 'group_name': r'.*?[^/]',
45 'repo_group_name': r'.*?[^/]',
43 'repo_group_name': r'.*?[^/]',
46 # repo names can have a slash in them, but they must not end with a slash
44 # repo names can have a slash in them, but they must not end with a slash
47 'repo_name': r'.*?[^/]',
45 'repo_name': r'.*?[^/]',
48 # file path eats up everything at the end
46 # file path eats up everything at the end
49 'f_path': r'.*',
47 'f_path': r'.*',
50 # reference types
48 # reference types
51 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
49 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
52 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
50 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
53 }
51 }
54
52
55
53
56 def add_route_requirements(route_path, requirements):
54 def add_route_requirements(route_path, requirements):
57 """
55 """
58 Adds regex requirements to pyramid routes using a mapping dict
56 Adds regex requirements to pyramid routes using a mapping dict
59
57
60 >>> add_route_requirements('/{action}/{id}', {'id': r'\d+'})
58 >>> add_route_requirements('/{action}/{id}', {'id': r'\d+'})
61 '/{action}/{id:\d+}'
59 '/{action}/{id:\d+}'
62
60
63 """
61 """
64 for key, regex in requirements.items():
62 for key, regex in requirements.items():
65 route_path = route_path.replace('{%s}' % key, '{%s:%s}' % (key, regex))
63 route_path = route_path.replace('{%s}' % key, '{%s:%s}' % (key, regex))
66 return route_path
64 return route_path
67
65
68
66
69 class JSRoutesMapper(Mapper):
67 class JSRoutesMapper(Mapper):
70 """
68 """
71 Wrapper for routes.Mapper to make pyroutes compatible url definitions
69 Wrapper for routes.Mapper to make pyroutes compatible url definitions
72 """
70 """
73 _named_route_regex = re.compile(r'^[a-z-_0-9A-Z]+$')
71 _named_route_regex = re.compile(r'^[a-z-_0-9A-Z]+$')
74 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
72 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
75 def __init__(self, *args, **kw):
73 def __init__(self, *args, **kw):
76 super(JSRoutesMapper, self).__init__(*args, **kw)
74 super(JSRoutesMapper, self).__init__(*args, **kw)
77 self._jsroutes = []
75 self._jsroutes = []
78
76
79 def connect(self, *args, **kw):
77 def connect(self, *args, **kw):
80 """
78 """
81 Wrapper for connect to take an extra argument jsroute=True
79 Wrapper for connect to take an extra argument jsroute=True
82
80
83 :param jsroute: boolean, if True will add the route to the pyroutes list
81 :param jsroute: boolean, if True will add the route to the pyroutes list
84 """
82 """
85 if kw.pop('jsroute', False):
83 if kw.pop('jsroute', False):
86 if not self._named_route_regex.match(args[0]):
84 if not self._named_route_regex.match(args[0]):
87 raise Exception('only named routes can be added to pyroutes')
85 raise Exception('only named routes can be added to pyroutes')
88 self._jsroutes.append(args[0])
86 self._jsroutes.append(args[0])
89
87
90 super(JSRoutesMapper, self).connect(*args, **kw)
88 super(JSRoutesMapper, self).connect(*args, **kw)
91
89
92 def _extract_route_information(self, route):
90 def _extract_route_information(self, route):
93 """
91 """
94 Convert a route into tuple(name, path, args), eg:
92 Convert a route into tuple(name, path, args), eg:
95 ('show_user', '/profile/%(username)s', ['username'])
93 ('show_user', '/profile/%(username)s', ['username'])
96 """
94 """
97 routepath = route.routepath
95 routepath = route.routepath
98 def replace(matchobj):
96 def replace(matchobj):
99 if matchobj.group(1):
97 if matchobj.group(1):
100 return "%%(%s)s" % matchobj.group(1).split(':')[0]
98 return "%%(%s)s" % matchobj.group(1).split(':')[0]
101 else:
99 else:
102 return "%%(%s)s" % matchobj.group(2)
100 return "%%(%s)s" % matchobj.group(2)
103
101
104 routepath = self._argument_prog.sub(replace, routepath)
102 routepath = self._argument_prog.sub(replace, routepath)
105 return (
103 return (
106 route.name,
104 route.name,
107 routepath,
105 routepath,
108 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
106 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
109 for arg in self._argument_prog.findall(route.routepath)]
107 for arg in self._argument_prog.findall(route.routepath)]
110 )
108 )
111
109
112 def jsroutes(self):
110 def jsroutes(self):
113 """
111 """
114 Return a list of pyroutes.js compatible routes
112 Return a list of pyroutes.js compatible routes
115 """
113 """
116 for route_name in self._jsroutes:
114 for route_name in self._jsroutes:
117 yield self._extract_route_information(self._routenames[route_name])
115 yield self._extract_route_information(self._routenames[route_name])
118
116
119
117
120 def make_map(config):
118 def make_map(config):
121 """Create, configure and return the routes Mapper"""
119 """Create, configure and return the routes Mapper"""
122 rmap = JSRoutesMapper(directory=config['pylons.paths']['controllers'],
120 rmap = JSRoutesMapper(directory=config['pylons.paths']['controllers'],
123 always_scan=config['debug'])
121 always_scan=config['debug'])
124 rmap.minimization = False
122 rmap.minimization = False
125 rmap.explicit = False
123 rmap.explicit = False
126
124
127 from rhodecode.lib.utils2 import str2bool
125 from rhodecode.lib.utils2 import str2bool
128 from rhodecode.model import repo, repo_group
126 from rhodecode.model import repo, repo_group
129
127
130 def check_repo(environ, match_dict):
128 def check_repo(environ, match_dict):
131 """
129 """
132 check for valid repository for proper 404 handling
130 check for valid repository for proper 404 handling
133
131
134 :param environ:
132 :param environ:
135 :param match_dict:
133 :param match_dict:
136 """
134 """
137 repo_name = match_dict.get('repo_name')
135 repo_name = match_dict.get('repo_name')
138
136
139 if match_dict.get('f_path'):
137 if match_dict.get('f_path'):
140 # fix for multiple initial slashes that causes errors
138 # fix for multiple initial slashes that causes errors
141 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
139 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
142 repo_model = repo.RepoModel()
140 repo_model = repo.RepoModel()
143 by_name_match = repo_model.get_by_repo_name(repo_name)
141 by_name_match = repo_model.get_by_repo_name(repo_name)
144 # if we match quickly from database, short circuit the operation,
142 # if we match quickly from database, short circuit the operation,
145 # and validate repo based on the type.
143 # and validate repo based on the type.
146 if by_name_match:
144 if by_name_match:
147 return True
145 return True
148
146
149 by_id_match = repo_model.get_repo_by_id(repo_name)
147 by_id_match = repo_model.get_repo_by_id(repo_name)
150 if by_id_match:
148 if by_id_match:
151 repo_name = by_id_match.repo_name
149 repo_name = by_id_match.repo_name
152 match_dict['repo_name'] = repo_name
150 match_dict['repo_name'] = repo_name
153 return True
151 return True
154
152
155 return False
153 return False
156
154
157 def check_group(environ, match_dict):
155 def check_group(environ, match_dict):
158 """
156 """
159 check for valid repository group path for proper 404 handling
157 check for valid repository group path for proper 404 handling
160
158
161 :param environ:
159 :param environ:
162 :param match_dict:
160 :param match_dict:
163 """
161 """
164 repo_group_name = match_dict.get('group_name')
162 repo_group_name = match_dict.get('group_name')
165 repo_group_model = repo_group.RepoGroupModel()
163 repo_group_model = repo_group.RepoGroupModel()
166 by_name_match = repo_group_model.get_by_group_name(repo_group_name)
164 by_name_match = repo_group_model.get_by_group_name(repo_group_name)
167 if by_name_match:
165 if by_name_match:
168 return True
166 return True
169
167
170 return False
168 return False
171
169
172 def check_user_group(environ, match_dict):
170 def check_user_group(environ, match_dict):
173 """
171 """
174 check for valid user group for proper 404 handling
172 check for valid user group for proper 404 handling
175
173
176 :param environ:
174 :param environ:
177 :param match_dict:
175 :param match_dict:
178 """
176 """
179 return True
177 return True
180
178
181 def check_int(environ, match_dict):
179 def check_int(environ, match_dict):
182 return match_dict.get('id').isdigit()
180 return match_dict.get('id').isdigit()
183
181
184
182
185 #==========================================================================
183 #==========================================================================
186 # CUSTOM ROUTES HERE
184 # CUSTOM ROUTES HERE
187 #==========================================================================
185 #==========================================================================
188
186
189 # MAIN PAGE
187 # MAIN PAGE
190 rmap.connect('home', '/', controller='home', action='index', jsroute=True)
188 rmap.connect('home', '/', controller='home', action='index', jsroute=True)
191
189
192 # TODO: johbo: Static links, to be replaced by our redirection mechanism
190 # ping and pylons error test
193 rmap.connect('rst_help',
194 'http://docutils.sourceforge.net/docs/user/rst/quickref.html',
195 _static=True)
196 rmap.connect('markdown_help',
197 'http://daringfireball.net/projects/markdown/syntax',
198 _static=True)
199 rmap.connect('rhodecode_official', 'https://rhodecode.com', _static=True)
200 rmap.connect('rhodecode_support', 'https://rhodecode.com/help/', _static=True)
201 rmap.connect('rhodecode_translations', 'https://rhodecode.com/translate/enterprise', _static=True)
202 # TODO: anderson - making this a static link since redirect won't play
203 # nice with POST requests
204 rmap.connect('enterprise_license_convert_from_old',
205 'https://rhodecode.com/u/license-upgrade',
206 _static=True)
207
208 routing_links.connect_redirection_links(rmap)
209
210 rmap.connect('ping', '%s/ping' % (ADMIN_PREFIX,), controller='home', action='ping')
191 rmap.connect('ping', '%s/ping' % (ADMIN_PREFIX,), controller='home', action='ping')
211 rmap.connect('error_test', '%s/error_test' % (ADMIN_PREFIX,), controller='home', action='error_test')
192 rmap.connect('error_test', '%s/error_test' % (ADMIN_PREFIX,), controller='home', action='error_test')
212
193
213 # ADMIN REPOSITORY ROUTES
194 # ADMIN REPOSITORY ROUTES
214 with rmap.submapper(path_prefix=ADMIN_PREFIX,
195 with rmap.submapper(path_prefix=ADMIN_PREFIX,
215 controller='admin/repos') as m:
196 controller='admin/repos') as m:
216 m.connect('repos', '/repos',
197 m.connect('repos', '/repos',
217 action='create', conditions={'method': ['POST']})
198 action='create', conditions={'method': ['POST']})
218 m.connect('repos', '/repos',
199 m.connect('repos', '/repos',
219 action='index', conditions={'method': ['GET']})
200 action='index', conditions={'method': ['GET']})
220 m.connect('new_repo', '/create_repository', jsroute=True,
201 m.connect('new_repo', '/create_repository', jsroute=True,
221 action='create_repository', conditions={'method': ['GET']})
202 action='create_repository', conditions={'method': ['GET']})
222 m.connect('/repos/{repo_name}',
203 m.connect('/repos/{repo_name}',
223 action='update', conditions={'method': ['PUT'],
204 action='update', conditions={'method': ['PUT'],
224 'function': check_repo},
205 'function': check_repo},
225 requirements=URL_NAME_REQUIREMENTS)
206 requirements=URL_NAME_REQUIREMENTS)
226 m.connect('delete_repo', '/repos/{repo_name}',
207 m.connect('delete_repo', '/repos/{repo_name}',
227 action='delete', conditions={'method': ['DELETE']},
208 action='delete', conditions={'method': ['DELETE']},
228 requirements=URL_NAME_REQUIREMENTS)
209 requirements=URL_NAME_REQUIREMENTS)
229 m.connect('repo', '/repos/{repo_name}',
210 m.connect('repo', '/repos/{repo_name}',
230 action='show', conditions={'method': ['GET'],
211 action='show', conditions={'method': ['GET'],
231 'function': check_repo},
212 'function': check_repo},
232 requirements=URL_NAME_REQUIREMENTS)
213 requirements=URL_NAME_REQUIREMENTS)
233
214
234 # ADMIN REPOSITORY GROUPS ROUTES
215 # ADMIN REPOSITORY GROUPS ROUTES
235 with rmap.submapper(path_prefix=ADMIN_PREFIX,
216 with rmap.submapper(path_prefix=ADMIN_PREFIX,
236 controller='admin/repo_groups') as m:
217 controller='admin/repo_groups') as m:
237 m.connect('repo_groups', '/repo_groups',
218 m.connect('repo_groups', '/repo_groups',
238 action='create', conditions={'method': ['POST']})
219 action='create', conditions={'method': ['POST']})
239 m.connect('repo_groups', '/repo_groups',
220 m.connect('repo_groups', '/repo_groups',
240 action='index', conditions={'method': ['GET']})
221 action='index', conditions={'method': ['GET']})
241 m.connect('new_repo_group', '/repo_groups/new',
222 m.connect('new_repo_group', '/repo_groups/new',
242 action='new', conditions={'method': ['GET']})
223 action='new', conditions={'method': ['GET']})
243 m.connect('update_repo_group', '/repo_groups/{group_name}',
224 m.connect('update_repo_group', '/repo_groups/{group_name}',
244 action='update', conditions={'method': ['PUT'],
225 action='update', conditions={'method': ['PUT'],
245 'function': check_group},
226 'function': check_group},
246 requirements=URL_NAME_REQUIREMENTS)
227 requirements=URL_NAME_REQUIREMENTS)
247
228
248 # EXTRAS REPO GROUP ROUTES
229 # EXTRAS REPO GROUP ROUTES
249 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
230 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
250 action='edit',
231 action='edit',
251 conditions={'method': ['GET'], 'function': check_group},
232 conditions={'method': ['GET'], 'function': check_group},
252 requirements=URL_NAME_REQUIREMENTS)
233 requirements=URL_NAME_REQUIREMENTS)
253 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
234 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
254 action='edit',
235 action='edit',
255 conditions={'method': ['PUT'], 'function': check_group},
236 conditions={'method': ['PUT'], 'function': check_group},
256 requirements=URL_NAME_REQUIREMENTS)
237 requirements=URL_NAME_REQUIREMENTS)
257
238
258 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
239 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
259 action='edit_repo_group_advanced',
240 action='edit_repo_group_advanced',
260 conditions={'method': ['GET'], 'function': check_group},
241 conditions={'method': ['GET'], 'function': check_group},
261 requirements=URL_NAME_REQUIREMENTS)
242 requirements=URL_NAME_REQUIREMENTS)
262 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
243 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
263 action='edit_repo_group_advanced',
244 action='edit_repo_group_advanced',
264 conditions={'method': ['PUT'], 'function': check_group},
245 conditions={'method': ['PUT'], 'function': check_group},
265 requirements=URL_NAME_REQUIREMENTS)
246 requirements=URL_NAME_REQUIREMENTS)
266
247
267 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
248 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
268 action='edit_repo_group_perms',
249 action='edit_repo_group_perms',
269 conditions={'method': ['GET'], 'function': check_group},
250 conditions={'method': ['GET'], 'function': check_group},
270 requirements=URL_NAME_REQUIREMENTS)
251 requirements=URL_NAME_REQUIREMENTS)
271 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
252 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
272 action='update_perms',
253 action='update_perms',
273 conditions={'method': ['PUT'], 'function': check_group},
254 conditions={'method': ['PUT'], 'function': check_group},
274 requirements=URL_NAME_REQUIREMENTS)
255 requirements=URL_NAME_REQUIREMENTS)
275
256
276 m.connect('delete_repo_group', '/repo_groups/{group_name}',
257 m.connect('delete_repo_group', '/repo_groups/{group_name}',
277 action='delete', conditions={'method': ['DELETE'],
258 action='delete', conditions={'method': ['DELETE'],
278 'function': check_group},
259 'function': check_group},
279 requirements=URL_NAME_REQUIREMENTS)
260 requirements=URL_NAME_REQUIREMENTS)
280
261
281 # ADMIN USER ROUTES
262 # ADMIN USER ROUTES
282 with rmap.submapper(path_prefix=ADMIN_PREFIX,
263 with rmap.submapper(path_prefix=ADMIN_PREFIX,
283 controller='admin/users') as m:
264 controller='admin/users') as m:
284 m.connect('users', '/users',
265 m.connect('users', '/users',
285 action='create', conditions={'method': ['POST']})
266 action='create', conditions={'method': ['POST']})
286 m.connect('new_user', '/users/new',
267 m.connect('new_user', '/users/new',
287 action='new', conditions={'method': ['GET']})
268 action='new', conditions={'method': ['GET']})
288 m.connect('update_user', '/users/{user_id}',
269 m.connect('update_user', '/users/{user_id}',
289 action='update', conditions={'method': ['PUT']})
270 action='update', conditions={'method': ['PUT']})
290 m.connect('delete_user', '/users/{user_id}',
271 m.connect('delete_user', '/users/{user_id}',
291 action='delete', conditions={'method': ['DELETE']})
272 action='delete', conditions={'method': ['DELETE']})
292 m.connect('edit_user', '/users/{user_id}/edit',
273 m.connect('edit_user', '/users/{user_id}/edit',
293 action='edit', conditions={'method': ['GET']}, jsroute=True)
274 action='edit', conditions={'method': ['GET']}, jsroute=True)
294 m.connect('user', '/users/{user_id}',
275 m.connect('user', '/users/{user_id}',
295 action='show', conditions={'method': ['GET']})
276 action='show', conditions={'method': ['GET']})
296 m.connect('force_password_reset_user', '/users/{user_id}/password_reset',
277 m.connect('force_password_reset_user', '/users/{user_id}/password_reset',
297 action='reset_password', conditions={'method': ['POST']})
278 action='reset_password', conditions={'method': ['POST']})
298 m.connect('create_personal_repo_group', '/users/{user_id}/create_repo_group',
279 m.connect('create_personal_repo_group', '/users/{user_id}/create_repo_group',
299 action='create_personal_repo_group', conditions={'method': ['POST']})
280 action='create_personal_repo_group', conditions={'method': ['POST']})
300
281
301 # EXTRAS USER ROUTES
282 # EXTRAS USER ROUTES
302 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
283 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
303 action='edit_advanced', conditions={'method': ['GET']})
284 action='edit_advanced', conditions={'method': ['GET']})
304 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
285 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
305 action='update_advanced', conditions={'method': ['PUT']})
286 action='update_advanced', conditions={'method': ['PUT']})
306
287
307 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
288 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
308 action='edit_global_perms', conditions={'method': ['GET']})
289 action='edit_global_perms', conditions={'method': ['GET']})
309 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
290 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
310 action='update_global_perms', conditions={'method': ['PUT']})
291 action='update_global_perms', conditions={'method': ['PUT']})
311
292
312 m.connect('edit_user_perms_summary', '/users/{user_id}/edit/permissions_summary',
293 m.connect('edit_user_perms_summary', '/users/{user_id}/edit/permissions_summary',
313 action='edit_perms_summary', conditions={'method': ['GET']})
294 action='edit_perms_summary', conditions={'method': ['GET']})
314
295
315 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
296 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
316 action='edit_emails', conditions={'method': ['GET']})
297 action='edit_emails', conditions={'method': ['GET']})
317 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
298 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
318 action='add_email', conditions={'method': ['PUT']})
299 action='add_email', conditions={'method': ['PUT']})
319 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
300 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
320 action='delete_email', conditions={'method': ['DELETE']})
301 action='delete_email', conditions={'method': ['DELETE']})
321
302
322 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
303 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
323 action='edit_ips', conditions={'method': ['GET']})
304 action='edit_ips', conditions={'method': ['GET']})
324 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
305 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
325 action='add_ip', conditions={'method': ['PUT']})
306 action='add_ip', conditions={'method': ['PUT']})
326 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
307 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
327 action='delete_ip', conditions={'method': ['DELETE']})
308 action='delete_ip', conditions={'method': ['DELETE']})
328
309
329 # ADMIN USER GROUPS REST ROUTES
310 # ADMIN USER GROUPS REST ROUTES
330 with rmap.submapper(path_prefix=ADMIN_PREFIX,
311 with rmap.submapper(path_prefix=ADMIN_PREFIX,
331 controller='admin/user_groups') as m:
312 controller='admin/user_groups') as m:
332 m.connect('users_groups', '/user_groups',
313 m.connect('users_groups', '/user_groups',
333 action='create', conditions={'method': ['POST']})
314 action='create', conditions={'method': ['POST']})
334 m.connect('users_groups', '/user_groups',
315 m.connect('users_groups', '/user_groups',
335 action='index', conditions={'method': ['GET']})
316 action='index', conditions={'method': ['GET']})
336 m.connect('new_users_group', '/user_groups/new',
317 m.connect('new_users_group', '/user_groups/new',
337 action='new', conditions={'method': ['GET']})
318 action='new', conditions={'method': ['GET']})
338 m.connect('update_users_group', '/user_groups/{user_group_id}',
319 m.connect('update_users_group', '/user_groups/{user_group_id}',
339 action='update', conditions={'method': ['PUT']})
320 action='update', conditions={'method': ['PUT']})
340 m.connect('delete_users_group', '/user_groups/{user_group_id}',
321 m.connect('delete_users_group', '/user_groups/{user_group_id}',
341 action='delete', conditions={'method': ['DELETE']})
322 action='delete', conditions={'method': ['DELETE']})
342 m.connect('edit_users_group', '/user_groups/{user_group_id}/edit',
323 m.connect('edit_users_group', '/user_groups/{user_group_id}/edit',
343 action='edit', conditions={'method': ['GET']},
324 action='edit', conditions={'method': ['GET']},
344 function=check_user_group)
325 function=check_user_group)
345
326
346 # EXTRAS USER GROUP ROUTES
327 # EXTRAS USER GROUP ROUTES
347 m.connect('edit_user_group_global_perms',
328 m.connect('edit_user_group_global_perms',
348 '/user_groups/{user_group_id}/edit/global_permissions',
329 '/user_groups/{user_group_id}/edit/global_permissions',
349 action='edit_global_perms', conditions={'method': ['GET']})
330 action='edit_global_perms', conditions={'method': ['GET']})
350 m.connect('edit_user_group_global_perms',
331 m.connect('edit_user_group_global_perms',
351 '/user_groups/{user_group_id}/edit/global_permissions',
332 '/user_groups/{user_group_id}/edit/global_permissions',
352 action='update_global_perms', conditions={'method': ['PUT']})
333 action='update_global_perms', conditions={'method': ['PUT']})
353 m.connect('edit_user_group_perms_summary',
334 m.connect('edit_user_group_perms_summary',
354 '/user_groups/{user_group_id}/edit/permissions_summary',
335 '/user_groups/{user_group_id}/edit/permissions_summary',
355 action='edit_perms_summary', conditions={'method': ['GET']})
336 action='edit_perms_summary', conditions={'method': ['GET']})
356
337
357 m.connect('edit_user_group_perms',
338 m.connect('edit_user_group_perms',
358 '/user_groups/{user_group_id}/edit/permissions',
339 '/user_groups/{user_group_id}/edit/permissions',
359 action='edit_perms', conditions={'method': ['GET']})
340 action='edit_perms', conditions={'method': ['GET']})
360 m.connect('edit_user_group_perms',
341 m.connect('edit_user_group_perms',
361 '/user_groups/{user_group_id}/edit/permissions',
342 '/user_groups/{user_group_id}/edit/permissions',
362 action='update_perms', conditions={'method': ['PUT']})
343 action='update_perms', conditions={'method': ['PUT']})
363
344
364 m.connect('edit_user_group_advanced',
345 m.connect('edit_user_group_advanced',
365 '/user_groups/{user_group_id}/edit/advanced',
346 '/user_groups/{user_group_id}/edit/advanced',
366 action='edit_advanced', conditions={'method': ['GET']})
347 action='edit_advanced', conditions={'method': ['GET']})
367
348
368 m.connect('edit_user_group_advanced_sync',
349 m.connect('edit_user_group_advanced_sync',
369 '/user_groups/{user_group_id}/edit/advanced/sync',
350 '/user_groups/{user_group_id}/edit/advanced/sync',
370 action='edit_advanced_set_synchronization', conditions={'method': ['POST']})
351 action='edit_advanced_set_synchronization', conditions={'method': ['POST']})
371
352
372 m.connect('edit_user_group_members',
353 m.connect('edit_user_group_members',
373 '/user_groups/{user_group_id}/edit/members', jsroute=True,
354 '/user_groups/{user_group_id}/edit/members', jsroute=True,
374 action='user_group_members', conditions={'method': ['GET']})
355 action='user_group_members', conditions={'method': ['GET']})
375
356
376 # ADMIN PERMISSIONS ROUTES
357 # ADMIN PERMISSIONS ROUTES
377 with rmap.submapper(path_prefix=ADMIN_PREFIX,
358 with rmap.submapper(path_prefix=ADMIN_PREFIX,
378 controller='admin/permissions') as m:
359 controller='admin/permissions') as m:
379 m.connect('admin_permissions_application', '/permissions/application',
360 m.connect('admin_permissions_application', '/permissions/application',
380 action='permission_application_update', conditions={'method': ['POST']})
361 action='permission_application_update', conditions={'method': ['POST']})
381 m.connect('admin_permissions_application', '/permissions/application',
362 m.connect('admin_permissions_application', '/permissions/application',
382 action='permission_application', conditions={'method': ['GET']})
363 action='permission_application', conditions={'method': ['GET']})
383
364
384 m.connect('admin_permissions_global', '/permissions/global',
365 m.connect('admin_permissions_global', '/permissions/global',
385 action='permission_global_update', conditions={'method': ['POST']})
366 action='permission_global_update', conditions={'method': ['POST']})
386 m.connect('admin_permissions_global', '/permissions/global',
367 m.connect('admin_permissions_global', '/permissions/global',
387 action='permission_global', conditions={'method': ['GET']})
368 action='permission_global', conditions={'method': ['GET']})
388
369
389 m.connect('admin_permissions_object', '/permissions/object',
370 m.connect('admin_permissions_object', '/permissions/object',
390 action='permission_objects_update', conditions={'method': ['POST']})
371 action='permission_objects_update', conditions={'method': ['POST']})
391 m.connect('admin_permissions_object', '/permissions/object',
372 m.connect('admin_permissions_object', '/permissions/object',
392 action='permission_objects', conditions={'method': ['GET']})
373 action='permission_objects', conditions={'method': ['GET']})
393
374
394 m.connect('admin_permissions_ips', '/permissions/ips',
375 m.connect('admin_permissions_ips', '/permissions/ips',
395 action='permission_ips', conditions={'method': ['POST']})
376 action='permission_ips', conditions={'method': ['POST']})
396 m.connect('admin_permissions_ips', '/permissions/ips',
377 m.connect('admin_permissions_ips', '/permissions/ips',
397 action='permission_ips', conditions={'method': ['GET']})
378 action='permission_ips', conditions={'method': ['GET']})
398
379
399 m.connect('admin_permissions_overview', '/permissions/overview',
380 m.connect('admin_permissions_overview', '/permissions/overview',
400 action='permission_perms', conditions={'method': ['GET']})
381 action='permission_perms', conditions={'method': ['GET']})
401
382
402 # ADMIN DEFAULTS REST ROUTES
383 # ADMIN DEFAULTS REST ROUTES
403 with rmap.submapper(path_prefix=ADMIN_PREFIX,
384 with rmap.submapper(path_prefix=ADMIN_PREFIX,
404 controller='admin/defaults') as m:
385 controller='admin/defaults') as m:
405 m.connect('admin_defaults_repositories', '/defaults/repositories',
386 m.connect('admin_defaults_repositories', '/defaults/repositories',
406 action='update_repository_defaults', conditions={'method': ['POST']})
387 action='update_repository_defaults', conditions={'method': ['POST']})
407 m.connect('admin_defaults_repositories', '/defaults/repositories',
388 m.connect('admin_defaults_repositories', '/defaults/repositories',
408 action='index', conditions={'method': ['GET']})
389 action='index', conditions={'method': ['GET']})
409
390
410 # ADMIN DEBUG STYLE ROUTES
391 # ADMIN DEBUG STYLE ROUTES
411 if str2bool(config.get('debug_style')):
392 if str2bool(config.get('debug_style')):
412 with rmap.submapper(path_prefix=ADMIN_PREFIX + '/debug_style',
393 with rmap.submapper(path_prefix=ADMIN_PREFIX + '/debug_style',
413 controller='debug_style') as m:
394 controller='debug_style') as m:
414 m.connect('debug_style_home', '',
395 m.connect('debug_style_home', '',
415 action='index', conditions={'method': ['GET']})
396 action='index', conditions={'method': ['GET']})
416 m.connect('debug_style_template', '/t/{t_path}',
397 m.connect('debug_style_template', '/t/{t_path}',
417 action='template', conditions={'method': ['GET']})
398 action='template', conditions={'method': ['GET']})
418
399
419 # ADMIN SETTINGS ROUTES
400 # ADMIN SETTINGS ROUTES
420 with rmap.submapper(path_prefix=ADMIN_PREFIX,
401 with rmap.submapper(path_prefix=ADMIN_PREFIX,
421 controller='admin/settings') as m:
402 controller='admin/settings') as m:
422
403
423 # default
404 # default
424 m.connect('admin_settings', '/settings',
405 m.connect('admin_settings', '/settings',
425 action='settings_global_update',
406 action='settings_global_update',
426 conditions={'method': ['POST']})
407 conditions={'method': ['POST']})
427 m.connect('admin_settings', '/settings',
408 m.connect('admin_settings', '/settings',
428 action='settings_global', conditions={'method': ['GET']})
409 action='settings_global', conditions={'method': ['GET']})
429
410
430 m.connect('admin_settings_vcs', '/settings/vcs',
411 m.connect('admin_settings_vcs', '/settings/vcs',
431 action='settings_vcs_update',
412 action='settings_vcs_update',
432 conditions={'method': ['POST']})
413 conditions={'method': ['POST']})
433 m.connect('admin_settings_vcs', '/settings/vcs',
414 m.connect('admin_settings_vcs', '/settings/vcs',
434 action='settings_vcs',
415 action='settings_vcs',
435 conditions={'method': ['GET']})
416 conditions={'method': ['GET']})
436 m.connect('admin_settings_vcs', '/settings/vcs',
417 m.connect('admin_settings_vcs', '/settings/vcs',
437 action='delete_svn_pattern',
418 action='delete_svn_pattern',
438 conditions={'method': ['DELETE']})
419 conditions={'method': ['DELETE']})
439
420
440 m.connect('admin_settings_mapping', '/settings/mapping',
421 m.connect('admin_settings_mapping', '/settings/mapping',
441 action='settings_mapping_update',
422 action='settings_mapping_update',
442 conditions={'method': ['POST']})
423 conditions={'method': ['POST']})
443 m.connect('admin_settings_mapping', '/settings/mapping',
424 m.connect('admin_settings_mapping', '/settings/mapping',
444 action='settings_mapping', conditions={'method': ['GET']})
425 action='settings_mapping', conditions={'method': ['GET']})
445
426
446 m.connect('admin_settings_global', '/settings/global',
427 m.connect('admin_settings_global', '/settings/global',
447 action='settings_global_update',
428 action='settings_global_update',
448 conditions={'method': ['POST']})
429 conditions={'method': ['POST']})
449 m.connect('admin_settings_global', '/settings/global',
430 m.connect('admin_settings_global', '/settings/global',
450 action='settings_global', conditions={'method': ['GET']})
431 action='settings_global', conditions={'method': ['GET']})
451
432
452 m.connect('admin_settings_visual', '/settings/visual',
433 m.connect('admin_settings_visual', '/settings/visual',
453 action='settings_visual_update',
434 action='settings_visual_update',
454 conditions={'method': ['POST']})
435 conditions={'method': ['POST']})
455 m.connect('admin_settings_visual', '/settings/visual',
436 m.connect('admin_settings_visual', '/settings/visual',
456 action='settings_visual', conditions={'method': ['GET']})
437 action='settings_visual', conditions={'method': ['GET']})
457
438
458 m.connect('admin_settings_issuetracker',
439 m.connect('admin_settings_issuetracker',
459 '/settings/issue-tracker', action='settings_issuetracker',
440 '/settings/issue-tracker', action='settings_issuetracker',
460 conditions={'method': ['GET']})
441 conditions={'method': ['GET']})
461 m.connect('admin_settings_issuetracker_save',
442 m.connect('admin_settings_issuetracker_save',
462 '/settings/issue-tracker/save',
443 '/settings/issue-tracker/save',
463 action='settings_issuetracker_save',
444 action='settings_issuetracker_save',
464 conditions={'method': ['POST']})
445 conditions={'method': ['POST']})
465 m.connect('admin_issuetracker_test', '/settings/issue-tracker/test',
446 m.connect('admin_issuetracker_test', '/settings/issue-tracker/test',
466 action='settings_issuetracker_test',
447 action='settings_issuetracker_test',
467 conditions={'method': ['POST']})
448 conditions={'method': ['POST']})
468 m.connect('admin_issuetracker_delete',
449 m.connect('admin_issuetracker_delete',
469 '/settings/issue-tracker/delete',
450 '/settings/issue-tracker/delete',
470 action='settings_issuetracker_delete',
451 action='settings_issuetracker_delete',
471 conditions={'method': ['DELETE']})
452 conditions={'method': ['DELETE']})
472
453
473 m.connect('admin_settings_email', '/settings/email',
454 m.connect('admin_settings_email', '/settings/email',
474 action='settings_email_update',
455 action='settings_email_update',
475 conditions={'method': ['POST']})
456 conditions={'method': ['POST']})
476 m.connect('admin_settings_email', '/settings/email',
457 m.connect('admin_settings_email', '/settings/email',
477 action='settings_email', conditions={'method': ['GET']})
458 action='settings_email', conditions={'method': ['GET']})
478
459
479 m.connect('admin_settings_hooks', '/settings/hooks',
460 m.connect('admin_settings_hooks', '/settings/hooks',
480 action='settings_hooks_update',
461 action='settings_hooks_update',
481 conditions={'method': ['POST', 'DELETE']})
462 conditions={'method': ['POST', 'DELETE']})
482 m.connect('admin_settings_hooks', '/settings/hooks',
463 m.connect('admin_settings_hooks', '/settings/hooks',
483 action='settings_hooks', conditions={'method': ['GET']})
464 action='settings_hooks', conditions={'method': ['GET']})
484
465
485 m.connect('admin_settings_search', '/settings/search',
466 m.connect('admin_settings_search', '/settings/search',
486 action='settings_search', conditions={'method': ['GET']})
467 action='settings_search', conditions={'method': ['GET']})
487
468
488 m.connect('admin_settings_supervisor', '/settings/supervisor',
469 m.connect('admin_settings_supervisor', '/settings/supervisor',
489 action='settings_supervisor', conditions={'method': ['GET']})
470 action='settings_supervisor', conditions={'method': ['GET']})
490 m.connect('admin_settings_supervisor_log', '/settings/supervisor/{procid}/log',
471 m.connect('admin_settings_supervisor_log', '/settings/supervisor/{procid}/log',
491 action='settings_supervisor_log', conditions={'method': ['GET']})
472 action='settings_supervisor_log', conditions={'method': ['GET']})
492
473
493 m.connect('admin_settings_labs', '/settings/labs',
474 m.connect('admin_settings_labs', '/settings/labs',
494 action='settings_labs_update',
475 action='settings_labs_update',
495 conditions={'method': ['POST']})
476 conditions={'method': ['POST']})
496 m.connect('admin_settings_labs', '/settings/labs',
477 m.connect('admin_settings_labs', '/settings/labs',
497 action='settings_labs', conditions={'method': ['GET']})
478 action='settings_labs', conditions={'method': ['GET']})
498
479
499 # ADMIN MY ACCOUNT
480 # ADMIN MY ACCOUNT
500 with rmap.submapper(path_prefix=ADMIN_PREFIX,
481 with rmap.submapper(path_prefix=ADMIN_PREFIX,
501 controller='admin/my_account') as m:
482 controller='admin/my_account') as m:
502
483
503 m.connect('my_account_edit', '/my_account/edit',
484 m.connect('my_account_edit', '/my_account/edit',
504 action='my_account_edit', conditions={'method': ['GET']})
485 action='my_account_edit', conditions={'method': ['GET']})
505 m.connect('my_account', '/my_account/update',
486 m.connect('my_account', '/my_account/update',
506 action='my_account_update', conditions={'method': ['POST']})
487 action='my_account_update', conditions={'method': ['POST']})
507
488
508 # NOTE(marcink): this needs to be kept for password force flag to be
489 # NOTE(marcink): this needs to be kept for password force flag to be
509 # handler, remove after migration to pyramid
490 # handler, remove after migration to pyramid
510 m.connect('my_account_password', '/my_account/password',
491 m.connect('my_account_password', '/my_account/password',
511 action='my_account_password', conditions={'method': ['GET']})
492 action='my_account_password', conditions={'method': ['GET']})
512
493
513 m.connect('my_account_repos', '/my_account/repos',
494 m.connect('my_account_repos', '/my_account/repos',
514 action='my_account_repos', conditions={'method': ['GET']})
495 action='my_account_repos', conditions={'method': ['GET']})
515
496
516 m.connect('my_account_watched', '/my_account/watched',
497 m.connect('my_account_watched', '/my_account/watched',
517 action='my_account_watched', conditions={'method': ['GET']})
498 action='my_account_watched', conditions={'method': ['GET']})
518
499
519 m.connect('my_account_pullrequests', '/my_account/pull_requests',
500 m.connect('my_account_pullrequests', '/my_account/pull_requests',
520 action='my_account_pullrequests', conditions={'method': ['GET']})
501 action='my_account_pullrequests', conditions={'method': ['GET']})
521
502
522 m.connect('my_account_perms', '/my_account/perms',
503 m.connect('my_account_perms', '/my_account/perms',
523 action='my_account_perms', conditions={'method': ['GET']})
504 action='my_account_perms', conditions={'method': ['GET']})
524
505
525 m.connect('my_account_emails', '/my_account/emails',
506 m.connect('my_account_emails', '/my_account/emails',
526 action='my_account_emails', conditions={'method': ['GET']})
507 action='my_account_emails', conditions={'method': ['GET']})
527 m.connect('my_account_emails', '/my_account/emails',
508 m.connect('my_account_emails', '/my_account/emails',
528 action='my_account_emails_add', conditions={'method': ['POST']})
509 action='my_account_emails_add', conditions={'method': ['POST']})
529 m.connect('my_account_emails', '/my_account/emails',
510 m.connect('my_account_emails', '/my_account/emails',
530 action='my_account_emails_delete', conditions={'method': ['DELETE']})
511 action='my_account_emails_delete', conditions={'method': ['DELETE']})
531
512
532 m.connect('my_account_notifications', '/my_account/notifications',
513 m.connect('my_account_notifications', '/my_account/notifications',
533 action='my_notifications',
514 action='my_notifications',
534 conditions={'method': ['GET']})
515 conditions={'method': ['GET']})
535 m.connect('my_account_notifications_toggle_visibility',
516 m.connect('my_account_notifications_toggle_visibility',
536 '/my_account/toggle_visibility',
517 '/my_account/toggle_visibility',
537 action='my_notifications_toggle_visibility',
518 action='my_notifications_toggle_visibility',
538 conditions={'method': ['POST']})
519 conditions={'method': ['POST']})
539 m.connect('my_account_notifications_test_channelstream',
520 m.connect('my_account_notifications_test_channelstream',
540 '/my_account/test_channelstream',
521 '/my_account/test_channelstream',
541 action='my_account_notifications_test_channelstream',
522 action='my_account_notifications_test_channelstream',
542 conditions={'method': ['POST']})
523 conditions={'method': ['POST']})
543
524
544 # NOTIFICATION REST ROUTES
525 # NOTIFICATION REST ROUTES
545 with rmap.submapper(path_prefix=ADMIN_PREFIX,
526 with rmap.submapper(path_prefix=ADMIN_PREFIX,
546 controller='admin/notifications') as m:
527 controller='admin/notifications') as m:
547 m.connect('notifications', '/notifications',
528 m.connect('notifications', '/notifications',
548 action='index', conditions={'method': ['GET']})
529 action='index', conditions={'method': ['GET']})
549 m.connect('notifications_mark_all_read', '/notifications/mark_all_read',
530 m.connect('notifications_mark_all_read', '/notifications/mark_all_read',
550 action='mark_all_read', conditions={'method': ['POST']})
531 action='mark_all_read', conditions={'method': ['POST']})
551 m.connect('/notifications/{notification_id}',
532 m.connect('/notifications/{notification_id}',
552 action='update', conditions={'method': ['PUT']})
533 action='update', conditions={'method': ['PUT']})
553 m.connect('/notifications/{notification_id}',
534 m.connect('/notifications/{notification_id}',
554 action='delete', conditions={'method': ['DELETE']})
535 action='delete', conditions={'method': ['DELETE']})
555 m.connect('notification', '/notifications/{notification_id}',
536 m.connect('notification', '/notifications/{notification_id}',
556 action='show', conditions={'method': ['GET']})
537 action='show', conditions={'method': ['GET']})
557
538
558 # ADMIN GIST
539 # ADMIN GIST
559 with rmap.submapper(path_prefix=ADMIN_PREFIX,
540 with rmap.submapper(path_prefix=ADMIN_PREFIX,
560 controller='admin/gists') as m:
541 controller='admin/gists') as m:
561 m.connect('gists', '/gists',
542 m.connect('gists', '/gists',
562 action='create', conditions={'method': ['POST']})
543 action='create', conditions={'method': ['POST']})
563 m.connect('gists', '/gists', jsroute=True,
544 m.connect('gists', '/gists', jsroute=True,
564 action='index', conditions={'method': ['GET']})
545 action='index', conditions={'method': ['GET']})
565 m.connect('new_gist', '/gists/new', jsroute=True,
546 m.connect('new_gist', '/gists/new', jsroute=True,
566 action='new', conditions={'method': ['GET']})
547 action='new', conditions={'method': ['GET']})
567
548
568 m.connect('/gists/{gist_id}',
549 m.connect('/gists/{gist_id}',
569 action='delete', conditions={'method': ['DELETE']})
550 action='delete', conditions={'method': ['DELETE']})
570 m.connect('edit_gist', '/gists/{gist_id}/edit',
551 m.connect('edit_gist', '/gists/{gist_id}/edit',
571 action='edit_form', conditions={'method': ['GET']})
552 action='edit_form', conditions={'method': ['GET']})
572 m.connect('edit_gist', '/gists/{gist_id}/edit',
553 m.connect('edit_gist', '/gists/{gist_id}/edit',
573 action='edit', conditions={'method': ['POST']})
554 action='edit', conditions={'method': ['POST']})
574 m.connect(
555 m.connect(
575 'edit_gist_check_revision', '/gists/{gist_id}/edit/check_revision',
556 'edit_gist_check_revision', '/gists/{gist_id}/edit/check_revision',
576 action='check_revision', conditions={'method': ['GET']})
557 action='check_revision', conditions={'method': ['GET']})
577
558
578 m.connect('gist', '/gists/{gist_id}',
559 m.connect('gist', '/gists/{gist_id}',
579 action='show', conditions={'method': ['GET']})
560 action='show', conditions={'method': ['GET']})
580 m.connect('gist_rev', '/gists/{gist_id}/{revision}',
561 m.connect('gist_rev', '/gists/{gist_id}/{revision}',
581 revision='tip',
562 revision='tip',
582 action='show', conditions={'method': ['GET']})
563 action='show', conditions={'method': ['GET']})
583 m.connect('formatted_gist', '/gists/{gist_id}/{revision}/{format}',
564 m.connect('formatted_gist', '/gists/{gist_id}/{revision}/{format}',
584 revision='tip',
565 revision='tip',
585 action='show', conditions={'method': ['GET']})
566 action='show', conditions={'method': ['GET']})
586 m.connect('formatted_gist_file', '/gists/{gist_id}/{revision}/{format}/{f_path}',
567 m.connect('formatted_gist_file', '/gists/{gist_id}/{revision}/{format}/{f_path}',
587 revision='tip',
568 revision='tip',
588 action='show', conditions={'method': ['GET']},
569 action='show', conditions={'method': ['GET']},
589 requirements=URL_NAME_REQUIREMENTS)
570 requirements=URL_NAME_REQUIREMENTS)
590
571
591 # ADMIN MAIN PAGES
572 # ADMIN MAIN PAGES
592 with rmap.submapper(path_prefix=ADMIN_PREFIX,
573 with rmap.submapper(path_prefix=ADMIN_PREFIX,
593 controller='admin/admin') as m:
574 controller='admin/admin') as m:
594 m.connect('admin_home', '', action='index')
575 m.connect('admin_home', '', action='index')
595 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
576 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
596 action='add_repo')
577 action='add_repo')
597 m.connect(
578 m.connect(
598 'pull_requests_global_0', '/pull_requests/{pull_request_id:[0-9]+}',
579 'pull_requests_global_0', '/pull_requests/{pull_request_id:[0-9]+}',
599 action='pull_requests')
580 action='pull_requests')
600 m.connect(
581 m.connect(
601 'pull_requests_global_1', '/pull-requests/{pull_request_id:[0-9]+}',
582 'pull_requests_global_1', '/pull-requests/{pull_request_id:[0-9]+}',
602 action='pull_requests')
583 action='pull_requests')
603 m.connect(
584 m.connect(
604 'pull_requests_global', '/pull-request/{pull_request_id:[0-9]+}',
585 'pull_requests_global', '/pull-request/{pull_request_id:[0-9]+}',
605 action='pull_requests')
586 action='pull_requests')
606
587
607 # USER JOURNAL
588 # USER JOURNAL
608 rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,),
589 rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,),
609 controller='journal', action='index')
590 controller='journal', action='index')
610 rmap.connect('journal_rss', '%s/journal/rss' % (ADMIN_PREFIX,),
591 rmap.connect('journal_rss', '%s/journal/rss' % (ADMIN_PREFIX,),
611 controller='journal', action='journal_rss')
592 controller='journal', action='journal_rss')
612 rmap.connect('journal_atom', '%s/journal/atom' % (ADMIN_PREFIX,),
593 rmap.connect('journal_atom', '%s/journal/atom' % (ADMIN_PREFIX,),
613 controller='journal', action='journal_atom')
594 controller='journal', action='journal_atom')
614
595
615 rmap.connect('public_journal', '%s/public_journal' % (ADMIN_PREFIX,),
596 rmap.connect('public_journal', '%s/public_journal' % (ADMIN_PREFIX,),
616 controller='journal', action='public_journal')
597 controller='journal', action='public_journal')
617
598
618 rmap.connect('public_journal_rss', '%s/public_journal/rss' % (ADMIN_PREFIX,),
599 rmap.connect('public_journal_rss', '%s/public_journal/rss' % (ADMIN_PREFIX,),
619 controller='journal', action='public_journal_rss')
600 controller='journal', action='public_journal_rss')
620
601
621 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % (ADMIN_PREFIX,),
602 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % (ADMIN_PREFIX,),
622 controller='journal', action='public_journal_rss')
603 controller='journal', action='public_journal_rss')
623
604
624 rmap.connect('public_journal_atom',
605 rmap.connect('public_journal_atom',
625 '%s/public_journal/atom' % (ADMIN_PREFIX,), controller='journal',
606 '%s/public_journal/atom' % (ADMIN_PREFIX,), controller='journal',
626 action='public_journal_atom')
607 action='public_journal_atom')
627
608
628 rmap.connect('public_journal_atom_old',
609 rmap.connect('public_journal_atom_old',
629 '%s/public_journal_atom' % (ADMIN_PREFIX,), controller='journal',
610 '%s/public_journal_atom' % (ADMIN_PREFIX,), controller='journal',
630 action='public_journal_atom')
611 action='public_journal_atom')
631
612
632 rmap.connect('toggle_following', '%s/toggle_following' % (ADMIN_PREFIX,),
613 rmap.connect('toggle_following', '%s/toggle_following' % (ADMIN_PREFIX,),
633 controller='journal', action='toggle_following', jsroute=True,
614 controller='journal', action='toggle_following', jsroute=True,
634 conditions={'method': ['POST']})
615 conditions={'method': ['POST']})
635
616
636 # FULL TEXT SEARCH
617 # FULL TEXT SEARCH
637 rmap.connect('search', '%s/search' % (ADMIN_PREFIX,),
618 rmap.connect('search', '%s/search' % (ADMIN_PREFIX,),
638 controller='search')
619 controller='search')
639 rmap.connect('search_repo_home', '/{repo_name}/search',
620 rmap.connect('search_repo_home', '/{repo_name}/search',
640 controller='search',
621 controller='search',
641 action='index',
622 action='index',
642 conditions={'function': check_repo},
623 conditions={'function': check_repo},
643 requirements=URL_NAME_REQUIREMENTS)
624 requirements=URL_NAME_REQUIREMENTS)
644
625
645 # FEEDS
626 # FEEDS
646 rmap.connect('rss_feed_home', '/{repo_name}/feed/rss',
627 rmap.connect('rss_feed_home', '/{repo_name}/feed/rss',
647 controller='feed', action='rss',
628 controller='feed', action='rss',
648 conditions={'function': check_repo},
629 conditions={'function': check_repo},
649 requirements=URL_NAME_REQUIREMENTS)
630 requirements=URL_NAME_REQUIREMENTS)
650
631
651 rmap.connect('atom_feed_home', '/{repo_name}/feed/atom',
632 rmap.connect('atom_feed_home', '/{repo_name}/feed/atom',
652 controller='feed', action='atom',
633 controller='feed', action='atom',
653 conditions={'function': check_repo},
634 conditions={'function': check_repo},
654 requirements=URL_NAME_REQUIREMENTS)
635 requirements=URL_NAME_REQUIREMENTS)
655
636
656 #==========================================================================
637 #==========================================================================
657 # REPOSITORY ROUTES
638 # REPOSITORY ROUTES
658 #==========================================================================
639 #==========================================================================
659
640
660 rmap.connect('repo_creating_home', '/{repo_name}/repo_creating',
641 rmap.connect('repo_creating_home', '/{repo_name}/repo_creating',
661 controller='admin/repos', action='repo_creating',
642 controller='admin/repos', action='repo_creating',
662 requirements=URL_NAME_REQUIREMENTS)
643 requirements=URL_NAME_REQUIREMENTS)
663 rmap.connect('repo_check_home', '/{repo_name}/crepo_check',
644 rmap.connect('repo_check_home', '/{repo_name}/crepo_check',
664 controller='admin/repos', action='repo_check',
645 controller='admin/repos', action='repo_check',
665 requirements=URL_NAME_REQUIREMENTS)
646 requirements=URL_NAME_REQUIREMENTS)
666
647
667 rmap.connect('repo_stats', '/{repo_name}/repo_stats/{commit_id}',
648 rmap.connect('repo_stats', '/{repo_name}/repo_stats/{commit_id}',
668 controller='summary', action='repo_stats',
649 controller='summary', action='repo_stats',
669 conditions={'function': check_repo},
650 conditions={'function': check_repo},
670 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
651 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
671
652
672 rmap.connect('repo_refs_data', '/{repo_name}/refs-data',
653 rmap.connect('repo_refs_data', '/{repo_name}/refs-data',
673 controller='summary', action='repo_refs_data',
654 controller='summary', action='repo_refs_data',
674 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
655 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
675 rmap.connect('repo_refs_changelog_data', '/{repo_name}/refs-data-changelog',
656 rmap.connect('repo_refs_changelog_data', '/{repo_name}/refs-data-changelog',
676 controller='summary', action='repo_refs_changelog_data',
657 controller='summary', action='repo_refs_changelog_data',
677 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
658 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
678 rmap.connect('repo_default_reviewers_data', '/{repo_name}/default-reviewers',
659 rmap.connect('repo_default_reviewers_data', '/{repo_name}/default-reviewers',
679 controller='summary', action='repo_default_reviewers_data',
660 controller='summary', action='repo_default_reviewers_data',
680 jsroute=True, requirements=URL_NAME_REQUIREMENTS)
661 jsroute=True, requirements=URL_NAME_REQUIREMENTS)
681
662
682 rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}',
663 rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}',
683 controller='changeset', revision='tip',
664 controller='changeset', revision='tip',
684 conditions={'function': check_repo},
665 conditions={'function': check_repo},
685 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
666 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
686 rmap.connect('changeset_children', '/{repo_name}/changeset_children/{revision}',
667 rmap.connect('changeset_children', '/{repo_name}/changeset_children/{revision}',
687 controller='changeset', revision='tip', action='changeset_children',
668 controller='changeset', revision='tip', action='changeset_children',
688 conditions={'function': check_repo},
669 conditions={'function': check_repo},
689 requirements=URL_NAME_REQUIREMENTS)
670 requirements=URL_NAME_REQUIREMENTS)
690 rmap.connect('changeset_parents', '/{repo_name}/changeset_parents/{revision}',
671 rmap.connect('changeset_parents', '/{repo_name}/changeset_parents/{revision}',
691 controller='changeset', revision='tip', action='changeset_parents',
672 controller='changeset', revision='tip', action='changeset_parents',
692 conditions={'function': check_repo},
673 conditions={'function': check_repo},
693 requirements=URL_NAME_REQUIREMENTS)
674 requirements=URL_NAME_REQUIREMENTS)
694
675
695 # repo edit options
676 # repo edit options
696 rmap.connect('edit_repo', '/{repo_name}/settings', jsroute=True,
677 rmap.connect('edit_repo', '/{repo_name}/settings', jsroute=True,
697 controller='admin/repos', action='edit',
678 controller='admin/repos', action='edit',
698 conditions={'method': ['GET'], 'function': check_repo},
679 conditions={'method': ['GET'], 'function': check_repo},
699 requirements=URL_NAME_REQUIREMENTS)
680 requirements=URL_NAME_REQUIREMENTS)
700
681
701 rmap.connect('edit_repo_perms', '/{repo_name}/settings/permissions',
682 rmap.connect('edit_repo_perms', '/{repo_name}/settings/permissions',
702 jsroute=True,
683 jsroute=True,
703 controller='admin/repos', action='edit_permissions',
684 controller='admin/repos', action='edit_permissions',
704 conditions={'method': ['GET'], 'function': check_repo},
685 conditions={'method': ['GET'], 'function': check_repo},
705 requirements=URL_NAME_REQUIREMENTS)
686 requirements=URL_NAME_REQUIREMENTS)
706 rmap.connect('edit_repo_perms_update', '/{repo_name}/settings/permissions',
687 rmap.connect('edit_repo_perms_update', '/{repo_name}/settings/permissions',
707 controller='admin/repos', action='edit_permissions_update',
688 controller='admin/repos', action='edit_permissions_update',
708 conditions={'method': ['PUT'], 'function': check_repo},
689 conditions={'method': ['PUT'], 'function': check_repo},
709 requirements=URL_NAME_REQUIREMENTS)
690 requirements=URL_NAME_REQUIREMENTS)
710
691
711 rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields',
692 rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields',
712 controller='admin/repos', action='edit_fields',
693 controller='admin/repos', action='edit_fields',
713 conditions={'method': ['GET'], 'function': check_repo},
694 conditions={'method': ['GET'], 'function': check_repo},
714 requirements=URL_NAME_REQUIREMENTS)
695 requirements=URL_NAME_REQUIREMENTS)
715 rmap.connect('create_repo_fields', '/{repo_name}/settings/fields/new',
696 rmap.connect('create_repo_fields', '/{repo_name}/settings/fields/new',
716 controller='admin/repos', action='create_repo_field',
697 controller='admin/repos', action='create_repo_field',
717 conditions={'method': ['PUT'], 'function': check_repo},
698 conditions={'method': ['PUT'], 'function': check_repo},
718 requirements=URL_NAME_REQUIREMENTS)
699 requirements=URL_NAME_REQUIREMENTS)
719 rmap.connect('delete_repo_fields', '/{repo_name}/settings/fields/{field_id}',
700 rmap.connect('delete_repo_fields', '/{repo_name}/settings/fields/{field_id}',
720 controller='admin/repos', action='delete_repo_field',
701 controller='admin/repos', action='delete_repo_field',
721 conditions={'method': ['DELETE'], 'function': check_repo},
702 conditions={'method': ['DELETE'], 'function': check_repo},
722 requirements=URL_NAME_REQUIREMENTS)
703 requirements=URL_NAME_REQUIREMENTS)
723
704
724 rmap.connect('edit_repo_advanced', '/{repo_name}/settings/advanced',
705 rmap.connect('edit_repo_advanced', '/{repo_name}/settings/advanced',
725 controller='admin/repos', action='edit_advanced',
706 controller='admin/repos', action='edit_advanced',
726 conditions={'method': ['GET'], 'function': check_repo},
707 conditions={'method': ['GET'], 'function': check_repo},
727 requirements=URL_NAME_REQUIREMENTS)
708 requirements=URL_NAME_REQUIREMENTS)
728
709
729 rmap.connect('edit_repo_advanced_locking', '/{repo_name}/settings/advanced/locking',
710 rmap.connect('edit_repo_advanced_locking', '/{repo_name}/settings/advanced/locking',
730 controller='admin/repos', action='edit_advanced_locking',
711 controller='admin/repos', action='edit_advanced_locking',
731 conditions={'method': ['PUT'], 'function': check_repo},
712 conditions={'method': ['PUT'], 'function': check_repo},
732 requirements=URL_NAME_REQUIREMENTS)
713 requirements=URL_NAME_REQUIREMENTS)
733 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
714 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
734 controller='admin/repos', action='toggle_locking',
715 controller='admin/repos', action='toggle_locking',
735 conditions={'method': ['GET'], 'function': check_repo},
716 conditions={'method': ['GET'], 'function': check_repo},
736 requirements=URL_NAME_REQUIREMENTS)
717 requirements=URL_NAME_REQUIREMENTS)
737
718
738 rmap.connect('edit_repo_advanced_journal', '/{repo_name}/settings/advanced/journal',
719 rmap.connect('edit_repo_advanced_journal', '/{repo_name}/settings/advanced/journal',
739 controller='admin/repos', action='edit_advanced_journal',
720 controller='admin/repos', action='edit_advanced_journal',
740 conditions={'method': ['PUT'], 'function': check_repo},
721 conditions={'method': ['PUT'], 'function': check_repo},
741 requirements=URL_NAME_REQUIREMENTS)
722 requirements=URL_NAME_REQUIREMENTS)
742
723
743 rmap.connect('edit_repo_advanced_fork', '/{repo_name}/settings/advanced/fork',
724 rmap.connect('edit_repo_advanced_fork', '/{repo_name}/settings/advanced/fork',
744 controller='admin/repos', action='edit_advanced_fork',
725 controller='admin/repos', action='edit_advanced_fork',
745 conditions={'method': ['PUT'], 'function': check_repo},
726 conditions={'method': ['PUT'], 'function': check_repo},
746 requirements=URL_NAME_REQUIREMENTS)
727 requirements=URL_NAME_REQUIREMENTS)
747
728
748 rmap.connect('edit_repo_caches', '/{repo_name}/settings/caches',
729 rmap.connect('edit_repo_caches', '/{repo_name}/settings/caches',
749 controller='admin/repos', action='edit_caches_form',
730 controller='admin/repos', action='edit_caches_form',
750 conditions={'method': ['GET'], 'function': check_repo},
731 conditions={'method': ['GET'], 'function': check_repo},
751 requirements=URL_NAME_REQUIREMENTS)
732 requirements=URL_NAME_REQUIREMENTS)
752 rmap.connect('edit_repo_caches', '/{repo_name}/settings/caches',
733 rmap.connect('edit_repo_caches', '/{repo_name}/settings/caches',
753 controller='admin/repos', action='edit_caches',
734 controller='admin/repos', action='edit_caches',
754 conditions={'method': ['PUT'], 'function': check_repo},
735 conditions={'method': ['PUT'], 'function': check_repo},
755 requirements=URL_NAME_REQUIREMENTS)
736 requirements=URL_NAME_REQUIREMENTS)
756
737
757 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
738 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
758 controller='admin/repos', action='edit_remote_form',
739 controller='admin/repos', action='edit_remote_form',
759 conditions={'method': ['GET'], 'function': check_repo},
740 conditions={'method': ['GET'], 'function': check_repo},
760 requirements=URL_NAME_REQUIREMENTS)
741 requirements=URL_NAME_REQUIREMENTS)
761 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
742 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
762 controller='admin/repos', action='edit_remote',
743 controller='admin/repos', action='edit_remote',
763 conditions={'method': ['PUT'], 'function': check_repo},
744 conditions={'method': ['PUT'], 'function': check_repo},
764 requirements=URL_NAME_REQUIREMENTS)
745 requirements=URL_NAME_REQUIREMENTS)
765
746
766 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
747 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
767 controller='admin/repos', action='edit_statistics_form',
748 controller='admin/repos', action='edit_statistics_form',
768 conditions={'method': ['GET'], 'function': check_repo},
749 conditions={'method': ['GET'], 'function': check_repo},
769 requirements=URL_NAME_REQUIREMENTS)
750 requirements=URL_NAME_REQUIREMENTS)
770 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
751 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
771 controller='admin/repos', action='edit_statistics',
752 controller='admin/repos', action='edit_statistics',
772 conditions={'method': ['PUT'], 'function': check_repo},
753 conditions={'method': ['PUT'], 'function': check_repo},
773 requirements=URL_NAME_REQUIREMENTS)
754 requirements=URL_NAME_REQUIREMENTS)
774 rmap.connect('repo_settings_issuetracker',
755 rmap.connect('repo_settings_issuetracker',
775 '/{repo_name}/settings/issue-tracker',
756 '/{repo_name}/settings/issue-tracker',
776 controller='admin/repos', action='repo_issuetracker',
757 controller='admin/repos', action='repo_issuetracker',
777 conditions={'method': ['GET'], 'function': check_repo},
758 conditions={'method': ['GET'], 'function': check_repo},
778 requirements=URL_NAME_REQUIREMENTS)
759 requirements=URL_NAME_REQUIREMENTS)
779 rmap.connect('repo_issuetracker_test',
760 rmap.connect('repo_issuetracker_test',
780 '/{repo_name}/settings/issue-tracker/test',
761 '/{repo_name}/settings/issue-tracker/test',
781 controller='admin/repos', action='repo_issuetracker_test',
762 controller='admin/repos', action='repo_issuetracker_test',
782 conditions={'method': ['POST'], 'function': check_repo},
763 conditions={'method': ['POST'], 'function': check_repo},
783 requirements=URL_NAME_REQUIREMENTS)
764 requirements=URL_NAME_REQUIREMENTS)
784 rmap.connect('repo_issuetracker_delete',
765 rmap.connect('repo_issuetracker_delete',
785 '/{repo_name}/settings/issue-tracker/delete',
766 '/{repo_name}/settings/issue-tracker/delete',
786 controller='admin/repos', action='repo_issuetracker_delete',
767 controller='admin/repos', action='repo_issuetracker_delete',
787 conditions={'method': ['DELETE'], 'function': check_repo},
768 conditions={'method': ['DELETE'], 'function': check_repo},
788 requirements=URL_NAME_REQUIREMENTS)
769 requirements=URL_NAME_REQUIREMENTS)
789 rmap.connect('repo_issuetracker_save',
770 rmap.connect('repo_issuetracker_save',
790 '/{repo_name}/settings/issue-tracker/save',
771 '/{repo_name}/settings/issue-tracker/save',
791 controller='admin/repos', action='repo_issuetracker_save',
772 controller='admin/repos', action='repo_issuetracker_save',
792 conditions={'method': ['POST'], 'function': check_repo},
773 conditions={'method': ['POST'], 'function': check_repo},
793 requirements=URL_NAME_REQUIREMENTS)
774 requirements=URL_NAME_REQUIREMENTS)
794 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
775 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
795 controller='admin/repos', action='repo_settings_vcs_update',
776 controller='admin/repos', action='repo_settings_vcs_update',
796 conditions={'method': ['POST'], 'function': check_repo},
777 conditions={'method': ['POST'], 'function': check_repo},
797 requirements=URL_NAME_REQUIREMENTS)
778 requirements=URL_NAME_REQUIREMENTS)
798 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
779 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
799 controller='admin/repos', action='repo_settings_vcs',
780 controller='admin/repos', action='repo_settings_vcs',
800 conditions={'method': ['GET'], 'function': check_repo},
781 conditions={'method': ['GET'], 'function': check_repo},
801 requirements=URL_NAME_REQUIREMENTS)
782 requirements=URL_NAME_REQUIREMENTS)
802 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
783 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
803 controller='admin/repos', action='repo_delete_svn_pattern',
784 controller='admin/repos', action='repo_delete_svn_pattern',
804 conditions={'method': ['DELETE'], 'function': check_repo},
785 conditions={'method': ['DELETE'], 'function': check_repo},
805 requirements=URL_NAME_REQUIREMENTS)
786 requirements=URL_NAME_REQUIREMENTS)
806 rmap.connect('repo_pullrequest_settings', '/{repo_name}/settings/pullrequest',
787 rmap.connect('repo_pullrequest_settings', '/{repo_name}/settings/pullrequest',
807 controller='admin/repos', action='repo_settings_pullrequest',
788 controller='admin/repos', action='repo_settings_pullrequest',
808 conditions={'method': ['GET', 'POST'], 'function': check_repo},
789 conditions={'method': ['GET', 'POST'], 'function': check_repo},
809 requirements=URL_NAME_REQUIREMENTS)
790 requirements=URL_NAME_REQUIREMENTS)
810
791
811 # still working url for backward compat.
792 # still working url for backward compat.
812 rmap.connect('raw_changeset_home_depraced',
793 rmap.connect('raw_changeset_home_depraced',
813 '/{repo_name}/raw-changeset/{revision}',
794 '/{repo_name}/raw-changeset/{revision}',
814 controller='changeset', action='changeset_raw',
795 controller='changeset', action='changeset_raw',
815 revision='tip', conditions={'function': check_repo},
796 revision='tip', conditions={'function': check_repo},
816 requirements=URL_NAME_REQUIREMENTS)
797 requirements=URL_NAME_REQUIREMENTS)
817
798
818 # new URLs
799 # new URLs
819 rmap.connect('changeset_raw_home',
800 rmap.connect('changeset_raw_home',
820 '/{repo_name}/changeset-diff/{revision}',
801 '/{repo_name}/changeset-diff/{revision}',
821 controller='changeset', action='changeset_raw',
802 controller='changeset', action='changeset_raw',
822 revision='tip', conditions={'function': check_repo},
803 revision='tip', conditions={'function': check_repo},
823 requirements=URL_NAME_REQUIREMENTS)
804 requirements=URL_NAME_REQUIREMENTS)
824
805
825 rmap.connect('changeset_patch_home',
806 rmap.connect('changeset_patch_home',
826 '/{repo_name}/changeset-patch/{revision}',
807 '/{repo_name}/changeset-patch/{revision}',
827 controller='changeset', action='changeset_patch',
808 controller='changeset', action='changeset_patch',
828 revision='tip', conditions={'function': check_repo},
809 revision='tip', conditions={'function': check_repo},
829 requirements=URL_NAME_REQUIREMENTS)
810 requirements=URL_NAME_REQUIREMENTS)
830
811
831 rmap.connect('changeset_download_home',
812 rmap.connect('changeset_download_home',
832 '/{repo_name}/changeset-download/{revision}',
813 '/{repo_name}/changeset-download/{revision}',
833 controller='changeset', action='changeset_download',
814 controller='changeset', action='changeset_download',
834 revision='tip', conditions={'function': check_repo},
815 revision='tip', conditions={'function': check_repo},
835 requirements=URL_NAME_REQUIREMENTS)
816 requirements=URL_NAME_REQUIREMENTS)
836
817
837 rmap.connect('changeset_comment',
818 rmap.connect('changeset_comment',
838 '/{repo_name}/changeset/{revision}/comment', jsroute=True,
819 '/{repo_name}/changeset/{revision}/comment', jsroute=True,
839 controller='changeset', revision='tip', action='comment',
820 controller='changeset', revision='tip', action='comment',
840 conditions={'function': check_repo},
821 conditions={'function': check_repo},
841 requirements=URL_NAME_REQUIREMENTS)
822 requirements=URL_NAME_REQUIREMENTS)
842
823
843 rmap.connect('changeset_comment_preview',
824 rmap.connect('changeset_comment_preview',
844 '/{repo_name}/changeset/comment/preview', jsroute=True,
825 '/{repo_name}/changeset/comment/preview', jsroute=True,
845 controller='changeset', action='preview_comment',
826 controller='changeset', action='preview_comment',
846 conditions={'function': check_repo, 'method': ['POST']},
827 conditions={'function': check_repo, 'method': ['POST']},
847 requirements=URL_NAME_REQUIREMENTS)
828 requirements=URL_NAME_REQUIREMENTS)
848
829
849 rmap.connect('changeset_comment_delete',
830 rmap.connect('changeset_comment_delete',
850 '/{repo_name}/changeset/comment/{comment_id}/delete',
831 '/{repo_name}/changeset/comment/{comment_id}/delete',
851 controller='changeset', action='delete_comment',
832 controller='changeset', action='delete_comment',
852 conditions={'function': check_repo, 'method': ['DELETE']},
833 conditions={'function': check_repo, 'method': ['DELETE']},
853 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
834 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
854
835
855 rmap.connect('changeset_info', '/{repo_name}/changeset_info/{revision}',
836 rmap.connect('changeset_info', '/{repo_name}/changeset_info/{revision}',
856 controller='changeset', action='changeset_info',
837 controller='changeset', action='changeset_info',
857 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
838 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
858
839
859 rmap.connect('compare_home',
840 rmap.connect('compare_home',
860 '/{repo_name}/compare',
841 '/{repo_name}/compare',
861 controller='compare', action='index',
842 controller='compare', action='index',
862 conditions={'function': check_repo},
843 conditions={'function': check_repo},
863 requirements=URL_NAME_REQUIREMENTS)
844 requirements=URL_NAME_REQUIREMENTS)
864
845
865 rmap.connect('compare_url',
846 rmap.connect('compare_url',
866 '/{repo_name}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}',
847 '/{repo_name}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}',
867 controller='compare', action='compare',
848 controller='compare', action='compare',
868 conditions={'function': check_repo},
849 conditions={'function': check_repo},
869 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
850 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
870
851
871 rmap.connect('pullrequest_home',
852 rmap.connect('pullrequest_home',
872 '/{repo_name}/pull-request/new', controller='pullrequests',
853 '/{repo_name}/pull-request/new', controller='pullrequests',
873 action='index', conditions={'function': check_repo,
854 action='index', conditions={'function': check_repo,
874 'method': ['GET']},
855 'method': ['GET']},
875 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
856 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
876
857
877 rmap.connect('pullrequest',
858 rmap.connect('pullrequest',
878 '/{repo_name}/pull-request/new', controller='pullrequests',
859 '/{repo_name}/pull-request/new', controller='pullrequests',
879 action='create', conditions={'function': check_repo,
860 action='create', conditions={'function': check_repo,
880 'method': ['POST']},
861 'method': ['POST']},
881 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
862 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
882
863
883 rmap.connect('pullrequest_repo_refs',
864 rmap.connect('pullrequest_repo_refs',
884 '/{repo_name}/pull-request/refs/{target_repo_name:.*?[^/]}',
865 '/{repo_name}/pull-request/refs/{target_repo_name:.*?[^/]}',
885 controller='pullrequests',
866 controller='pullrequests',
886 action='get_repo_refs',
867 action='get_repo_refs',
887 conditions={'function': check_repo, 'method': ['GET']},
868 conditions={'function': check_repo, 'method': ['GET']},
888 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
869 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
889
870
890 rmap.connect('pullrequest_repo_destinations',
871 rmap.connect('pullrequest_repo_destinations',
891 '/{repo_name}/pull-request/repo-destinations',
872 '/{repo_name}/pull-request/repo-destinations',
892 controller='pullrequests',
873 controller='pullrequests',
893 action='get_repo_destinations',
874 action='get_repo_destinations',
894 conditions={'function': check_repo, 'method': ['GET']},
875 conditions={'function': check_repo, 'method': ['GET']},
895 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
876 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
896
877
897 rmap.connect('pullrequest_show',
878 rmap.connect('pullrequest_show',
898 '/{repo_name}/pull-request/{pull_request_id}',
879 '/{repo_name}/pull-request/{pull_request_id}',
899 controller='pullrequests',
880 controller='pullrequests',
900 action='show', conditions={'function': check_repo,
881 action='show', conditions={'function': check_repo,
901 'method': ['GET']},
882 'method': ['GET']},
902 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
883 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
903
884
904 rmap.connect('pullrequest_update',
885 rmap.connect('pullrequest_update',
905 '/{repo_name}/pull-request/{pull_request_id}',
886 '/{repo_name}/pull-request/{pull_request_id}',
906 controller='pullrequests',
887 controller='pullrequests',
907 action='update', conditions={'function': check_repo,
888 action='update', conditions={'function': check_repo,
908 'method': ['PUT']},
889 'method': ['PUT']},
909 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
890 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
910
891
911 rmap.connect('pullrequest_merge',
892 rmap.connect('pullrequest_merge',
912 '/{repo_name}/pull-request/{pull_request_id}',
893 '/{repo_name}/pull-request/{pull_request_id}',
913 controller='pullrequests',
894 controller='pullrequests',
914 action='merge', conditions={'function': check_repo,
895 action='merge', conditions={'function': check_repo,
915 'method': ['POST']},
896 'method': ['POST']},
916 requirements=URL_NAME_REQUIREMENTS)
897 requirements=URL_NAME_REQUIREMENTS)
917
898
918 rmap.connect('pullrequest_delete',
899 rmap.connect('pullrequest_delete',
919 '/{repo_name}/pull-request/{pull_request_id}',
900 '/{repo_name}/pull-request/{pull_request_id}',
920 controller='pullrequests',
901 controller='pullrequests',
921 action='delete', conditions={'function': check_repo,
902 action='delete', conditions={'function': check_repo,
922 'method': ['DELETE']},
903 'method': ['DELETE']},
923 requirements=URL_NAME_REQUIREMENTS)
904 requirements=URL_NAME_REQUIREMENTS)
924
905
925 rmap.connect('pullrequest_show_all',
906 rmap.connect('pullrequest_show_all',
926 '/{repo_name}/pull-request',
907 '/{repo_name}/pull-request',
927 controller='pullrequests',
908 controller='pullrequests',
928 action='show_all', conditions={'function': check_repo,
909 action='show_all', conditions={'function': check_repo,
929 'method': ['GET']},
910 'method': ['GET']},
930 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
911 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
931
912
932 rmap.connect('pullrequest_comment',
913 rmap.connect('pullrequest_comment',
933 '/{repo_name}/pull-request-comment/{pull_request_id}',
914 '/{repo_name}/pull-request-comment/{pull_request_id}',
934 controller='pullrequests',
915 controller='pullrequests',
935 action='comment', conditions={'function': check_repo,
916 action='comment', conditions={'function': check_repo,
936 'method': ['POST']},
917 'method': ['POST']},
937 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
918 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
938
919
939 rmap.connect('pullrequest_comment_delete',
920 rmap.connect('pullrequest_comment_delete',
940 '/{repo_name}/pull-request-comment/{comment_id}/delete',
921 '/{repo_name}/pull-request-comment/{comment_id}/delete',
941 controller='pullrequests', action='delete_comment',
922 controller='pullrequests', action='delete_comment',
942 conditions={'function': check_repo, 'method': ['DELETE']},
923 conditions={'function': check_repo, 'method': ['DELETE']},
943 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
924 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
944
925
945 rmap.connect('summary_home_explicit', '/{repo_name}/summary',
926 rmap.connect('summary_home_explicit', '/{repo_name}/summary',
946 controller='summary', conditions={'function': check_repo},
927 controller='summary', conditions={'function': check_repo},
947 requirements=URL_NAME_REQUIREMENTS)
928 requirements=URL_NAME_REQUIREMENTS)
948
929
949 rmap.connect('branches_home', '/{repo_name}/branches',
930 rmap.connect('branches_home', '/{repo_name}/branches',
950 controller='branches', conditions={'function': check_repo},
931 controller='branches', conditions={'function': check_repo},
951 requirements=URL_NAME_REQUIREMENTS)
932 requirements=URL_NAME_REQUIREMENTS)
952
933
953 rmap.connect('tags_home', '/{repo_name}/tags',
934 rmap.connect('tags_home', '/{repo_name}/tags',
954 controller='tags', conditions={'function': check_repo},
935 controller='tags', conditions={'function': check_repo},
955 requirements=URL_NAME_REQUIREMENTS)
936 requirements=URL_NAME_REQUIREMENTS)
956
937
957 rmap.connect('bookmarks_home', '/{repo_name}/bookmarks',
938 rmap.connect('bookmarks_home', '/{repo_name}/bookmarks',
958 controller='bookmarks', conditions={'function': check_repo},
939 controller='bookmarks', conditions={'function': check_repo},
959 requirements=URL_NAME_REQUIREMENTS)
940 requirements=URL_NAME_REQUIREMENTS)
960
941
961 rmap.connect('changelog_home', '/{repo_name}/changelog', jsroute=True,
942 rmap.connect('changelog_home', '/{repo_name}/changelog', jsroute=True,
962 controller='changelog', conditions={'function': check_repo},
943 controller='changelog', conditions={'function': check_repo},
963 requirements=URL_NAME_REQUIREMENTS)
944 requirements=URL_NAME_REQUIREMENTS)
964
945
965 rmap.connect('changelog_summary_home', '/{repo_name}/changelog_summary',
946 rmap.connect('changelog_summary_home', '/{repo_name}/changelog_summary',
966 controller='changelog', action='changelog_summary',
947 controller='changelog', action='changelog_summary',
967 conditions={'function': check_repo},
948 conditions={'function': check_repo},
968 requirements=URL_NAME_REQUIREMENTS)
949 requirements=URL_NAME_REQUIREMENTS)
969
950
970 rmap.connect('changelog_file_home',
951 rmap.connect('changelog_file_home',
971 '/{repo_name}/changelog/{revision}/{f_path}',
952 '/{repo_name}/changelog/{revision}/{f_path}',
972 controller='changelog', f_path=None,
953 controller='changelog', f_path=None,
973 conditions={'function': check_repo},
954 conditions={'function': check_repo},
974 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
955 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
975
956
976 rmap.connect('changelog_elements', '/{repo_name}/changelog_details',
957 rmap.connect('changelog_elements', '/{repo_name}/changelog_details',
977 controller='changelog', action='changelog_elements',
958 controller='changelog', action='changelog_elements',
978 conditions={'function': check_repo},
959 conditions={'function': check_repo},
979 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
960 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
980
961
981 rmap.connect('files_home', '/{repo_name}/files/{revision}/{f_path}',
962 rmap.connect('files_home', '/{repo_name}/files/{revision}/{f_path}',
982 controller='files', revision='tip', f_path='',
963 controller='files', revision='tip', f_path='',
983 conditions={'function': check_repo},
964 conditions={'function': check_repo},
984 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
965 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
985
966
986 rmap.connect('files_home_simple_catchrev',
967 rmap.connect('files_home_simple_catchrev',
987 '/{repo_name}/files/{revision}',
968 '/{repo_name}/files/{revision}',
988 controller='files', revision='tip', f_path='',
969 controller='files', revision='tip', f_path='',
989 conditions={'function': check_repo},
970 conditions={'function': check_repo},
990 requirements=URL_NAME_REQUIREMENTS)
971 requirements=URL_NAME_REQUIREMENTS)
991
972
992 rmap.connect('files_home_simple_catchall',
973 rmap.connect('files_home_simple_catchall',
993 '/{repo_name}/files',
974 '/{repo_name}/files',
994 controller='files', revision='tip', f_path='',
975 controller='files', revision='tip', f_path='',
995 conditions={'function': check_repo},
976 conditions={'function': check_repo},
996 requirements=URL_NAME_REQUIREMENTS)
977 requirements=URL_NAME_REQUIREMENTS)
997
978
998 rmap.connect('files_history_home',
979 rmap.connect('files_history_home',
999 '/{repo_name}/history/{revision}/{f_path}',
980 '/{repo_name}/history/{revision}/{f_path}',
1000 controller='files', action='history', revision='tip', f_path='',
981 controller='files', action='history', revision='tip', f_path='',
1001 conditions={'function': check_repo},
982 conditions={'function': check_repo},
1002 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
983 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1003
984
1004 rmap.connect('files_authors_home',
985 rmap.connect('files_authors_home',
1005 '/{repo_name}/authors/{revision}/{f_path}',
986 '/{repo_name}/authors/{revision}/{f_path}',
1006 controller='files', action='authors', revision='tip', f_path='',
987 controller='files', action='authors', revision='tip', f_path='',
1007 conditions={'function': check_repo},
988 conditions={'function': check_repo},
1008 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
989 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1009
990
1010 rmap.connect('files_diff_home', '/{repo_name}/diff/{f_path}',
991 rmap.connect('files_diff_home', '/{repo_name}/diff/{f_path}',
1011 controller='files', action='diff', f_path='',
992 controller='files', action='diff', f_path='',
1012 conditions={'function': check_repo},
993 conditions={'function': check_repo},
1013 requirements=URL_NAME_REQUIREMENTS)
994 requirements=URL_NAME_REQUIREMENTS)
1014
995
1015 rmap.connect('files_diff_2way_home',
996 rmap.connect('files_diff_2way_home',
1016 '/{repo_name}/diff-2way/{f_path}',
997 '/{repo_name}/diff-2way/{f_path}',
1017 controller='files', action='diff_2way', f_path='',
998 controller='files', action='diff_2way', f_path='',
1018 conditions={'function': check_repo},
999 conditions={'function': check_repo},
1019 requirements=URL_NAME_REQUIREMENTS)
1000 requirements=URL_NAME_REQUIREMENTS)
1020
1001
1021 rmap.connect('files_rawfile_home',
1002 rmap.connect('files_rawfile_home',
1022 '/{repo_name}/rawfile/{revision}/{f_path}',
1003 '/{repo_name}/rawfile/{revision}/{f_path}',
1023 controller='files', action='rawfile', revision='tip',
1004 controller='files', action='rawfile', revision='tip',
1024 f_path='', conditions={'function': check_repo},
1005 f_path='', conditions={'function': check_repo},
1025 requirements=URL_NAME_REQUIREMENTS)
1006 requirements=URL_NAME_REQUIREMENTS)
1026
1007
1027 rmap.connect('files_raw_home',
1008 rmap.connect('files_raw_home',
1028 '/{repo_name}/raw/{revision}/{f_path}',
1009 '/{repo_name}/raw/{revision}/{f_path}',
1029 controller='files', action='raw', revision='tip', f_path='',
1010 controller='files', action='raw', revision='tip', f_path='',
1030 conditions={'function': check_repo},
1011 conditions={'function': check_repo},
1031 requirements=URL_NAME_REQUIREMENTS)
1012 requirements=URL_NAME_REQUIREMENTS)
1032
1013
1033 rmap.connect('files_render_home',
1014 rmap.connect('files_render_home',
1034 '/{repo_name}/render/{revision}/{f_path}',
1015 '/{repo_name}/render/{revision}/{f_path}',
1035 controller='files', action='index', revision='tip', f_path='',
1016 controller='files', action='index', revision='tip', f_path='',
1036 rendered=True, conditions={'function': check_repo},
1017 rendered=True, conditions={'function': check_repo},
1037 requirements=URL_NAME_REQUIREMENTS)
1018 requirements=URL_NAME_REQUIREMENTS)
1038
1019
1039 rmap.connect('files_annotate_home',
1020 rmap.connect('files_annotate_home',
1040 '/{repo_name}/annotate/{revision}/{f_path}',
1021 '/{repo_name}/annotate/{revision}/{f_path}',
1041 controller='files', action='index', revision='tip',
1022 controller='files', action='index', revision='tip',
1042 f_path='', annotate=True, conditions={'function': check_repo},
1023 f_path='', annotate=True, conditions={'function': check_repo},
1043 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1024 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1044
1025
1045 rmap.connect('files_annotate_previous',
1026 rmap.connect('files_annotate_previous',
1046 '/{repo_name}/annotate-previous/{revision}/{f_path}',
1027 '/{repo_name}/annotate-previous/{revision}/{f_path}',
1047 controller='files', action='annotate_previous', revision='tip',
1028 controller='files', action='annotate_previous', revision='tip',
1048 f_path='', annotate=True, conditions={'function': check_repo},
1029 f_path='', annotate=True, conditions={'function': check_repo},
1049 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1030 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1050
1031
1051 rmap.connect('files_edit',
1032 rmap.connect('files_edit',
1052 '/{repo_name}/edit/{revision}/{f_path}',
1033 '/{repo_name}/edit/{revision}/{f_path}',
1053 controller='files', action='edit', revision='tip',
1034 controller='files', action='edit', revision='tip',
1054 f_path='',
1035 f_path='',
1055 conditions={'function': check_repo, 'method': ['POST']},
1036 conditions={'function': check_repo, 'method': ['POST']},
1056 requirements=URL_NAME_REQUIREMENTS)
1037 requirements=URL_NAME_REQUIREMENTS)
1057
1038
1058 rmap.connect('files_edit_home',
1039 rmap.connect('files_edit_home',
1059 '/{repo_name}/edit/{revision}/{f_path}',
1040 '/{repo_name}/edit/{revision}/{f_path}',
1060 controller='files', action='edit_home', revision='tip',
1041 controller='files', action='edit_home', revision='tip',
1061 f_path='', conditions={'function': check_repo},
1042 f_path='', conditions={'function': check_repo},
1062 requirements=URL_NAME_REQUIREMENTS)
1043 requirements=URL_NAME_REQUIREMENTS)
1063
1044
1064 rmap.connect('files_add',
1045 rmap.connect('files_add',
1065 '/{repo_name}/add/{revision}/{f_path}',
1046 '/{repo_name}/add/{revision}/{f_path}',
1066 controller='files', action='add', revision='tip',
1047 controller='files', action='add', revision='tip',
1067 f_path='',
1048 f_path='',
1068 conditions={'function': check_repo, 'method': ['POST']},
1049 conditions={'function': check_repo, 'method': ['POST']},
1069 requirements=URL_NAME_REQUIREMENTS)
1050 requirements=URL_NAME_REQUIREMENTS)
1070
1051
1071 rmap.connect('files_add_home',
1052 rmap.connect('files_add_home',
1072 '/{repo_name}/add/{revision}/{f_path}',
1053 '/{repo_name}/add/{revision}/{f_path}',
1073 controller='files', action='add_home', revision='tip',
1054 controller='files', action='add_home', revision='tip',
1074 f_path='', conditions={'function': check_repo},
1055 f_path='', conditions={'function': check_repo},
1075 requirements=URL_NAME_REQUIREMENTS)
1056 requirements=URL_NAME_REQUIREMENTS)
1076
1057
1077 rmap.connect('files_delete',
1058 rmap.connect('files_delete',
1078 '/{repo_name}/delete/{revision}/{f_path}',
1059 '/{repo_name}/delete/{revision}/{f_path}',
1079 controller='files', action='delete', revision='tip',
1060 controller='files', action='delete', revision='tip',
1080 f_path='',
1061 f_path='',
1081 conditions={'function': check_repo, 'method': ['POST']},
1062 conditions={'function': check_repo, 'method': ['POST']},
1082 requirements=URL_NAME_REQUIREMENTS)
1063 requirements=URL_NAME_REQUIREMENTS)
1083
1064
1084 rmap.connect('files_delete_home',
1065 rmap.connect('files_delete_home',
1085 '/{repo_name}/delete/{revision}/{f_path}',
1066 '/{repo_name}/delete/{revision}/{f_path}',
1086 controller='files', action='delete_home', revision='tip',
1067 controller='files', action='delete_home', revision='tip',
1087 f_path='', conditions={'function': check_repo},
1068 f_path='', conditions={'function': check_repo},
1088 requirements=URL_NAME_REQUIREMENTS)
1069 requirements=URL_NAME_REQUIREMENTS)
1089
1070
1090 rmap.connect('files_archive_home', '/{repo_name}/archive/{fname}',
1071 rmap.connect('files_archive_home', '/{repo_name}/archive/{fname}',
1091 controller='files', action='archivefile',
1072 controller='files', action='archivefile',
1092 conditions={'function': check_repo},
1073 conditions={'function': check_repo},
1093 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1074 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1094
1075
1095 rmap.connect('files_nodelist_home',
1076 rmap.connect('files_nodelist_home',
1096 '/{repo_name}/nodelist/{revision}/{f_path}',
1077 '/{repo_name}/nodelist/{revision}/{f_path}',
1097 controller='files', action='nodelist',
1078 controller='files', action='nodelist',
1098 conditions={'function': check_repo},
1079 conditions={'function': check_repo},
1099 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1080 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1100
1081
1101 rmap.connect('files_nodetree_full',
1082 rmap.connect('files_nodetree_full',
1102 '/{repo_name}/nodetree_full/{commit_id}/{f_path}',
1083 '/{repo_name}/nodetree_full/{commit_id}/{f_path}',
1103 controller='files', action='nodetree_full',
1084 controller='files', action='nodetree_full',
1104 conditions={'function': check_repo},
1085 conditions={'function': check_repo},
1105 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1086 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1106
1087
1107 rmap.connect('repo_fork_create_home', '/{repo_name}/fork',
1088 rmap.connect('repo_fork_create_home', '/{repo_name}/fork',
1108 controller='forks', action='fork_create',
1089 controller='forks', action='fork_create',
1109 conditions={'function': check_repo, 'method': ['POST']},
1090 conditions={'function': check_repo, 'method': ['POST']},
1110 requirements=URL_NAME_REQUIREMENTS)
1091 requirements=URL_NAME_REQUIREMENTS)
1111
1092
1112 rmap.connect('repo_fork_home', '/{repo_name}/fork',
1093 rmap.connect('repo_fork_home', '/{repo_name}/fork',
1113 controller='forks', action='fork',
1094 controller='forks', action='fork',
1114 conditions={'function': check_repo},
1095 conditions={'function': check_repo},
1115 requirements=URL_NAME_REQUIREMENTS)
1096 requirements=URL_NAME_REQUIREMENTS)
1116
1097
1117 rmap.connect('repo_forks_home', '/{repo_name}/forks',
1098 rmap.connect('repo_forks_home', '/{repo_name}/forks',
1118 controller='forks', action='forks',
1099 controller='forks', action='forks',
1119 conditions={'function': check_repo},
1100 conditions={'function': check_repo},
1120 requirements=URL_NAME_REQUIREMENTS)
1101 requirements=URL_NAME_REQUIREMENTS)
1121
1102
1122 # must be here for proper group/repo catching pattern
1103 # must be here for proper group/repo catching pattern
1123 _connect_with_slash(
1104 _connect_with_slash(
1124 rmap, 'repo_group_home', '/{group_name}',
1105 rmap, 'repo_group_home', '/{group_name}',
1125 controller='home', action='index_repo_group',
1106 controller='home', action='index_repo_group',
1126 conditions={'function': check_group},
1107 conditions={'function': check_group},
1127 requirements=URL_NAME_REQUIREMENTS)
1108 requirements=URL_NAME_REQUIREMENTS)
1128
1109
1129 # catch all, at the end
1110 # catch all, at the end
1130 _connect_with_slash(
1111 _connect_with_slash(
1131 rmap, 'summary_home', '/{repo_name}', jsroute=True,
1112 rmap, 'summary_home', '/{repo_name}', jsroute=True,
1132 controller='summary', action='index',
1113 controller='summary', action='index',
1133 conditions={'function': check_repo},
1114 conditions={'function': check_repo},
1134 requirements=URL_NAME_REQUIREMENTS)
1115 requirements=URL_NAME_REQUIREMENTS)
1135
1116
1136 return rmap
1117 return rmap
1137
1118
1138
1119
1139 def _connect_with_slash(mapper, name, path, *args, **kwargs):
1120 def _connect_with_slash(mapper, name, path, *args, **kwargs):
1140 """
1121 """
1141 Connect a route with an optional trailing slash in `path`.
1122 Connect a route with an optional trailing slash in `path`.
1142 """
1123 """
1143 mapper.connect(name + '_slash', path + '/', *args, **kwargs)
1124 mapper.connect(name + '_slash', path + '/', *args, **kwargs)
1144 mapper.connect(name, path, *args, **kwargs)
1125 mapper.connect(name, path, *args, **kwargs)
@@ -1,72 +1,101 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
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
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
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/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 Single source for redirection links.
22 Single source for redirection links.
23
23
24 Goal of this module is to provide a single source of truth regarding external
24 Goal of this module is to provide a single source of truth regarding external
25 links. The data inside this module is used to configure the routing
25 links. The data inside this module is used to configure the routing
26 system of Enterprise and it is used also as a base to check if this data
26 system of Enterprise and it is used also as a base to check if this data
27 and our server configuration are in sync.
27 and our server configuration are in sync.
28
28
29 .. py:data:: link_config
29 .. py:data:: link_config
30
30
31 Contains the configuration for external links. Each item is supposed to be
31 Contains the configuration for external links. Each item is supposed to be
32 a `dict` like this example::
32 a `dict` like this example::
33
33
34 {"name": "url_name",
34 {"name": "url_name",
35 "target": "https://rhodecode.com/r1/enterprise/keyword/",
35 "target": "https://rhodecode.com/r1/enterprise/keyword/",
36 "external_target": "https://example.com/some-page.html",
36 "external_target": "https://example.com/some-page.html",
37 }
37 }
38
38
39 then you can retrieve the url by simply calling the URL function:
39 then you can retrieve the url by simply calling the URL function:
40
40
41 `h.url('url_name')`
41 `h.url('url_name')`
42
42
43 The redirection must be first implemented in our servers before
43 The redirection must be first implemented in our servers before
44 you can see it working.
44 you can see it working.
45 """
45 """
46 # flake8: noqa
46 # flake8: noqa
47 from __future__ import unicode_literals
47 from __future__ import unicode_literals
48
48
49
50 link_config = [
49 link_config = [
51 {"name": "enterprise_docs",
50 {
51 "name": "enterprise_docs",
52 "target": "https://rhodecode.com/r1/enterprise/docs/",
52 "target": "https://rhodecode.com/r1/enterprise/docs/",
53 "external_target": "https://docs.rhodecode.com/RhodeCode-Enterprise/",
53 "external_target": "https://docs.rhodecode.com/RhodeCode-Enterprise/",
54 },
54 },
55 {"name": "enterprise_log_file_locations",
55 {
56 "name": "enterprise_log_file_locations",
56 "target": "https://rhodecode.com/r1/enterprise/docs/admin-system-overview/",
57 "target": "https://rhodecode.com/r1/enterprise/docs/admin-system-overview/",
57 "external_target": "https://docs.rhodecode.com/RhodeCode-Enterprise/admin/system-overview.html#log-files",
58 "external_target": "https://docs.rhodecode.com/RhodeCode-Enterprise/admin/system-overview.html#log-files",
58 },
59 },
59 {"name": "enterprise_issue_tracker_settings",
60 {
61 "name": "enterprise_issue_tracker_settings",
60 "target": "https://rhodecode.com/r1/enterprise/docs/issue-trackers-overview/",
62 "target": "https://rhodecode.com/r1/enterprise/docs/issue-trackers-overview/",
61 "external_target": "https://docs.rhodecode.com/RhodeCode-Enterprise/issue-trackers/issue-trackers.html",
63 "external_target": "https://docs.rhodecode.com/RhodeCode-Enterprise/issue-trackers/issue-trackers.html",
62 },
64 },
63 {"name": "enterprise_svn_setup",
65 {
66 "name": "enterprise_svn_setup",
64 "target": "https://rhodecode.com/r1/enterprise/docs/svn-setup/",
67 "target": "https://rhodecode.com/r1/enterprise/docs/svn-setup/",
65 "external_target": "https://docs.rhodecode.com/RhodeCode-Enterprise/admin/svn-http.html",
68 "external_target": "https://docs.rhodecode.com/RhodeCode-Enterprise/admin/svn-http.html",
66 },
69 },
70 {
71 "name": "rst_help",
72 "target": "http://docutils.sourceforge.net/docs/user/rst/quickref.html",
73 "external_target": "http://docutils.sourceforge.net/docs/user/rst/quickref.html",
74 },
75 {
76 "name": "markdown_help",
77 "target": "http://daringfireball.net/projects/markdown/syntax",
78 "external_target": "http://daringfireball.net/projects/markdown/syntax",
79 },
80 {
81 "name": "rhodecode_official",
82 "target": "https://rhodecode.com",
83 "external_target": "https://rhodecode.com/",
84 },
85 {
86 "name": "rhodecode_support",
87 "target": "https://rhodecode.com/help/",
88 "external_target": "https://rhodecode.com/support",
89 },
90 {
91 "name": "rhodecode_translations",
92 "target": "https://rhodecode.com/translate/enterprise",
93 "external_target": "https://www.transifex.com/rhodecode/RhodeCode/",
94 },
95
67 ]
96 ]
68
97
69
98
70 def connect_redirection_links(rmap):
99 def connect_redirection_links(config):
71 for link in link_config:
100 for link in link_config:
72 rmap.connect(link['name'], link['target'], _static=True)
101 config.add_route(link['name'], link['target'], static=True)
@@ -1,589 +1,589 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
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
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
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/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 The base Controller API
22 The base Controller API
23 Provides the BaseController class for subclassing. And usage in different
23 Provides the BaseController class for subclassing. And usage in different
24 controllers
24 controllers
25 """
25 """
26
26
27 import logging
27 import logging
28 import socket
28 import socket
29
29
30 import ipaddress
30 import ipaddress
31 import pyramid.threadlocal
31 import pyramid.threadlocal
32
32
33 from paste.auth.basic import AuthBasicAuthenticator
33 from paste.auth.basic import AuthBasicAuthenticator
34 from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden, get_exception
34 from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden, get_exception
35 from paste.httpheaders import WWW_AUTHENTICATE, AUTHORIZATION
35 from paste.httpheaders import WWW_AUTHENTICATE, AUTHORIZATION
36 from pylons import config, tmpl_context as c, request, session, url
36 from pylons import config, tmpl_context as c, request, session, url
37 from pylons.controllers import WSGIController
37 from pylons.controllers import WSGIController
38 from pylons.controllers.util import redirect
38 from pylons.controllers.util import redirect
39 from pylons.i18n import translation
39 from pylons.i18n import translation
40 # marcink: don't remove this import
40 # marcink: don't remove this import
41 from pylons.templating import render_mako as render # noqa
41 from pylons.templating import render_mako as render # noqa
42 from pylons.i18n.translation import _
42 from pylons.i18n.translation import _
43 from webob.exc import HTTPFound
43 from webob.exc import HTTPFound
44
44
45
45
46 import rhodecode
46 import rhodecode
47 from rhodecode.authentication.base import VCS_TYPE
47 from rhodecode.authentication.base import VCS_TYPE
48 from rhodecode.lib import auth, utils2
48 from rhodecode.lib import auth, utils2
49 from rhodecode.lib import helpers as h
49 from rhodecode.lib import helpers as h
50 from rhodecode.lib.auth import AuthUser, CookieStoreWrapper
50 from rhodecode.lib.auth import AuthUser, CookieStoreWrapper
51 from rhodecode.lib.exceptions import UserCreationError
51 from rhodecode.lib.exceptions import UserCreationError
52 from rhodecode.lib.utils import (
52 from rhodecode.lib.utils import (
53 get_repo_slug, set_rhodecode_config, password_changed,
53 get_repo_slug, set_rhodecode_config, password_changed,
54 get_enabled_hook_classes)
54 get_enabled_hook_classes)
55 from rhodecode.lib.utils2 import (
55 from rhodecode.lib.utils2 import (
56 str2bool, safe_unicode, AttributeDict, safe_int, md5, aslist)
56 str2bool, safe_unicode, AttributeDict, safe_int, md5, aslist)
57 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
57 from rhodecode.lib.vcs.exceptions import RepositoryRequirementError
58 from rhodecode.model import meta
58 from rhodecode.model import meta
59 from rhodecode.model.db import Repository, User, ChangesetComment
59 from rhodecode.model.db import Repository, User, ChangesetComment
60 from rhodecode.model.notification import NotificationModel
60 from rhodecode.model.notification import NotificationModel
61 from rhodecode.model.scm import ScmModel
61 from rhodecode.model.scm import ScmModel
62 from rhodecode.model.settings import VcsSettingsModel, SettingsModel
62 from rhodecode.model.settings import VcsSettingsModel, SettingsModel
63
63
64
64
65 log = logging.getLogger(__name__)
65 log = logging.getLogger(__name__)
66
66
67
67
68 def _filter_proxy(ip):
68 def _filter_proxy(ip):
69 """
69 """
70 Passed in IP addresses in HEADERS can be in a special format of multiple
70 Passed in IP addresses in HEADERS can be in a special format of multiple
71 ips. Those comma separated IPs are passed from various proxies in the
71 ips. Those comma separated IPs are passed from various proxies in the
72 chain of request processing. The left-most being the original client.
72 chain of request processing. The left-most being the original client.
73 We only care about the first IP which came from the org. client.
73 We only care about the first IP which came from the org. client.
74
74
75 :param ip: ip string from headers
75 :param ip: ip string from headers
76 """
76 """
77 if ',' in ip:
77 if ',' in ip:
78 _ips = ip.split(',')
78 _ips = ip.split(',')
79 _first_ip = _ips[0].strip()
79 _first_ip = _ips[0].strip()
80 log.debug('Got multiple IPs %s, using %s', ','.join(_ips), _first_ip)
80 log.debug('Got multiple IPs %s, using %s', ','.join(_ips), _first_ip)
81 return _first_ip
81 return _first_ip
82 return ip
82 return ip
83
83
84
84
85 def _filter_port(ip):
85 def _filter_port(ip):
86 """
86 """
87 Removes a port from ip, there are 4 main cases to handle here.
87 Removes a port from ip, there are 4 main cases to handle here.
88 - ipv4 eg. 127.0.0.1
88 - ipv4 eg. 127.0.0.1
89 - ipv6 eg. ::1
89 - ipv6 eg. ::1
90 - ipv4+port eg. 127.0.0.1:8080
90 - ipv4+port eg. 127.0.0.1:8080
91 - ipv6+port eg. [::1]:8080
91 - ipv6+port eg. [::1]:8080
92
92
93 :param ip:
93 :param ip:
94 """
94 """
95 def is_ipv6(ip_addr):
95 def is_ipv6(ip_addr):
96 if hasattr(socket, 'inet_pton'):
96 if hasattr(socket, 'inet_pton'):
97 try:
97 try:
98 socket.inet_pton(socket.AF_INET6, ip_addr)
98 socket.inet_pton(socket.AF_INET6, ip_addr)
99 except socket.error:
99 except socket.error:
100 return False
100 return False
101 else:
101 else:
102 # fallback to ipaddress
102 # fallback to ipaddress
103 try:
103 try:
104 ipaddress.IPv6Address(ip_addr)
104 ipaddress.IPv6Address(ip_addr)
105 except Exception:
105 except Exception:
106 return False
106 return False
107 return True
107 return True
108
108
109 if ':' not in ip: # must be ipv4 pure ip
109 if ':' not in ip: # must be ipv4 pure ip
110 return ip
110 return ip
111
111
112 if '[' in ip and ']' in ip: # ipv6 with port
112 if '[' in ip and ']' in ip: # ipv6 with port
113 return ip.split(']')[0][1:].lower()
113 return ip.split(']')[0][1:].lower()
114
114
115 # must be ipv6 or ipv4 with port
115 # must be ipv6 or ipv4 with port
116 if is_ipv6(ip):
116 if is_ipv6(ip):
117 return ip
117 return ip
118 else:
118 else:
119 ip, _port = ip.split(':')[:2] # means ipv4+port
119 ip, _port = ip.split(':')[:2] # means ipv4+port
120 return ip
120 return ip
121
121
122
122
123 def get_ip_addr(environ):
123 def get_ip_addr(environ):
124 proxy_key = 'HTTP_X_REAL_IP'
124 proxy_key = 'HTTP_X_REAL_IP'
125 proxy_key2 = 'HTTP_X_FORWARDED_FOR'
125 proxy_key2 = 'HTTP_X_FORWARDED_FOR'
126 def_key = 'REMOTE_ADDR'
126 def_key = 'REMOTE_ADDR'
127 _filters = lambda x: _filter_port(_filter_proxy(x))
127 _filters = lambda x: _filter_port(_filter_proxy(x))
128
128
129 ip = environ.get(proxy_key)
129 ip = environ.get(proxy_key)
130 if ip:
130 if ip:
131 return _filters(ip)
131 return _filters(ip)
132
132
133 ip = environ.get(proxy_key2)
133 ip = environ.get(proxy_key2)
134 if ip:
134 if ip:
135 return _filters(ip)
135 return _filters(ip)
136
136
137 ip = environ.get(def_key, '0.0.0.0')
137 ip = environ.get(def_key, '0.0.0.0')
138 return _filters(ip)
138 return _filters(ip)
139
139
140
140
141 def get_server_ip_addr(environ, log_errors=True):
141 def get_server_ip_addr(environ, log_errors=True):
142 hostname = environ.get('SERVER_NAME')
142 hostname = environ.get('SERVER_NAME')
143 try:
143 try:
144 return socket.gethostbyname(hostname)
144 return socket.gethostbyname(hostname)
145 except Exception as e:
145 except Exception as e:
146 if log_errors:
146 if log_errors:
147 # in some cases this lookup is not possible, and we don't want to
147 # in some cases this lookup is not possible, and we don't want to
148 # make it an exception in logs
148 # make it an exception in logs
149 log.exception('Could not retrieve server ip address: %s', e)
149 log.exception('Could not retrieve server ip address: %s', e)
150 return hostname
150 return hostname
151
151
152
152
153 def get_server_port(environ):
153 def get_server_port(environ):
154 return environ.get('SERVER_PORT')
154 return environ.get('SERVER_PORT')
155
155
156
156
157 def get_access_path(environ):
157 def get_access_path(environ):
158 path = environ.get('PATH_INFO')
158 path = environ.get('PATH_INFO')
159 org_req = environ.get('pylons.original_request')
159 org_req = environ.get('pylons.original_request')
160 if org_req:
160 if org_req:
161 path = org_req.environ.get('PATH_INFO')
161 path = org_req.environ.get('PATH_INFO')
162 return path
162 return path
163
163
164
164
165 def vcs_operation_context(
165 def vcs_operation_context(
166 environ, repo_name, username, action, scm, check_locking=True,
166 environ, repo_name, username, action, scm, check_locking=True,
167 is_shadow_repo=False):
167 is_shadow_repo=False):
168 """
168 """
169 Generate the context for a vcs operation, e.g. push or pull.
169 Generate the context for a vcs operation, e.g. push or pull.
170
170
171 This context is passed over the layers so that hooks triggered by the
171 This context is passed over the layers so that hooks triggered by the
172 vcs operation know details like the user, the user's IP address etc.
172 vcs operation know details like the user, the user's IP address etc.
173
173
174 :param check_locking: Allows to switch of the computation of the locking
174 :param check_locking: Allows to switch of the computation of the locking
175 data. This serves mainly the need of the simplevcs middleware to be
175 data. This serves mainly the need of the simplevcs middleware to be
176 able to disable this for certain operations.
176 able to disable this for certain operations.
177
177
178 """
178 """
179 # Tri-state value: False: unlock, None: nothing, True: lock
179 # Tri-state value: False: unlock, None: nothing, True: lock
180 make_lock = None
180 make_lock = None
181 locked_by = [None, None, None]
181 locked_by = [None, None, None]
182 is_anonymous = username == User.DEFAULT_USER
182 is_anonymous = username == User.DEFAULT_USER
183 if not is_anonymous and check_locking:
183 if not is_anonymous and check_locking:
184 log.debug('Checking locking on repository "%s"', repo_name)
184 log.debug('Checking locking on repository "%s"', repo_name)
185 user = User.get_by_username(username)
185 user = User.get_by_username(username)
186 repo = Repository.get_by_repo_name(repo_name)
186 repo = Repository.get_by_repo_name(repo_name)
187 make_lock, __, locked_by = repo.get_locking_state(
187 make_lock, __, locked_by = repo.get_locking_state(
188 action, user.user_id)
188 action, user.user_id)
189
189
190 settings_model = VcsSettingsModel(repo=repo_name)
190 settings_model = VcsSettingsModel(repo=repo_name)
191 ui_settings = settings_model.get_ui_settings()
191 ui_settings = settings_model.get_ui_settings()
192
192
193 extras = {
193 extras = {
194 'ip': get_ip_addr(environ),
194 'ip': get_ip_addr(environ),
195 'username': username,
195 'username': username,
196 'action': action,
196 'action': action,
197 'repository': repo_name,
197 'repository': repo_name,
198 'scm': scm,
198 'scm': scm,
199 'config': rhodecode.CONFIG['__file__'],
199 'config': rhodecode.CONFIG['__file__'],
200 'make_lock': make_lock,
200 'make_lock': make_lock,
201 'locked_by': locked_by,
201 'locked_by': locked_by,
202 'server_url': utils2.get_server_url(environ),
202 'server_url': utils2.get_server_url(environ),
203 'hooks': get_enabled_hook_classes(ui_settings),
203 'hooks': get_enabled_hook_classes(ui_settings),
204 'is_shadow_repo': is_shadow_repo,
204 'is_shadow_repo': is_shadow_repo,
205 }
205 }
206 return extras
206 return extras
207
207
208
208
209 class BasicAuth(AuthBasicAuthenticator):
209 class BasicAuth(AuthBasicAuthenticator):
210
210
211 def __init__(self, realm, authfunc, registry, auth_http_code=None,
211 def __init__(self, realm, authfunc, registry, auth_http_code=None,
212 initial_call_detection=False, acl_repo_name=None):
212 initial_call_detection=False, acl_repo_name=None):
213 self.realm = realm
213 self.realm = realm
214 self.initial_call = initial_call_detection
214 self.initial_call = initial_call_detection
215 self.authfunc = authfunc
215 self.authfunc = authfunc
216 self.registry = registry
216 self.registry = registry
217 self.acl_repo_name = acl_repo_name
217 self.acl_repo_name = acl_repo_name
218 self._rc_auth_http_code = auth_http_code
218 self._rc_auth_http_code = auth_http_code
219
219
220 def _get_response_from_code(self, http_code):
220 def _get_response_from_code(self, http_code):
221 try:
221 try:
222 return get_exception(safe_int(http_code))
222 return get_exception(safe_int(http_code))
223 except Exception:
223 except Exception:
224 log.exception('Failed to fetch response for code %s' % http_code)
224 log.exception('Failed to fetch response for code %s' % http_code)
225 return HTTPForbidden
225 return HTTPForbidden
226
226
227 def build_authentication(self):
227 def build_authentication(self):
228 head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
228 head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
229 if self._rc_auth_http_code and not self.initial_call:
229 if self._rc_auth_http_code and not self.initial_call:
230 # return alternative HTTP code if alternative http return code
230 # return alternative HTTP code if alternative http return code
231 # is specified in RhodeCode config, but ONLY if it's not the
231 # is specified in RhodeCode config, but ONLY if it's not the
232 # FIRST call
232 # FIRST call
233 custom_response_klass = self._get_response_from_code(
233 custom_response_klass = self._get_response_from_code(
234 self._rc_auth_http_code)
234 self._rc_auth_http_code)
235 return custom_response_klass(headers=head)
235 return custom_response_klass(headers=head)
236 return HTTPUnauthorized(headers=head)
236 return HTTPUnauthorized(headers=head)
237
237
238 def authenticate(self, environ):
238 def authenticate(self, environ):
239 authorization = AUTHORIZATION(environ)
239 authorization = AUTHORIZATION(environ)
240 if not authorization:
240 if not authorization:
241 return self.build_authentication()
241 return self.build_authentication()
242 (authmeth, auth) = authorization.split(' ', 1)
242 (authmeth, auth) = authorization.split(' ', 1)
243 if 'basic' != authmeth.lower():
243 if 'basic' != authmeth.lower():
244 return self.build_authentication()
244 return self.build_authentication()
245 auth = auth.strip().decode('base64')
245 auth = auth.strip().decode('base64')
246 _parts = auth.split(':', 1)
246 _parts = auth.split(':', 1)
247 if len(_parts) == 2:
247 if len(_parts) == 2:
248 username, password = _parts
248 username, password = _parts
249 if self.authfunc(
249 if self.authfunc(
250 username, password, environ, VCS_TYPE,
250 username, password, environ, VCS_TYPE,
251 registry=self.registry, acl_repo_name=self.acl_repo_name):
251 registry=self.registry, acl_repo_name=self.acl_repo_name):
252 return username
252 return username
253 if username and password:
253 if username and password:
254 # we mark that we actually executed authentication once, at
254 # we mark that we actually executed authentication once, at
255 # that point we can use the alternative auth code
255 # that point we can use the alternative auth code
256 self.initial_call = False
256 self.initial_call = False
257
257
258 return self.build_authentication()
258 return self.build_authentication()
259
259
260 __call__ = authenticate
260 __call__ = authenticate
261
261
262
262
263 def attach_context_attributes(context, request):
263 def attach_context_attributes(context, request):
264 """
264 """
265 Attach variables into template context called `c`, please note that
265 Attach variables into template context called `c`, please note that
266 request could be pylons or pyramid request in here.
266 request could be pylons or pyramid request in here.
267 """
267 """
268 rc_config = SettingsModel().get_all_settings(cache=True)
268 rc_config = SettingsModel().get_all_settings(cache=True)
269
269
270 context.rhodecode_version = rhodecode.__version__
270 context.rhodecode_version = rhodecode.__version__
271 context.rhodecode_edition = config.get('rhodecode.edition')
271 context.rhodecode_edition = config.get('rhodecode.edition')
272 # unique secret + version does not leak the version but keep consistency
272 # unique secret + version does not leak the version but keep consistency
273 context.rhodecode_version_hash = md5(
273 context.rhodecode_version_hash = md5(
274 config.get('beaker.session.secret', '') +
274 config.get('beaker.session.secret', '') +
275 rhodecode.__version__)[:8]
275 rhodecode.__version__)[:8]
276
276
277 # Default language set for the incoming request
277 # Default language set for the incoming request
278 context.language = translation.get_lang()[0]
278 context.language = translation.get_lang()[0]
279
279
280 # Visual options
280 # Visual options
281 context.visual = AttributeDict({})
281 context.visual = AttributeDict({})
282
282
283 # DB stored Visual Items
283 # DB stored Visual Items
284 context.visual.show_public_icon = str2bool(
284 context.visual.show_public_icon = str2bool(
285 rc_config.get('rhodecode_show_public_icon'))
285 rc_config.get('rhodecode_show_public_icon'))
286 context.visual.show_private_icon = str2bool(
286 context.visual.show_private_icon = str2bool(
287 rc_config.get('rhodecode_show_private_icon'))
287 rc_config.get('rhodecode_show_private_icon'))
288 context.visual.stylify_metatags = str2bool(
288 context.visual.stylify_metatags = str2bool(
289 rc_config.get('rhodecode_stylify_metatags'))
289 rc_config.get('rhodecode_stylify_metatags'))
290 context.visual.dashboard_items = safe_int(
290 context.visual.dashboard_items = safe_int(
291 rc_config.get('rhodecode_dashboard_items', 100))
291 rc_config.get('rhodecode_dashboard_items', 100))
292 context.visual.admin_grid_items = safe_int(
292 context.visual.admin_grid_items = safe_int(
293 rc_config.get('rhodecode_admin_grid_items', 100))
293 rc_config.get('rhodecode_admin_grid_items', 100))
294 context.visual.repository_fields = str2bool(
294 context.visual.repository_fields = str2bool(
295 rc_config.get('rhodecode_repository_fields'))
295 rc_config.get('rhodecode_repository_fields'))
296 context.visual.show_version = str2bool(
296 context.visual.show_version = str2bool(
297 rc_config.get('rhodecode_show_version'))
297 rc_config.get('rhodecode_show_version'))
298 context.visual.use_gravatar = str2bool(
298 context.visual.use_gravatar = str2bool(
299 rc_config.get('rhodecode_use_gravatar'))
299 rc_config.get('rhodecode_use_gravatar'))
300 context.visual.gravatar_url = rc_config.get('rhodecode_gravatar_url')
300 context.visual.gravatar_url = rc_config.get('rhodecode_gravatar_url')
301 context.visual.default_renderer = rc_config.get(
301 context.visual.default_renderer = rc_config.get(
302 'rhodecode_markup_renderer', 'rst')
302 'rhodecode_markup_renderer', 'rst')
303 context.visual.comment_types = ChangesetComment.COMMENT_TYPES
303 context.visual.comment_types = ChangesetComment.COMMENT_TYPES
304 context.visual.rhodecode_support_url = \
304 context.visual.rhodecode_support_url = \
305 rc_config.get('rhodecode_support_url') or url('rhodecode_support')
305 rc_config.get('rhodecode_support_url') or h.route_url('rhodecode_support')
306
306
307 context.pre_code = rc_config.get('rhodecode_pre_code')
307 context.pre_code = rc_config.get('rhodecode_pre_code')
308 context.post_code = rc_config.get('rhodecode_post_code')
308 context.post_code = rc_config.get('rhodecode_post_code')
309 context.rhodecode_name = rc_config.get('rhodecode_title')
309 context.rhodecode_name = rc_config.get('rhodecode_title')
310 context.default_encodings = aslist(config.get('default_encoding'), sep=',')
310 context.default_encodings = aslist(config.get('default_encoding'), sep=',')
311 # if we have specified default_encoding in the request, it has more
311 # if we have specified default_encoding in the request, it has more
312 # priority
312 # priority
313 if request.GET.get('default_encoding'):
313 if request.GET.get('default_encoding'):
314 context.default_encodings.insert(0, request.GET.get('default_encoding'))
314 context.default_encodings.insert(0, request.GET.get('default_encoding'))
315 context.clone_uri_tmpl = rc_config.get('rhodecode_clone_uri_tmpl')
315 context.clone_uri_tmpl = rc_config.get('rhodecode_clone_uri_tmpl')
316
316
317 # INI stored
317 # INI stored
318 context.labs_active = str2bool(
318 context.labs_active = str2bool(
319 config.get('labs_settings_active', 'false'))
319 config.get('labs_settings_active', 'false'))
320 context.visual.allow_repo_location_change = str2bool(
320 context.visual.allow_repo_location_change = str2bool(
321 config.get('allow_repo_location_change', True))
321 config.get('allow_repo_location_change', True))
322 context.visual.allow_custom_hooks_settings = str2bool(
322 context.visual.allow_custom_hooks_settings = str2bool(
323 config.get('allow_custom_hooks_settings', True))
323 config.get('allow_custom_hooks_settings', True))
324 context.debug_style = str2bool(config.get('debug_style', False))
324 context.debug_style = str2bool(config.get('debug_style', False))
325
325
326 context.rhodecode_instanceid = config.get('instance_id')
326 context.rhodecode_instanceid = config.get('instance_id')
327
327
328 # AppEnlight
328 # AppEnlight
329 context.appenlight_enabled = str2bool(config.get('appenlight', 'false'))
329 context.appenlight_enabled = str2bool(config.get('appenlight', 'false'))
330 context.appenlight_api_public_key = config.get(
330 context.appenlight_api_public_key = config.get(
331 'appenlight.api_public_key', '')
331 'appenlight.api_public_key', '')
332 context.appenlight_server_url = config.get('appenlight.server_url', '')
332 context.appenlight_server_url = config.get('appenlight.server_url', '')
333
333
334 # JS template context
334 # JS template context
335 context.template_context = {
335 context.template_context = {
336 'repo_name': None,
336 'repo_name': None,
337 'repo_type': None,
337 'repo_type': None,
338 'repo_landing_commit': None,
338 'repo_landing_commit': None,
339 'rhodecode_user': {
339 'rhodecode_user': {
340 'username': None,
340 'username': None,
341 'email': None,
341 'email': None,
342 'notification_status': False
342 'notification_status': False
343 },
343 },
344 'visual': {
344 'visual': {
345 'default_renderer': None
345 'default_renderer': None
346 },
346 },
347 'commit_data': {
347 'commit_data': {
348 'commit_id': None
348 'commit_id': None
349 },
349 },
350 'pull_request_data': {'pull_request_id': None},
350 'pull_request_data': {'pull_request_id': None},
351 'timeago': {
351 'timeago': {
352 'refresh_time': 120 * 1000,
352 'refresh_time': 120 * 1000,
353 'cutoff_limit': 1000 * 60 * 60 * 24 * 7
353 'cutoff_limit': 1000 * 60 * 60 * 24 * 7
354 },
354 },
355 'pylons_dispatch': {
355 'pylons_dispatch': {
356 # 'controller': request.environ['pylons.routes_dict']['controller'],
356 # 'controller': request.environ['pylons.routes_dict']['controller'],
357 # 'action': request.environ['pylons.routes_dict']['action'],
357 # 'action': request.environ['pylons.routes_dict']['action'],
358 },
358 },
359 'pyramid_dispatch': {
359 'pyramid_dispatch': {
360
360
361 },
361 },
362 'extra': {'plugins': {}}
362 'extra': {'plugins': {}}
363 }
363 }
364 # END CONFIG VARS
364 # END CONFIG VARS
365
365
366 # TODO: This dosn't work when called from pylons compatibility tween.
366 # TODO: This dosn't work when called from pylons compatibility tween.
367 # Fix this and remove it from base controller.
367 # Fix this and remove it from base controller.
368 # context.repo_name = get_repo_slug(request) # can be empty
368 # context.repo_name = get_repo_slug(request) # can be empty
369
369
370 diffmode = 'sideside'
370 diffmode = 'sideside'
371 if request.GET.get('diffmode'):
371 if request.GET.get('diffmode'):
372 if request.GET['diffmode'] == 'unified':
372 if request.GET['diffmode'] == 'unified':
373 diffmode = 'unified'
373 diffmode = 'unified'
374 elif request.session.get('diffmode'):
374 elif request.session.get('diffmode'):
375 diffmode = request.session['diffmode']
375 diffmode = request.session['diffmode']
376
376
377 context.diffmode = diffmode
377 context.diffmode = diffmode
378
378
379 if request.session.get('diffmode') != diffmode:
379 if request.session.get('diffmode') != diffmode:
380 request.session['diffmode'] = diffmode
380 request.session['diffmode'] = diffmode
381
381
382 context.csrf_token = auth.get_csrf_token()
382 context.csrf_token = auth.get_csrf_token()
383 context.backends = rhodecode.BACKENDS.keys()
383 context.backends = rhodecode.BACKENDS.keys()
384 context.backends.sort()
384 context.backends.sort()
385 context.unread_notifications = NotificationModel().get_unread_cnt_for_user(
385 context.unread_notifications = NotificationModel().get_unread_cnt_for_user(
386 context.rhodecode_user.user_id)
386 context.rhodecode_user.user_id)
387
387
388 context.pyramid_request = pyramid.threadlocal.get_current_request()
388 context.pyramid_request = pyramid.threadlocal.get_current_request()
389
389
390
390
391 def get_auth_user(environ):
391 def get_auth_user(environ):
392 ip_addr = get_ip_addr(environ)
392 ip_addr = get_ip_addr(environ)
393 # make sure that we update permissions each time we call controller
393 # make sure that we update permissions each time we call controller
394 _auth_token = (request.GET.get('auth_token', '') or
394 _auth_token = (request.GET.get('auth_token', '') or
395 request.GET.get('api_key', ''))
395 request.GET.get('api_key', ''))
396
396
397 if _auth_token:
397 if _auth_token:
398 # when using API_KEY we assume user exists, and
398 # when using API_KEY we assume user exists, and
399 # doesn't need auth based on cookies.
399 # doesn't need auth based on cookies.
400 auth_user = AuthUser(api_key=_auth_token, ip_addr=ip_addr)
400 auth_user = AuthUser(api_key=_auth_token, ip_addr=ip_addr)
401 authenticated = False
401 authenticated = False
402 else:
402 else:
403 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
403 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
404 try:
404 try:
405 auth_user = AuthUser(user_id=cookie_store.get('user_id', None),
405 auth_user = AuthUser(user_id=cookie_store.get('user_id', None),
406 ip_addr=ip_addr)
406 ip_addr=ip_addr)
407 except UserCreationError as e:
407 except UserCreationError as e:
408 h.flash(e, 'error')
408 h.flash(e, 'error')
409 # container auth or other auth functions that create users
409 # container auth or other auth functions that create users
410 # on the fly can throw this exception signaling that there's
410 # on the fly can throw this exception signaling that there's
411 # issue with user creation, explanation should be provided
411 # issue with user creation, explanation should be provided
412 # in Exception itself. We then create a simple blank
412 # in Exception itself. We then create a simple blank
413 # AuthUser
413 # AuthUser
414 auth_user = AuthUser(ip_addr=ip_addr)
414 auth_user = AuthUser(ip_addr=ip_addr)
415
415
416 if password_changed(auth_user, session):
416 if password_changed(auth_user, session):
417 session.invalidate()
417 session.invalidate()
418 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
418 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
419 auth_user = AuthUser(ip_addr=ip_addr)
419 auth_user = AuthUser(ip_addr=ip_addr)
420
420
421 authenticated = cookie_store.get('is_authenticated')
421 authenticated = cookie_store.get('is_authenticated')
422
422
423 if not auth_user.is_authenticated and auth_user.is_user_object:
423 if not auth_user.is_authenticated and auth_user.is_user_object:
424 # user is not authenticated and not empty
424 # user is not authenticated and not empty
425 auth_user.set_authenticated(authenticated)
425 auth_user.set_authenticated(authenticated)
426
426
427 return auth_user
427 return auth_user
428
428
429
429
430 class BaseController(WSGIController):
430 class BaseController(WSGIController):
431
431
432 def __before__(self):
432 def __before__(self):
433 """
433 """
434 __before__ is called before controller methods and after __call__
434 __before__ is called before controller methods and after __call__
435 """
435 """
436 # on each call propagate settings calls into global settings.
436 # on each call propagate settings calls into global settings.
437 set_rhodecode_config(config)
437 set_rhodecode_config(config)
438 attach_context_attributes(c, request)
438 attach_context_attributes(c, request)
439
439
440 # TODO: Remove this when fixed in attach_context_attributes()
440 # TODO: Remove this when fixed in attach_context_attributes()
441 c.repo_name = get_repo_slug(request) # can be empty
441 c.repo_name = get_repo_slug(request) # can be empty
442
442
443 self.cut_off_limit_diff = safe_int(config.get('cut_off_limit_diff'))
443 self.cut_off_limit_diff = safe_int(config.get('cut_off_limit_diff'))
444 self.cut_off_limit_file = safe_int(config.get('cut_off_limit_file'))
444 self.cut_off_limit_file = safe_int(config.get('cut_off_limit_file'))
445 self.sa = meta.Session
445 self.sa = meta.Session
446 self.scm_model = ScmModel(self.sa)
446 self.scm_model = ScmModel(self.sa)
447
447
448 # set user language
448 # set user language
449 user_lang = getattr(c.pyramid_request, '_LOCALE_', None)
449 user_lang = getattr(c.pyramid_request, '_LOCALE_', None)
450 if user_lang:
450 if user_lang:
451 translation.set_lang(user_lang)
451 translation.set_lang(user_lang)
452 log.debug('set language to %s for user %s',
452 log.debug('set language to %s for user %s',
453 user_lang, self._rhodecode_user)
453 user_lang, self._rhodecode_user)
454
454
455 def _dispatch_redirect(self, with_url, environ, start_response):
455 def _dispatch_redirect(self, with_url, environ, start_response):
456 resp = HTTPFound(with_url)
456 resp = HTTPFound(with_url)
457 environ['SCRIPT_NAME'] = '' # handle prefix middleware
457 environ['SCRIPT_NAME'] = '' # handle prefix middleware
458 environ['PATH_INFO'] = with_url
458 environ['PATH_INFO'] = with_url
459 return resp(environ, start_response)
459 return resp(environ, start_response)
460
460
461 def __call__(self, environ, start_response):
461 def __call__(self, environ, start_response):
462 """Invoke the Controller"""
462 """Invoke the Controller"""
463 # WSGIController.__call__ dispatches to the Controller method
463 # WSGIController.__call__ dispatches to the Controller method
464 # the request is routed to. This routing information is
464 # the request is routed to. This routing information is
465 # available in environ['pylons.routes_dict']
465 # available in environ['pylons.routes_dict']
466 from rhodecode.lib import helpers as h
466 from rhodecode.lib import helpers as h
467
467
468 # Provide the Pylons context to Pyramid's debugtoolbar if it asks
468 # Provide the Pylons context to Pyramid's debugtoolbar if it asks
469 if environ.get('debugtoolbar.wants_pylons_context', False):
469 if environ.get('debugtoolbar.wants_pylons_context', False):
470 environ['debugtoolbar.pylons_context'] = c._current_obj()
470 environ['debugtoolbar.pylons_context'] = c._current_obj()
471
471
472 _route_name = '.'.join([environ['pylons.routes_dict']['controller'],
472 _route_name = '.'.join([environ['pylons.routes_dict']['controller'],
473 environ['pylons.routes_dict']['action']])
473 environ['pylons.routes_dict']['action']])
474
474
475 self.rc_config = SettingsModel().get_all_settings(cache=True)
475 self.rc_config = SettingsModel().get_all_settings(cache=True)
476 self.ip_addr = get_ip_addr(environ)
476 self.ip_addr = get_ip_addr(environ)
477
477
478 # The rhodecode auth user is looked up and passed through the
478 # The rhodecode auth user is looked up and passed through the
479 # environ by the pylons compatibility tween in pyramid.
479 # environ by the pylons compatibility tween in pyramid.
480 # So we can just grab it from there.
480 # So we can just grab it from there.
481 auth_user = environ['rc_auth_user']
481 auth_user = environ['rc_auth_user']
482
482
483 # set globals for auth user
483 # set globals for auth user
484 request.user = auth_user
484 request.user = auth_user
485 c.rhodecode_user = self._rhodecode_user = auth_user
485 c.rhodecode_user = self._rhodecode_user = auth_user
486
486
487 log.info('IP: %s User: %s accessed %s [%s]' % (
487 log.info('IP: %s User: %s accessed %s [%s]' % (
488 self.ip_addr, auth_user, safe_unicode(get_access_path(environ)),
488 self.ip_addr, auth_user, safe_unicode(get_access_path(environ)),
489 _route_name)
489 _route_name)
490 )
490 )
491
491
492 user_obj = auth_user.get_instance()
492 user_obj = auth_user.get_instance()
493 if user_obj and user_obj.user_data.get('force_password_change'):
493 if user_obj and user_obj.user_data.get('force_password_change'):
494 h.flash('You are required to change your password', 'warning',
494 h.flash('You are required to change your password', 'warning',
495 ignore_duplicate=True)
495 ignore_duplicate=True)
496 return self._dispatch_redirect(
496 return self._dispatch_redirect(
497 url('my_account_password'), environ, start_response)
497 url('my_account_password'), environ, start_response)
498
498
499 return WSGIController.__call__(self, environ, start_response)
499 return WSGIController.__call__(self, environ, start_response)
500
500
501
501
502 class BaseRepoController(BaseController):
502 class BaseRepoController(BaseController):
503 """
503 """
504 Base class for controllers responsible for loading all needed data for
504 Base class for controllers responsible for loading all needed data for
505 repository loaded items are
505 repository loaded items are
506
506
507 c.rhodecode_repo: instance of scm repository
507 c.rhodecode_repo: instance of scm repository
508 c.rhodecode_db_repo: instance of db
508 c.rhodecode_db_repo: instance of db
509 c.repository_requirements_missing: shows that repository specific data
509 c.repository_requirements_missing: shows that repository specific data
510 could not be displayed due to the missing requirements
510 could not be displayed due to the missing requirements
511 c.repository_pull_requests: show number of open pull requests
511 c.repository_pull_requests: show number of open pull requests
512 """
512 """
513
513
514 def __before__(self):
514 def __before__(self):
515 super(BaseRepoController, self).__before__()
515 super(BaseRepoController, self).__before__()
516 if c.repo_name: # extracted from routes
516 if c.repo_name: # extracted from routes
517 db_repo = Repository.get_by_repo_name(c.repo_name)
517 db_repo = Repository.get_by_repo_name(c.repo_name)
518 if not db_repo:
518 if not db_repo:
519 return
519 return
520
520
521 log.debug(
521 log.debug(
522 'Found repository in database %s with state `%s`',
522 'Found repository in database %s with state `%s`',
523 safe_unicode(db_repo), safe_unicode(db_repo.repo_state))
523 safe_unicode(db_repo), safe_unicode(db_repo.repo_state))
524 route = getattr(request.environ.get('routes.route'), 'name', '')
524 route = getattr(request.environ.get('routes.route'), 'name', '')
525
525
526 # allow to delete repos that are somehow damages in filesystem
526 # allow to delete repos that are somehow damages in filesystem
527 if route in ['delete_repo']:
527 if route in ['delete_repo']:
528 return
528 return
529
529
530 if db_repo.repo_state in [Repository.STATE_PENDING]:
530 if db_repo.repo_state in [Repository.STATE_PENDING]:
531 if route in ['repo_creating_home']:
531 if route in ['repo_creating_home']:
532 return
532 return
533 check_url = url('repo_creating_home', repo_name=c.repo_name)
533 check_url = url('repo_creating_home', repo_name=c.repo_name)
534 return redirect(check_url)
534 return redirect(check_url)
535
535
536 self.rhodecode_db_repo = db_repo
536 self.rhodecode_db_repo = db_repo
537
537
538 missing_requirements = False
538 missing_requirements = False
539 try:
539 try:
540 self.rhodecode_repo = self.rhodecode_db_repo.scm_instance()
540 self.rhodecode_repo = self.rhodecode_db_repo.scm_instance()
541 except RepositoryRequirementError as e:
541 except RepositoryRequirementError as e:
542 missing_requirements = True
542 missing_requirements = True
543 self._handle_missing_requirements(e)
543 self._handle_missing_requirements(e)
544
544
545 if self.rhodecode_repo is None and not missing_requirements:
545 if self.rhodecode_repo is None and not missing_requirements:
546 log.error('%s this repository is present in database but it '
546 log.error('%s this repository is present in database but it '
547 'cannot be created as an scm instance', c.repo_name)
547 'cannot be created as an scm instance', c.repo_name)
548
548
549 h.flash(_(
549 h.flash(_(
550 "The repository at %(repo_name)s cannot be located.") %
550 "The repository at %(repo_name)s cannot be located.") %
551 {'repo_name': c.repo_name},
551 {'repo_name': c.repo_name},
552 category='error', ignore_duplicate=True)
552 category='error', ignore_duplicate=True)
553 redirect(url('home'))
553 redirect(url('home'))
554
554
555 # update last change according to VCS data
555 # update last change according to VCS data
556 if not missing_requirements:
556 if not missing_requirements:
557 commit = db_repo.get_commit(
557 commit = db_repo.get_commit(
558 pre_load=["author", "date", "message", "parents"])
558 pre_load=["author", "date", "message", "parents"])
559 db_repo.update_commit_cache(commit)
559 db_repo.update_commit_cache(commit)
560
560
561 # Prepare context
561 # Prepare context
562 c.rhodecode_db_repo = db_repo
562 c.rhodecode_db_repo = db_repo
563 c.rhodecode_repo = self.rhodecode_repo
563 c.rhodecode_repo = self.rhodecode_repo
564 c.repository_requirements_missing = missing_requirements
564 c.repository_requirements_missing = missing_requirements
565
565
566 self._update_global_counters(self.scm_model, db_repo)
566 self._update_global_counters(self.scm_model, db_repo)
567
567
568 def _update_global_counters(self, scm_model, db_repo):
568 def _update_global_counters(self, scm_model, db_repo):
569 """
569 """
570 Base variables that are exposed to every page of repository
570 Base variables that are exposed to every page of repository
571 """
571 """
572 c.repository_pull_requests = scm_model.get_pull_requests(db_repo)
572 c.repository_pull_requests = scm_model.get_pull_requests(db_repo)
573
573
574 def _handle_missing_requirements(self, error):
574 def _handle_missing_requirements(self, error):
575 self.rhodecode_repo = None
575 self.rhodecode_repo = None
576 log.error(
576 log.error(
577 'Requirements are missing for repository %s: %s',
577 'Requirements are missing for repository %s: %s',
578 c.repo_name, error.message)
578 c.repo_name, error.message)
579
579
580 summary_url = url('summary_home', repo_name=c.repo_name)
580 summary_url = url('summary_home', repo_name=c.repo_name)
581 statistics_url = url('edit_repo_statistics', repo_name=c.repo_name)
581 statistics_url = url('edit_repo_statistics', repo_name=c.repo_name)
582 settings_update_url = url('repo', repo_name=c.repo_name)
582 settings_update_url = url('repo', repo_name=c.repo_name)
583 path = request.path
583 path = request.path
584 should_redirect = (
584 should_redirect = (
585 path not in (summary_url, settings_update_url)
585 path not in (summary_url, settings_update_url)
586 and '/settings' not in path or path == statistics_url
586 and '/settings' not in path or path == statistics_url
587 )
587 )
588 if should_redirect:
588 if should_redirect:
589 redirect(summary_url)
589 redirect(summary_url)
@@ -1,150 +1,150 b''
1 <%namespace name="base" file="/base/base.mako"/>
1 <%namespace name="base" file="/base/base.mako"/>
2
2
3 <div class="panel panel-default user-profile">
3 <div class="panel panel-default user-profile">
4 <div class="panel-heading">
4 <div class="panel-heading">
5 <h3 class="panel-title">${_('User Profile')}</h3>
5 <h3 class="panel-title">${_('User Profile')}</h3>
6 </div>
6 </div>
7 <div class="panel-body">
7 <div class="panel-body">
8 <div class="user-profile-content">
8 <div class="user-profile-content">
9 ${h.secure_form(url('update_user', user_id=c.user.user_id),method='put', class_='form')}
9 ${h.secure_form(url('update_user', user_id=c.user.user_id),method='put', class_='form')}
10 <% readonly = None %>
10 <% readonly = None %>
11 <% disabled = "" %>
11 <% disabled = "" %>
12 %if c.extern_type != 'rhodecode':
12 %if c.extern_type != 'rhodecode':
13 <% readonly = "readonly" %>
13 <% readonly = "readonly" %>
14 <% disabled = " disabled" %>
14 <% disabled = " disabled" %>
15 <div class="infoform">
15 <div class="infoform">
16 <div class="fields">
16 <div class="fields">
17 <p>${_('This user was created from external source (%s). Editing some of the settings is limited.' % c.extern_type)}</p>
17 <p>${_('This user was created from external source (%s). Editing some of the settings is limited.' % c.extern_type)}</p>
18 </div>
18 </div>
19 </div>
19 </div>
20 %endif
20 %endif
21 <div class="form">
21 <div class="form">
22 <div class="fields">
22 <div class="fields">
23 <div class="field">
23 <div class="field">
24 <div class="label photo">
24 <div class="label photo">
25 ${_('Photo')}:
25 ${_('Photo')}:
26 </div>
26 </div>
27 <div class="input profile">
27 <div class="input profile">
28 %if c.visual.use_gravatar:
28 %if c.visual.use_gravatar:
29 ${base.gravatar(c.user.email, 100)}
29 ${base.gravatar(c.user.email, 100)}
30 <p class="help-block">${_('Change the avatar at')} <a href="http://gravatar.com">gravatar.com</a>.</p>
30 <p class="help-block">${_('Change the avatar at')} <a href="http://gravatar.com">gravatar.com</a>.</p>
31 %else:
31 %else:
32 ${base.gravatar(c.user.email, 20)}
32 ${base.gravatar(c.user.email, 20)}
33 ${_('Avatars are disabled')}
33 ${_('Avatars are disabled')}
34 %endif
34 %endif
35 </div>
35 </div>
36 </div>
36 </div>
37 <div class="field">
37 <div class="field">
38 <div class="label">
38 <div class="label">
39 ${_('Username')}:
39 ${_('Username')}:
40 </div>
40 </div>
41 <div class="input">
41 <div class="input">
42 ${h.text('username', class_='%s medium' % disabled, readonly=readonly)}
42 ${h.text('username', class_='%s medium' % disabled, readonly=readonly)}
43 </div>
43 </div>
44 </div>
44 </div>
45 <div class="field">
45 <div class="field">
46 <div class="label">
46 <div class="label">
47 <label for="name">${_('First Name')}:</label>
47 <label for="name">${_('First Name')}:</label>
48 </div>
48 </div>
49 <div class="input">
49 <div class="input">
50 ${h.text('firstname', class_="medium")}
50 ${h.text('firstname', class_="medium")}
51 </div>
51 </div>
52 </div>
52 </div>
53
53
54 <div class="field">
54 <div class="field">
55 <div class="label">
55 <div class="label">
56 <label for="lastname">${_('Last Name')}:</label>
56 <label for="lastname">${_('Last Name')}:</label>
57 </div>
57 </div>
58 <div class="input">
58 <div class="input">
59 ${h.text('lastname', class_="medium")}
59 ${h.text('lastname', class_="medium")}
60 </div>
60 </div>
61 </div>
61 </div>
62
62
63 <div class="field">
63 <div class="field">
64 <div class="label">
64 <div class="label">
65 <label for="email">${_('Email')}:</label>
65 <label for="email">${_('Email')}:</label>
66 </div>
66 </div>
67 <div class="input">
67 <div class="input">
68 ## we should be able to edit email !
68 ## we should be able to edit email !
69 ${h.text('email', class_="medium")}
69 ${h.text('email', class_="medium")}
70 </div>
70 </div>
71 </div>
71 </div>
72 <div class="field">
72 <div class="field">
73 <div class="label">
73 <div class="label">
74 ${_('New Password')}:
74 ${_('New Password')}:
75 </div>
75 </div>
76 <div class="input">
76 <div class="input">
77 ${h.password('new_password',class_='%s medium' % disabled,autocomplete="off",readonly=readonly)}
77 ${h.password('new_password',class_='%s medium' % disabled,autocomplete="off",readonly=readonly)}
78 </div>
78 </div>
79 </div>
79 </div>
80 <div class="field">
80 <div class="field">
81 <div class="label">
81 <div class="label">
82 ${_('New Password Confirmation')}:
82 ${_('New Password Confirmation')}:
83 </div>
83 </div>
84 <div class="input">
84 <div class="input">
85 ${h.password('password_confirmation',class_="%s medium" % disabled,autocomplete="off",readonly=readonly)}
85 ${h.password('password_confirmation',class_="%s medium" % disabled,autocomplete="off",readonly=readonly)}
86 </div>
86 </div>
87 </div>
87 </div>
88 <div class="field">
88 <div class="field">
89 <div class="label-text">
89 <div class="label-text">
90 ${_('Active')}:
90 ${_('Active')}:
91 </div>
91 </div>
92 <div class="input user-checkbox">
92 <div class="input user-checkbox">
93 ${h.checkbox('active',value=True)}
93 ${h.checkbox('active',value=True)}
94 </div>
94 </div>
95 </div>
95 </div>
96 <div class="field">
96 <div class="field">
97 <div class="label-text">
97 <div class="label-text">
98 ${_('Super Admin')}:
98 ${_('Super Admin')}:
99 </div>
99 </div>
100 <div class="input user-checkbox">
100 <div class="input user-checkbox">
101 ${h.checkbox('admin',value=True)}
101 ${h.checkbox('admin',value=True)}
102 </div>
102 </div>
103 </div>
103 </div>
104 <div class="field">
104 <div class="field">
105 <div class="label-text">
105 <div class="label-text">
106 ${_('Source of Record')}:
106 ${_('Source of Record')}:
107 </div>
107 </div>
108 <div class="input">
108 <div class="input">
109 <p>${c.extern_type}</p>
109 <p>${c.extern_type}</p>
110 ${h.hidden('extern_type', readonly="readonly")}
110 ${h.hidden('extern_type', readonly="readonly")}
111 </div>
111 </div>
112 </div>
112 </div>
113 <div class="field">
113 <div class="field">
114 <div class="label-text">
114 <div class="label-text">
115 ${_('Name in Source of Record')}:
115 ${_('Name in Source of Record')}:
116 </div>
116 </div>
117 <div class="input">
117 <div class="input">
118 <p>${c.extern_name}</p>
118 <p>${c.extern_name}</p>
119 ${h.hidden('extern_name', readonly="readonly")}
119 ${h.hidden('extern_name', readonly="readonly")}
120 </div>
120 </div>
121 </div>
121 </div>
122 <div class="field">
122 <div class="field">
123 <div class="label">
123 <div class="label">
124 ${_('Language')}:
124 ${_('Language')}:
125 </div>
125 </div>
126 <div class="input">
126 <div class="input">
127 ## allowed_languages is defined in the users.py
127 ## allowed_languages is defined in the users.py
128 ## c.language comes from base.py as a default language
128 ## c.language comes from base.py as a default language
129 ${h.select('language', c.language, c.allowed_languages)}
129 ${h.select('language', c.language, c.allowed_languages)}
130 <p class="help-block">${h.literal(_('Help translate %(rc_link)s into your language.') % {'rc_link': h.link_to('RhodeCode Enterprise', h.url('rhodecode_translations'))})}</p>
130 <p class="help-block">${h.literal(_('Help translate %(rc_link)s into your language.') % {'rc_link': h.link_to('RhodeCode Enterprise', h.route_url('rhodecode_translations'))})}</p>
131 </div>
131 </div>
132 </div>
132 </div>
133 <div class="buttons">
133 <div class="buttons">
134 ${h.submit('save', _('Save'), class_="btn")}
134 ${h.submit('save', _('Save'), class_="btn")}
135 ${h.reset('reset', _('Reset'), class_="btn")}
135 ${h.reset('reset', _('Reset'), class_="btn")}
136 </div>
136 </div>
137 </div>
137 </div>
138 </div>
138 </div>
139 ${h.end_form()}
139 ${h.end_form()}
140 </div>
140 </div>
141 </div>
141 </div>
142 </div>
142 </div>
143
143
144 <script>
144 <script>
145 $('#language').select2({
145 $('#language').select2({
146 'containerCssClass': "drop-menu",
146 'containerCssClass': "drop-menu",
147 'dropdownCssClass': "drop-menu-dropdown",
147 'dropdownCssClass': "drop-menu-dropdown",
148 'dropdownAutoWidth': true
148 'dropdownAutoWidth': true
149 });
149 });
150 </script>
150 </script>
@@ -1,601 +1,601 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="root.mako"/>
2 <%inherit file="root.mako"/>
3
3
4 <div class="outerwrapper">
4 <div class="outerwrapper">
5 <!-- HEADER -->
5 <!-- HEADER -->
6 <div class="header">
6 <div class="header">
7 <div id="header-inner" class="wrapper">
7 <div id="header-inner" class="wrapper">
8 <div id="logo">
8 <div id="logo">
9 <div class="logo-wrapper">
9 <div class="logo-wrapper">
10 <a href="${h.url('home')}"><img src="${h.asset('images/rhodecode-logo-white-216x60.png')}" alt="RhodeCode"/></a>
10 <a href="${h.url('home')}"><img src="${h.asset('images/rhodecode-logo-white-216x60.png')}" alt="RhodeCode"/></a>
11 </div>
11 </div>
12 %if c.rhodecode_name:
12 %if c.rhodecode_name:
13 <div class="branding">- ${h.branding(c.rhodecode_name)}</div>
13 <div class="branding">- ${h.branding(c.rhodecode_name)}</div>
14 %endif
14 %endif
15 </div>
15 </div>
16 <!-- MENU BAR NAV -->
16 <!-- MENU BAR NAV -->
17 ${self.menu_bar_nav()}
17 ${self.menu_bar_nav()}
18 <!-- END MENU BAR NAV -->
18 <!-- END MENU BAR NAV -->
19 </div>
19 </div>
20 </div>
20 </div>
21 ${self.menu_bar_subnav()}
21 ${self.menu_bar_subnav()}
22 <!-- END HEADER -->
22 <!-- END HEADER -->
23
23
24 <!-- CONTENT -->
24 <!-- CONTENT -->
25 <div id="content" class="wrapper">
25 <div id="content" class="wrapper">
26
26
27 <rhodecode-toast id="notifications"></rhodecode-toast>
27 <rhodecode-toast id="notifications"></rhodecode-toast>
28
28
29 <div class="main">
29 <div class="main">
30 ${next.main()}
30 ${next.main()}
31 </div>
31 </div>
32 </div>
32 </div>
33 <!-- END CONTENT -->
33 <!-- END CONTENT -->
34
34
35 </div>
35 </div>
36 <!-- FOOTER -->
36 <!-- FOOTER -->
37 <div id="footer">
37 <div id="footer">
38 <div id="footer-inner" class="title wrapper">
38 <div id="footer-inner" class="title wrapper">
39 <div>
39 <div>
40 <p class="footer-link-right">
40 <p class="footer-link-right">
41 % if c.visual.show_version:
41 % if c.visual.show_version:
42 RhodeCode Enterprise ${c.rhodecode_version} ${c.rhodecode_edition}
42 RhodeCode Enterprise ${c.rhodecode_version} ${c.rhodecode_edition}
43 % endif
43 % endif
44 &copy; 2010-${h.datetime.today().year}, <a href="${h.url('rhodecode_official')}" target="_blank">RhodeCode GmbH</a>. All rights reserved.
44 &copy; 2010-${h.datetime.today().year}, <a href="${h.route_url('rhodecode_official')}" target="_blank">RhodeCode GmbH</a>. All rights reserved.
45 % if c.visual.rhodecode_support_url:
45 % if c.visual.rhodecode_support_url:
46 <a href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a>
46 <a href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a>
47 % endif
47 % endif
48 </p>
48 </p>
49 <% sid = 'block' if request.GET.get('showrcid') else 'none' %>
49 <% sid = 'block' if request.GET.get('showrcid') else 'none' %>
50 <p class="server-instance" style="display:${sid}">
50 <p class="server-instance" style="display:${sid}">
51 ## display hidden instance ID if specially defined
51 ## display hidden instance ID if specially defined
52 % if c.rhodecode_instanceid:
52 % if c.rhodecode_instanceid:
53 ${_('RhodeCode instance id: %s') % c.rhodecode_instanceid}
53 ${_('RhodeCode instance id: %s') % c.rhodecode_instanceid}
54 % endif
54 % endif
55 </p>
55 </p>
56 </div>
56 </div>
57 </div>
57 </div>
58 </div>
58 </div>
59
59
60 <!-- END FOOTER -->
60 <!-- END FOOTER -->
61
61
62 ### MAKO DEFS ###
62 ### MAKO DEFS ###
63
63
64 <%def name="menu_bar_subnav()">
64 <%def name="menu_bar_subnav()">
65 </%def>
65 </%def>
66
66
67 <%def name="breadcrumbs(class_='breadcrumbs')">
67 <%def name="breadcrumbs(class_='breadcrumbs')">
68 <div class="${class_}">
68 <div class="${class_}">
69 ${self.breadcrumbs_links()}
69 ${self.breadcrumbs_links()}
70 </div>
70 </div>
71 </%def>
71 </%def>
72
72
73 <%def name="admin_menu()">
73 <%def name="admin_menu()">
74 <ul class="admin_menu submenu">
74 <ul class="admin_menu submenu">
75 <li><a href="${h.url('admin_home')}">${_('Admin journal')}</a></li>
75 <li><a href="${h.url('admin_home')}">${_('Admin journal')}</a></li>
76 <li><a href="${h.url('repos')}">${_('Repositories')}</a></li>
76 <li><a href="${h.url('repos')}">${_('Repositories')}</a></li>
77 <li><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
77 <li><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
78 <li><a href="${h.route_path('users')}">${_('Users')}</a></li>
78 <li><a href="${h.route_path('users')}">${_('Users')}</a></li>
79 <li><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
79 <li><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
80 <li><a href="${h.url('admin_permissions_application')}">${_('Permissions')}</a></li>
80 <li><a href="${h.url('admin_permissions_application')}">${_('Permissions')}</a></li>
81 <li><a href="${h.route_path('auth_home', traverse='')}">${_('Authentication')}</a></li>
81 <li><a href="${h.route_path('auth_home', traverse='')}">${_('Authentication')}</a></li>
82 <li><a href="${h.route_path('global_integrations_home')}">${_('Integrations')}</a></li>
82 <li><a href="${h.route_path('global_integrations_home')}">${_('Integrations')}</a></li>
83 <li><a href="${h.url('admin_defaults_repositories')}">${_('Defaults')}</a></li>
83 <li><a href="${h.url('admin_defaults_repositories')}">${_('Defaults')}</a></li>
84 <li class="last"><a href="${h.url('admin_settings')}">${_('Settings')}</a></li>
84 <li class="last"><a href="${h.url('admin_settings')}">${_('Settings')}</a></li>
85 </ul>
85 </ul>
86 </%def>
86 </%def>
87
87
88
88
89 <%def name="dt_info_panel(elements)">
89 <%def name="dt_info_panel(elements)">
90 <dl class="dl-horizontal">
90 <dl class="dl-horizontal">
91 %for dt, dd, title, show_items in elements:
91 %for dt, dd, title, show_items in elements:
92 <dt>${dt}:</dt>
92 <dt>${dt}:</dt>
93 <dd title="${title}">
93 <dd title="${title}">
94 %if callable(dd):
94 %if callable(dd):
95 ## allow lazy evaluation of elements
95 ## allow lazy evaluation of elements
96 ${dd()}
96 ${dd()}
97 %else:
97 %else:
98 ${dd}
98 ${dd}
99 %endif
99 %endif
100 %if show_items:
100 %if show_items:
101 <span class="btn-collapse" data-toggle="item-${h.md5_safe(dt)[:6]}-details">${_('Show More')} </span>
101 <span class="btn-collapse" data-toggle="item-${h.md5_safe(dt)[:6]}-details">${_('Show More')} </span>
102 %endif
102 %endif
103 </dd>
103 </dd>
104
104
105 %if show_items:
105 %if show_items:
106 <div class="collapsable-content" data-toggle="item-${h.md5_safe(dt)[:6]}-details" style="display: none">
106 <div class="collapsable-content" data-toggle="item-${h.md5_safe(dt)[:6]}-details" style="display: none">
107 %for item in show_items:
107 %for item in show_items:
108 <dt></dt>
108 <dt></dt>
109 <dd>${item}</dd>
109 <dd>${item}</dd>
110 %endfor
110 %endfor
111 </div>
111 </div>
112 %endif
112 %endif
113
113
114 %endfor
114 %endfor
115 </dl>
115 </dl>
116 </%def>
116 </%def>
117
117
118
118
119 <%def name="gravatar(email, size=16)">
119 <%def name="gravatar(email, size=16)">
120 <%
120 <%
121 if (size > 16):
121 if (size > 16):
122 gravatar_class = 'gravatar gravatar-large'
122 gravatar_class = 'gravatar gravatar-large'
123 else:
123 else:
124 gravatar_class = 'gravatar'
124 gravatar_class = 'gravatar'
125 %>
125 %>
126 <%doc>
126 <%doc>
127 TODO: johbo: For now we serve double size images to make it smooth
127 TODO: johbo: For now we serve double size images to make it smooth
128 for retina. This is how it worked until now. Should be replaced
128 for retina. This is how it worked until now. Should be replaced
129 with a better solution at some point.
129 with a better solution at some point.
130 </%doc>
130 </%doc>
131 <img class="${gravatar_class}" src="${h.gravatar_url(email, size * 2)}" height="${size}" width="${size}">
131 <img class="${gravatar_class}" src="${h.gravatar_url(email, size * 2)}" height="${size}" width="${size}">
132 </%def>
132 </%def>
133
133
134
134
135 <%def name="gravatar_with_user(contact, size=16, show_disabled=False)">
135 <%def name="gravatar_with_user(contact, size=16, show_disabled=False)">
136 <% email = h.email_or_none(contact) %>
136 <% email = h.email_or_none(contact) %>
137 <div class="rc-user tooltip" title="${h.author_string(email)}">
137 <div class="rc-user tooltip" title="${h.author_string(email)}">
138 ${self.gravatar(email, size)}
138 ${self.gravatar(email, size)}
139 <span class="${'user user-disabled' if show_disabled else 'user'}"> ${h.link_to_user(contact)}</span>
139 <span class="${'user user-disabled' if show_disabled else 'user'}"> ${h.link_to_user(contact)}</span>
140 </div>
140 </div>
141 </%def>
141 </%def>
142
142
143
143
144 ## admin menu used for people that have some admin resources
144 ## admin menu used for people that have some admin resources
145 <%def name="admin_menu_simple(repositories=None, repository_groups=None, user_groups=None)">
145 <%def name="admin_menu_simple(repositories=None, repository_groups=None, user_groups=None)">
146 <ul class="submenu">
146 <ul class="submenu">
147 %if repositories:
147 %if repositories:
148 <li class="local-admin-repos"><a href="${h.url('repos')}">${_('Repositories')}</a></li>
148 <li class="local-admin-repos"><a href="${h.url('repos')}">${_('Repositories')}</a></li>
149 %endif
149 %endif
150 %if repository_groups:
150 %if repository_groups:
151 <li class="local-admin-repo-groups"><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
151 <li class="local-admin-repo-groups"><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
152 %endif
152 %endif
153 %if user_groups:
153 %if user_groups:
154 <li class="local-admin-user-groups"><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
154 <li class="local-admin-user-groups"><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
155 %endif
155 %endif
156 </ul>
156 </ul>
157 </%def>
157 </%def>
158
158
159 <%def name="repo_page_title(repo_instance)">
159 <%def name="repo_page_title(repo_instance)">
160 <div class="title-content">
160 <div class="title-content">
161 <div class="title-main">
161 <div class="title-main">
162 ## SVN/HG/GIT icons
162 ## SVN/HG/GIT icons
163 %if h.is_hg(repo_instance):
163 %if h.is_hg(repo_instance):
164 <i class="icon-hg"></i>
164 <i class="icon-hg"></i>
165 %endif
165 %endif
166 %if h.is_git(repo_instance):
166 %if h.is_git(repo_instance):
167 <i class="icon-git"></i>
167 <i class="icon-git"></i>
168 %endif
168 %endif
169 %if h.is_svn(repo_instance):
169 %if h.is_svn(repo_instance):
170 <i class="icon-svn"></i>
170 <i class="icon-svn"></i>
171 %endif
171 %endif
172
172
173 ## public/private
173 ## public/private
174 %if repo_instance.private:
174 %if repo_instance.private:
175 <i class="icon-repo-private"></i>
175 <i class="icon-repo-private"></i>
176 %else:
176 %else:
177 <i class="icon-repo-public"></i>
177 <i class="icon-repo-public"></i>
178 %endif
178 %endif
179
179
180 ## repo name with group name
180 ## repo name with group name
181 ${h.breadcrumb_repo_link(c.rhodecode_db_repo)}
181 ${h.breadcrumb_repo_link(c.rhodecode_db_repo)}
182
182
183 </div>
183 </div>
184
184
185 ## FORKED
185 ## FORKED
186 %if repo_instance.fork:
186 %if repo_instance.fork:
187 <p>
187 <p>
188 <i class="icon-code-fork"></i> ${_('Fork of')}
188 <i class="icon-code-fork"></i> ${_('Fork of')}
189 <a href="${h.url('summary_home',repo_name=repo_instance.fork.repo_name)}">${repo_instance.fork.repo_name}</a>
189 <a href="${h.url('summary_home',repo_name=repo_instance.fork.repo_name)}">${repo_instance.fork.repo_name}</a>
190 </p>
190 </p>
191 %endif
191 %endif
192
192
193 ## IMPORTED FROM REMOTE
193 ## IMPORTED FROM REMOTE
194 %if repo_instance.clone_uri:
194 %if repo_instance.clone_uri:
195 <p>
195 <p>
196 <i class="icon-code-fork"></i> ${_('Clone from')}
196 <i class="icon-code-fork"></i> ${_('Clone from')}
197 <a href="${h.url(h.safe_str(h.hide_credentials(repo_instance.clone_uri)))}">${h.hide_credentials(repo_instance.clone_uri)}</a>
197 <a href="${h.url(h.safe_str(h.hide_credentials(repo_instance.clone_uri)))}">${h.hide_credentials(repo_instance.clone_uri)}</a>
198 </p>
198 </p>
199 %endif
199 %endif
200
200
201 ## LOCKING STATUS
201 ## LOCKING STATUS
202 %if repo_instance.locked[0]:
202 %if repo_instance.locked[0]:
203 <p class="locking_locked">
203 <p class="locking_locked">
204 <i class="icon-repo-lock"></i>
204 <i class="icon-repo-lock"></i>
205 ${_('Repository locked by %(user)s') % {'user': h.person_by_id(repo_instance.locked[0])}}
205 ${_('Repository locked by %(user)s') % {'user': h.person_by_id(repo_instance.locked[0])}}
206 </p>
206 </p>
207 %elif repo_instance.enable_locking:
207 %elif repo_instance.enable_locking:
208 <p class="locking_unlocked">
208 <p class="locking_unlocked">
209 <i class="icon-repo-unlock"></i>
209 <i class="icon-repo-unlock"></i>
210 ${_('Repository not locked. Pull repository to lock it.')}
210 ${_('Repository not locked. Pull repository to lock it.')}
211 </p>
211 </p>
212 %endif
212 %endif
213
213
214 </div>
214 </div>
215 </%def>
215 </%def>
216
216
217 <%def name="repo_menu(active=None)">
217 <%def name="repo_menu(active=None)">
218 <%
218 <%
219 def is_active(selected):
219 def is_active(selected):
220 if selected == active:
220 if selected == active:
221 return "active"
221 return "active"
222 %>
222 %>
223
223
224 <!--- CONTEXT BAR -->
224 <!--- CONTEXT BAR -->
225 <div id="context-bar">
225 <div id="context-bar">
226 <div class="wrapper">
226 <div class="wrapper">
227 <ul id="context-pages" class="horizontal-list navigation">
227 <ul id="context-pages" class="horizontal-list navigation">
228 <li class="${is_active('summary')}"><a class="menulink" href="${h.url('summary_home', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li>
228 <li class="${is_active('summary')}"><a class="menulink" href="${h.url('summary_home', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li>
229 <li class="${is_active('changelog')}"><a class="menulink" href="${h.url('changelog_home', repo_name=c.repo_name)}"><div class="menulabel">${_('Changelog')}</div></a></li>
229 <li class="${is_active('changelog')}"><a class="menulink" href="${h.url('changelog_home', repo_name=c.repo_name)}"><div class="menulabel">${_('Changelog')}</div></a></li>
230 <li class="${is_active('files')}"><a class="menulink" href="${h.url('files_home', repo_name=c.repo_name, revision=c.rhodecode_db_repo.landing_rev[1])}"><div class="menulabel">${_('Files')}</div></a></li>
230 <li class="${is_active('files')}"><a class="menulink" href="${h.url('files_home', repo_name=c.repo_name, revision=c.rhodecode_db_repo.landing_rev[1])}"><div class="menulabel">${_('Files')}</div></a></li>
231 <li class="${is_active('compare')}">
231 <li class="${is_active('compare')}">
232 <a class="menulink" href="${h.url('compare_home',repo_name=c.repo_name)}"><div class="menulabel">${_('Compare')}</div></a>
232 <a class="menulink" href="${h.url('compare_home',repo_name=c.repo_name)}"><div class="menulabel">${_('Compare')}</div></a>
233 </li>
233 </li>
234 ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()"
234 ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()"
235 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
235 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
236 <li class="${is_active('showpullrequest')}">
236 <li class="${is_active('showpullrequest')}">
237 <a class="menulink" href="${h.url('pullrequest_show_all',repo_name=c.repo_name)}" title="${_('Show Pull Requests for %s') % c.repo_name}">
237 <a class="menulink" href="${h.url('pullrequest_show_all',repo_name=c.repo_name)}" title="${_('Show Pull Requests for %s') % c.repo_name}">
238 %if c.repository_pull_requests:
238 %if c.repository_pull_requests:
239 <span class="pr_notifications">${c.repository_pull_requests}</span>
239 <span class="pr_notifications">${c.repository_pull_requests}</span>
240 %endif
240 %endif
241 <div class="menulabel">${_('Pull Requests')}</div>
241 <div class="menulabel">${_('Pull Requests')}</div>
242 </a>
242 </a>
243 </li>
243 </li>
244 %endif
244 %endif
245 <li class="${is_active('options')}">
245 <li class="${is_active('options')}">
246 <a class="menulink" href="#" class="dropdown"><div class="menulabel">${_('Options')} <div class="show_more"></div></div></a>
246 <a class="menulink" href="#" class="dropdown"><div class="menulabel">${_('Options')} <div class="show_more"></div></div></a>
247 <ul class="submenu">
247 <ul class="submenu">
248 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
248 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
249 <li><a href="${h.url('edit_repo',repo_name=c.repo_name)}">${_('Settings')}</a></li>
249 <li><a href="${h.url('edit_repo',repo_name=c.repo_name)}">${_('Settings')}</a></li>
250 %endif
250 %endif
251 %if c.rhodecode_db_repo.fork:
251 %if c.rhodecode_db_repo.fork:
252 <li><a href="${h.url('compare_url',repo_name=c.rhodecode_db_repo.fork.repo_name,source_ref_type=c.rhodecode_db_repo.landing_rev[0],source_ref=c.rhodecode_db_repo.landing_rev[1], target_repo=c.repo_name,target_ref_type='branch' if request.GET.get('branch') else c.rhodecode_db_repo.landing_rev[0],target_ref=request.GET.get('branch') or c.rhodecode_db_repo.landing_rev[1], merge=1)}">
252 <li><a href="${h.url('compare_url',repo_name=c.rhodecode_db_repo.fork.repo_name,source_ref_type=c.rhodecode_db_repo.landing_rev[0],source_ref=c.rhodecode_db_repo.landing_rev[1], target_repo=c.repo_name,target_ref_type='branch' if request.GET.get('branch') else c.rhodecode_db_repo.landing_rev[0],target_ref=request.GET.get('branch') or c.rhodecode_db_repo.landing_rev[1], merge=1)}">
253 ${_('Compare fork')}</a></li>
253 ${_('Compare fork')}</a></li>
254 %endif
254 %endif
255
255
256 <li><a href="${h.url('search_repo_home',repo_name=c.repo_name)}">${_('Search')}</a></li>
256 <li><a href="${h.url('search_repo_home',repo_name=c.repo_name)}">${_('Search')}</a></li>
257
257
258 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking:
258 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking:
259 %if c.rhodecode_db_repo.locked[0]:
259 %if c.rhodecode_db_repo.locked[0]:
260 <li><a class="locking_del" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Unlock')}</a></li>
260 <li><a class="locking_del" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Unlock')}</a></li>
261 %else:
261 %else:
262 <li><a class="locking_add" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Lock')}</a></li>
262 <li><a class="locking_add" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Lock')}</a></li>
263 %endif
263 %endif
264 %endif
264 %endif
265 %if c.rhodecode_user.username != h.DEFAULT_USER:
265 %if c.rhodecode_user.username != h.DEFAULT_USER:
266 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
266 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
267 <li><a href="${h.url('repo_fork_home',repo_name=c.repo_name)}">${_('Fork')}</a></li>
267 <li><a href="${h.url('repo_fork_home',repo_name=c.repo_name)}">${_('Fork')}</a></li>
268 <li><a href="${h.url('pullrequest_home',repo_name=c.repo_name)}">${_('Create Pull Request')}</a></li>
268 <li><a href="${h.url('pullrequest_home',repo_name=c.repo_name)}">${_('Create Pull Request')}</a></li>
269 %endif
269 %endif
270 %endif
270 %endif
271 </ul>
271 </ul>
272 </li>
272 </li>
273 </ul>
273 </ul>
274 </div>
274 </div>
275 <div class="clear"></div>
275 <div class="clear"></div>
276 </div>
276 </div>
277 <!--- END CONTEXT BAR -->
277 <!--- END CONTEXT BAR -->
278
278
279 </%def>
279 </%def>
280
280
281 <%def name="usermenu(active=False)">
281 <%def name="usermenu(active=False)">
282 ## USER MENU
282 ## USER MENU
283 <li id="quick_login_li" class="${'active' if active else ''}">
283 <li id="quick_login_li" class="${'active' if active else ''}">
284 <a id="quick_login_link" class="menulink childs">
284 <a id="quick_login_link" class="menulink childs">
285 ${gravatar(c.rhodecode_user.email, 20)}
285 ${gravatar(c.rhodecode_user.email, 20)}
286 <span class="user">
286 <span class="user">
287 %if c.rhodecode_user.username != h.DEFAULT_USER:
287 %if c.rhodecode_user.username != h.DEFAULT_USER:
288 <span class="menu_link_user">${c.rhodecode_user.username}</span><div class="show_more"></div>
288 <span class="menu_link_user">${c.rhodecode_user.username}</span><div class="show_more"></div>
289 %else:
289 %else:
290 <span>${_('Sign in')}</span>
290 <span>${_('Sign in')}</span>
291 %endif
291 %endif
292 </span>
292 </span>
293 </a>
293 </a>
294
294
295 <div class="user-menu submenu">
295 <div class="user-menu submenu">
296 <div id="quick_login">
296 <div id="quick_login">
297 %if c.rhodecode_user.username == h.DEFAULT_USER:
297 %if c.rhodecode_user.username == h.DEFAULT_USER:
298 <h4>${_('Sign in to your account')}</h4>
298 <h4>${_('Sign in to your account')}</h4>
299 ${h.form(h.route_path('login', _query={'came_from': h.url.current()}), needs_csrf_token=False)}
299 ${h.form(h.route_path('login', _query={'came_from': h.url.current()}), needs_csrf_token=False)}
300 <div class="form form-vertical">
300 <div class="form form-vertical">
301 <div class="fields">
301 <div class="fields">
302 <div class="field">
302 <div class="field">
303 <div class="label">
303 <div class="label">
304 <label for="username">${_('Username')}:</label>
304 <label for="username">${_('Username')}:</label>
305 </div>
305 </div>
306 <div class="input">
306 <div class="input">
307 ${h.text('username',class_='focus',tabindex=1)}
307 ${h.text('username',class_='focus',tabindex=1)}
308 </div>
308 </div>
309
309
310 </div>
310 </div>
311 <div class="field">
311 <div class="field">
312 <div class="label">
312 <div class="label">
313 <label for="password">${_('Password')}:</label>
313 <label for="password">${_('Password')}:</label>
314 %if h.HasPermissionAny('hg.password_reset.enabled')():
314 %if h.HasPermissionAny('hg.password_reset.enabled')():
315 <span class="forgot_password">${h.link_to(_('(Forgot password?)'),h.route_path('reset_password'), class_='pwd_reset')}</span>
315 <span class="forgot_password">${h.link_to(_('(Forgot password?)'),h.route_path('reset_password'), class_='pwd_reset')}</span>
316 %endif
316 %endif
317 </div>
317 </div>
318 <div class="input">
318 <div class="input">
319 ${h.password('password',class_='focus',tabindex=2)}
319 ${h.password('password',class_='focus',tabindex=2)}
320 </div>
320 </div>
321 </div>
321 </div>
322 <div class="buttons">
322 <div class="buttons">
323 <div class="register">
323 <div class="register">
324 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
324 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
325 ${h.link_to(_("Don't have an account ?"),h.route_path('register'))}
325 ${h.link_to(_("Don't have an account ?"),h.route_path('register'))}
326 %endif
326 %endif
327 </div>
327 </div>
328 <div class="submit">
328 <div class="submit">
329 ${h.submit('sign_in',_('Sign In'),class_="btn btn-small",tabindex=3)}
329 ${h.submit('sign_in',_('Sign In'),class_="btn btn-small",tabindex=3)}
330 </div>
330 </div>
331 </div>
331 </div>
332 </div>
332 </div>
333 </div>
333 </div>
334 ${h.end_form()}
334 ${h.end_form()}
335 %else:
335 %else:
336 <div class="">
336 <div class="">
337 <div class="big_gravatar">${gravatar(c.rhodecode_user.email, 48)}</div>
337 <div class="big_gravatar">${gravatar(c.rhodecode_user.email, 48)}</div>
338 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
338 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
339 <div class="email">${c.rhodecode_user.email}</div>
339 <div class="email">${c.rhodecode_user.email}</div>
340 </div>
340 </div>
341 <div class="">
341 <div class="">
342 <ol class="links">
342 <ol class="links">
343 <li>${h.link_to(_(u'My account'),h.route_path('my_account_profile'))}</li>
343 <li>${h.link_to(_(u'My account'),h.route_path('my_account_profile'))}</li>
344 % if c.rhodecode_user.personal_repo_group:
344 % if c.rhodecode_user.personal_repo_group:
345 <li>${h.link_to(_(u'My personal group'), h.url('repo_group_home', group_name=c.rhodecode_user.personal_repo_group.group_name))}</li>
345 <li>${h.link_to(_(u'My personal group'), h.url('repo_group_home', group_name=c.rhodecode_user.personal_repo_group.group_name))}</li>
346 % endif
346 % endif
347 <li class="logout">
347 <li class="logout">
348 ${h.secure_form(h.route_path('logout'))}
348 ${h.secure_form(h.route_path('logout'))}
349 ${h.submit('log_out', _(u'Sign Out'),class_="btn btn-primary")}
349 ${h.submit('log_out', _(u'Sign Out'),class_="btn btn-primary")}
350 ${h.end_form()}
350 ${h.end_form()}
351 </li>
351 </li>
352 </ol>
352 </ol>
353 </div>
353 </div>
354 %endif
354 %endif
355 </div>
355 </div>
356 </div>
356 </div>
357 %if c.rhodecode_user.username != h.DEFAULT_USER:
357 %if c.rhodecode_user.username != h.DEFAULT_USER:
358 <div class="pill_container">
358 <div class="pill_container">
359 % if c.unread_notifications == 0:
359 % if c.unread_notifications == 0:
360 <a class="menu_link_notifications empty" href="${h.url('notifications')}">${c.unread_notifications}</a>
360 <a class="menu_link_notifications empty" href="${h.url('notifications')}">${c.unread_notifications}</a>
361 % else:
361 % else:
362 <a class="menu_link_notifications" href="${h.url('notifications')}">${c.unread_notifications}</a>
362 <a class="menu_link_notifications" href="${h.url('notifications')}">${c.unread_notifications}</a>
363 % endif
363 % endif
364 </div>
364 </div>
365 % endif
365 % endif
366 </li>
366 </li>
367 </%def>
367 </%def>
368
368
369 <%def name="menu_items(active=None)">
369 <%def name="menu_items(active=None)">
370 <%
370 <%
371 def is_active(selected):
371 def is_active(selected):
372 if selected == active:
372 if selected == active:
373 return "active"
373 return "active"
374 return ""
374 return ""
375 %>
375 %>
376 <ul id="quick" class="main_nav navigation horizontal-list">
376 <ul id="quick" class="main_nav navigation horizontal-list">
377 <!-- repo switcher -->
377 <!-- repo switcher -->
378 <li class="${is_active('repositories')} repo_switcher_li has_select2">
378 <li class="${is_active('repositories')} repo_switcher_li has_select2">
379 <input id="repo_switcher" name="repo_switcher" type="hidden">
379 <input id="repo_switcher" name="repo_switcher" type="hidden">
380 </li>
380 </li>
381
381
382 ## ROOT MENU
382 ## ROOT MENU
383 %if c.rhodecode_user.username != h.DEFAULT_USER:
383 %if c.rhodecode_user.username != h.DEFAULT_USER:
384 <li class="${is_active('journal')}">
384 <li class="${is_active('journal')}">
385 <a class="menulink" title="${_('Show activity journal')}" href="${h.url('journal')}">
385 <a class="menulink" title="${_('Show activity journal')}" href="${h.url('journal')}">
386 <div class="menulabel">${_('Journal')}</div>
386 <div class="menulabel">${_('Journal')}</div>
387 </a>
387 </a>
388 </li>
388 </li>
389 %else:
389 %else:
390 <li class="${is_active('journal')}">
390 <li class="${is_active('journal')}">
391 <a class="menulink" title="${_('Show Public activity journal')}" href="${h.url('public_journal')}">
391 <a class="menulink" title="${_('Show Public activity journal')}" href="${h.url('public_journal')}">
392 <div class="menulabel">${_('Public journal')}</div>
392 <div class="menulabel">${_('Public journal')}</div>
393 </a>
393 </a>
394 </li>
394 </li>
395 %endif
395 %endif
396 <li class="${is_active('gists')}">
396 <li class="${is_active('gists')}">
397 <a class="menulink childs" title="${_('Show Gists')}" href="${h.url('gists')}">
397 <a class="menulink childs" title="${_('Show Gists')}" href="${h.url('gists')}">
398 <div class="menulabel">${_('Gists')}</div>
398 <div class="menulabel">${_('Gists')}</div>
399 </a>
399 </a>
400 </li>
400 </li>
401 <li class="${is_active('search')}">
401 <li class="${is_active('search')}">
402 <a class="menulink" title="${_('Search in repositories you have access to')}" href="${h.url('search')}">
402 <a class="menulink" title="${_('Search in repositories you have access to')}" href="${h.url('search')}">
403 <div class="menulabel">${_('Search')}</div>
403 <div class="menulabel">${_('Search')}</div>
404 </a>
404 </a>
405 </li>
405 </li>
406 % if h.HasPermissionAll('hg.admin')('access admin main page'):
406 % if h.HasPermissionAll('hg.admin')('access admin main page'):
407 <li class="${is_active('admin')}">
407 <li class="${is_active('admin')}">
408 <a class="menulink childs" title="${_('Admin settings')}" href="#" onclick="return false;">
408 <a class="menulink childs" title="${_('Admin settings')}" href="#" onclick="return false;">
409 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
409 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
410 </a>
410 </a>
411 ${admin_menu()}
411 ${admin_menu()}
412 </li>
412 </li>
413 % elif c.rhodecode_user.repositories_admin or c.rhodecode_user.repository_groups_admin or c.rhodecode_user.user_groups_admin:
413 % elif c.rhodecode_user.repositories_admin or c.rhodecode_user.repository_groups_admin or c.rhodecode_user.user_groups_admin:
414 <li class="${is_active('admin')}">
414 <li class="${is_active('admin')}">
415 <a class="menulink childs" title="${_('Delegated Admin settings')}">
415 <a class="menulink childs" title="${_('Delegated Admin settings')}">
416 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
416 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
417 </a>
417 </a>
418 ${admin_menu_simple(c.rhodecode_user.repositories_admin,
418 ${admin_menu_simple(c.rhodecode_user.repositories_admin,
419 c.rhodecode_user.repository_groups_admin,
419 c.rhodecode_user.repository_groups_admin,
420 c.rhodecode_user.user_groups_admin or h.HasPermissionAny('hg.usergroup.create.true')())}
420 c.rhodecode_user.user_groups_admin or h.HasPermissionAny('hg.usergroup.create.true')())}
421 </li>
421 </li>
422 % endif
422 % endif
423 % if c.debug_style:
423 % if c.debug_style:
424 <li class="${is_active('debug_style')}">
424 <li class="${is_active('debug_style')}">
425 <a class="menulink" title="${_('Style')}" href="${h.url('debug_style_home')}">
425 <a class="menulink" title="${_('Style')}" href="${h.url('debug_style_home')}">
426 <div class="menulabel">${_('Style')}</div>
426 <div class="menulabel">${_('Style')}</div>
427 </a>
427 </a>
428 </li>
428 </li>
429 % endif
429 % endif
430 ## render extra user menu
430 ## render extra user menu
431 ${usermenu(active=(active=='my_account'))}
431 ${usermenu(active=(active=='my_account'))}
432 </ul>
432 </ul>
433
433
434 <script type="text/javascript">
434 <script type="text/javascript">
435 var visual_show_public_icon = "${c.visual.show_public_icon}" == "True";
435 var visual_show_public_icon = "${c.visual.show_public_icon}" == "True";
436
436
437 /*format the look of items in the list*/
437 /*format the look of items in the list*/
438 var format = function(state, escapeMarkup){
438 var format = function(state, escapeMarkup){
439 if (!state.id){
439 if (!state.id){
440 return state.text; // optgroup
440 return state.text; // optgroup
441 }
441 }
442 var obj_dict = state.obj;
442 var obj_dict = state.obj;
443 var tmpl = '';
443 var tmpl = '';
444
444
445 if(obj_dict && state.type == 'repo'){
445 if(obj_dict && state.type == 'repo'){
446 if(obj_dict['repo_type'] === 'hg'){
446 if(obj_dict['repo_type'] === 'hg'){
447 tmpl += '<i class="icon-hg"></i> ';
447 tmpl += '<i class="icon-hg"></i> ';
448 }
448 }
449 else if(obj_dict['repo_type'] === 'git'){
449 else if(obj_dict['repo_type'] === 'git'){
450 tmpl += '<i class="icon-git"></i> ';
450 tmpl += '<i class="icon-git"></i> ';
451 }
451 }
452 else if(obj_dict['repo_type'] === 'svn'){
452 else if(obj_dict['repo_type'] === 'svn'){
453 tmpl += '<i class="icon-svn"></i> ';
453 tmpl += '<i class="icon-svn"></i> ';
454 }
454 }
455 if(obj_dict['private']){
455 if(obj_dict['private']){
456 tmpl += '<i class="icon-lock" ></i> ';
456 tmpl += '<i class="icon-lock" ></i> ';
457 }
457 }
458 else if(visual_show_public_icon){
458 else if(visual_show_public_icon){
459 tmpl += '<i class="icon-unlock-alt"></i> ';
459 tmpl += '<i class="icon-unlock-alt"></i> ';
460 }
460 }
461 }
461 }
462 if(obj_dict && state.type == 'commit') {
462 if(obj_dict && state.type == 'commit') {
463 tmpl += '<i class="icon-tag"></i>';
463 tmpl += '<i class="icon-tag"></i>';
464 }
464 }
465 if(obj_dict && state.type == 'group'){
465 if(obj_dict && state.type == 'group'){
466 tmpl += '<i class="icon-folder-close"></i> ';
466 tmpl += '<i class="icon-folder-close"></i> ';
467 }
467 }
468 tmpl += escapeMarkup(state.text);
468 tmpl += escapeMarkup(state.text);
469 return tmpl;
469 return tmpl;
470 };
470 };
471
471
472 var formatResult = function(result, container, query, escapeMarkup) {
472 var formatResult = function(result, container, query, escapeMarkup) {
473 return format(result, escapeMarkup);
473 return format(result, escapeMarkup);
474 };
474 };
475
475
476 var formatSelection = function(data, container, escapeMarkup) {
476 var formatSelection = function(data, container, escapeMarkup) {
477 return format(data, escapeMarkup);
477 return format(data, escapeMarkup);
478 };
478 };
479
479
480 $("#repo_switcher").select2({
480 $("#repo_switcher").select2({
481 cachedDataSource: {},
481 cachedDataSource: {},
482 minimumInputLength: 2,
482 minimumInputLength: 2,
483 placeholder: '<div class="menulabel">${_('Go to')} <div class="show_more"></div></div>',
483 placeholder: '<div class="menulabel">${_('Go to')} <div class="show_more"></div></div>',
484 dropdownAutoWidth: true,
484 dropdownAutoWidth: true,
485 formatResult: formatResult,
485 formatResult: formatResult,
486 formatSelection: formatSelection,
486 formatSelection: formatSelection,
487 containerCssClass: "repo-switcher",
487 containerCssClass: "repo-switcher",
488 dropdownCssClass: "repo-switcher-dropdown",
488 dropdownCssClass: "repo-switcher-dropdown",
489 escapeMarkup: function(m){
489 escapeMarkup: function(m){
490 // don't escape our custom placeholder
490 // don't escape our custom placeholder
491 if(m.substr(0,23) == '<div class="menulabel">'){
491 if(m.substr(0,23) == '<div class="menulabel">'){
492 return m;
492 return m;
493 }
493 }
494
494
495 return Select2.util.escapeMarkup(m);
495 return Select2.util.escapeMarkup(m);
496 },
496 },
497 query: $.debounce(250, function(query){
497 query: $.debounce(250, function(query){
498 self = this;
498 self = this;
499 var cacheKey = query.term;
499 var cacheKey = query.term;
500 var cachedData = self.cachedDataSource[cacheKey];
500 var cachedData = self.cachedDataSource[cacheKey];
501
501
502 if (cachedData) {
502 if (cachedData) {
503 query.callback({results: cachedData.results});
503 query.callback({results: cachedData.results});
504 } else {
504 } else {
505 $.ajax({
505 $.ajax({
506 url: pyroutes.url('goto_switcher_data'),
506 url: pyroutes.url('goto_switcher_data'),
507 data: {'query': query.term},
507 data: {'query': query.term},
508 dataType: 'json',
508 dataType: 'json',
509 type: 'GET',
509 type: 'GET',
510 success: function(data) {
510 success: function(data) {
511 self.cachedDataSource[cacheKey] = data;
511 self.cachedDataSource[cacheKey] = data;
512 query.callback({results: data.results});
512 query.callback({results: data.results});
513 },
513 },
514 error: function(data, textStatus, errorThrown) {
514 error: function(data, textStatus, errorThrown) {
515 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
515 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
516 }
516 }
517 })
517 })
518 }
518 }
519 })
519 })
520 });
520 });
521
521
522 $("#repo_switcher").on('select2-selecting', function(e){
522 $("#repo_switcher").on('select2-selecting', function(e){
523 e.preventDefault();
523 e.preventDefault();
524 window.location = e.choice.url;
524 window.location = e.choice.url;
525 });
525 });
526
526
527 </script>
527 </script>
528 <script src="${h.asset('js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script>
528 <script src="${h.asset('js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script>
529 </%def>
529 </%def>
530
530
531 <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
531 <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
532 <div class="modal-dialog">
532 <div class="modal-dialog">
533 <div class="modal-content">
533 <div class="modal-content">
534 <div class="modal-header">
534 <div class="modal-header">
535 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
535 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
536 <h4 class="modal-title" id="myModalLabel">${_('Keyboard shortcuts')}</h4>
536 <h4 class="modal-title" id="myModalLabel">${_('Keyboard shortcuts')}</h4>
537 </div>
537 </div>
538 <div class="modal-body">
538 <div class="modal-body">
539 <div class="block-left">
539 <div class="block-left">
540 <table class="keyboard-mappings">
540 <table class="keyboard-mappings">
541 <tbody>
541 <tbody>
542 <tr>
542 <tr>
543 <th></th>
543 <th></th>
544 <th>${_('Site-wide shortcuts')}</th>
544 <th>${_('Site-wide shortcuts')}</th>
545 </tr>
545 </tr>
546 <%
546 <%
547 elems = [
547 elems = [
548 ('/', 'Open quick search box'),
548 ('/', 'Open quick search box'),
549 ('g h', 'Goto home page'),
549 ('g h', 'Goto home page'),
550 ('g g', 'Goto my private gists page'),
550 ('g g', 'Goto my private gists page'),
551 ('g G', 'Goto my public gists page'),
551 ('g G', 'Goto my public gists page'),
552 ('n r', 'New repository page'),
552 ('n r', 'New repository page'),
553 ('n g', 'New gist page'),
553 ('n g', 'New gist page'),
554 ]
554 ]
555 %>
555 %>
556 %for key, desc in elems:
556 %for key, desc in elems:
557 <tr>
557 <tr>
558 <td class="keys">
558 <td class="keys">
559 <span class="key tag">${key}</span>
559 <span class="key tag">${key}</span>
560 </td>
560 </td>
561 <td>${desc}</td>
561 <td>${desc}</td>
562 </tr>
562 </tr>
563 %endfor
563 %endfor
564 </tbody>
564 </tbody>
565 </table>
565 </table>
566 </div>
566 </div>
567 <div class="block-left">
567 <div class="block-left">
568 <table class="keyboard-mappings">
568 <table class="keyboard-mappings">
569 <tbody>
569 <tbody>
570 <tr>
570 <tr>
571 <th></th>
571 <th></th>
572 <th>${_('Repositories')}</th>
572 <th>${_('Repositories')}</th>
573 </tr>
573 </tr>
574 <%
574 <%
575 elems = [
575 elems = [
576 ('g s', 'Goto summary page'),
576 ('g s', 'Goto summary page'),
577 ('g c', 'Goto changelog page'),
577 ('g c', 'Goto changelog page'),
578 ('g f', 'Goto files page'),
578 ('g f', 'Goto files page'),
579 ('g F', 'Goto files page with file search activated'),
579 ('g F', 'Goto files page with file search activated'),
580 ('g p', 'Goto pull requests page'),
580 ('g p', 'Goto pull requests page'),
581 ('g o', 'Goto repository settings'),
581 ('g o', 'Goto repository settings'),
582 ('g O', 'Goto repository permissions settings'),
582 ('g O', 'Goto repository permissions settings'),
583 ]
583 ]
584 %>
584 %>
585 %for key, desc in elems:
585 %for key, desc in elems:
586 <tr>
586 <tr>
587 <td class="keys">
587 <td class="keys">
588 <span class="key tag">${key}</span>
588 <span class="key tag">${key}</span>
589 </td>
589 </td>
590 <td>${desc}</td>
590 <td>${desc}</td>
591 </tr>
591 </tr>
592 %endfor
592 %endfor
593 </tbody>
593 </tbody>
594 </table>
594 </table>
595 </div>
595 </div>
596 </div>
596 </div>
597 <div class="modal-footer">
597 <div class="modal-footer">
598 </div>
598 </div>
599 </div><!-- /.modal-content -->
599 </div><!-- /.modal-content -->
600 </div><!-- /.modal-dialog -->
600 </div><!-- /.modal-dialog -->
601 </div><!-- /.modal -->
601 </div><!-- /.modal -->
@@ -1,218 +1,218 b''
1 ## snippet for displaying issue tracker settings
1 ## snippet for displaying issue tracker settings
2 ## usage:
2 ## usage:
3 ## <%namespace name="its" file="/base/issue_tracker_settings.mako"/>
3 ## <%namespace name="its" file="/base/issue_tracker_settings.mako"/>
4 ## ${its.issue_tracker_settings_table(patterns, form_url, delete_url)}
4 ## ${its.issue_tracker_settings_table(patterns, form_url, delete_url)}
5 ## ${its.issue_tracker_settings_test(test_url)}
5 ## ${its.issue_tracker_settings_test(test_url)}
6
6
7 <%def name="issue_tracker_settings_table(patterns, form_url, delete_url)">
7 <%def name="issue_tracker_settings_table(patterns, form_url, delete_url)">
8 <table class="rctable issuetracker">
8 <table class="rctable issuetracker">
9 <tr>
9 <tr>
10 <th>${_('Description')}</th>
10 <th>${_('Description')}</th>
11 <th>${_('Pattern')}</th>
11 <th>${_('Pattern')}</th>
12 <th>${_('Url')}</th>
12 <th>${_('Url')}</th>
13 <th>${_('Prefix')}</th>
13 <th>${_('Prefix')}</th>
14 <th ></th>
14 <th ></th>
15 </tr>
15 </tr>
16 <tr>
16 <tr>
17 <td class="td-description issue-tracker-example">Example</td>
17 <td class="td-description issue-tracker-example">Example</td>
18 <td class="td-regex issue-tracker-example">${'(?:#)(?P<issue_id>\d+)'}</td>
18 <td class="td-regex issue-tracker-example">${'(?:#)(?P<issue_id>\d+)'}</td>
19 <td class="td-url issue-tracker-example">${'https://myissueserver.com/${repo}/issue/${issue_id}'}</td>
19 <td class="td-url issue-tracker-example">${'https://myissueserver.com/${repo}/issue/${issue_id}'}</td>
20 <td class="td-prefix issue-tracker-example">#</td>
20 <td class="td-prefix issue-tracker-example">#</td>
21 <td class="issue-tracker-example"><a href="https://docs.rhodecode.com/RhodeCode-Enterprise/issue-trackers/issue-trackers.html" target="_blank">${_('Read more')}</a></td>
21 <td class="issue-tracker-example"><a href="${h.route_url('enterprise_issue_tracker_settings')}" target="_blank">${_('Read more')}</a></td>
22 </tr>
22 </tr>
23 %for uid, entry in patterns:
23 %for uid, entry in patterns:
24 <tr id="entry_${uid}">
24 <tr id="entry_${uid}">
25 <td class="td-description issuetracker_desc">
25 <td class="td-description issuetracker_desc">
26 <span class="entry">
26 <span class="entry">
27 ${entry.desc}
27 ${entry.desc}
28 </span>
28 </span>
29 <span class="edit">
29 <span class="edit">
30 ${h.text('new_pattern_description_'+uid, class_='medium-inline', value=entry.desc or '')}
30 ${h.text('new_pattern_description_'+uid, class_='medium-inline', value=entry.desc or '')}
31 </span>
31 </span>
32 </td>
32 </td>
33 <td class="td-regex issuetracker_pat">
33 <td class="td-regex issuetracker_pat">
34 <span class="entry">
34 <span class="entry">
35 ${entry.pat}
35 ${entry.pat}
36 </span>
36 </span>
37 <span class="edit">
37 <span class="edit">
38 ${h.text('new_pattern_pattern_'+uid, class_='medium-inline', value=entry.pat or '')}
38 ${h.text('new_pattern_pattern_'+uid, class_='medium-inline', value=entry.pat or '')}
39 </span>
39 </span>
40 </td>
40 </td>
41 <td class="td-url issuetracker_url">
41 <td class="td-url issuetracker_url">
42 <span class="entry">
42 <span class="entry">
43 ${entry.url}
43 ${entry.url}
44 </span>
44 </span>
45 <span class="edit">
45 <span class="edit">
46 ${h.text('new_pattern_url_'+uid, class_='medium-inline', value=entry.url or '')}
46 ${h.text('new_pattern_url_'+uid, class_='medium-inline', value=entry.url or '')}
47 </span>
47 </span>
48 </td>
48 </td>
49 <td class="td-prefix issuetracker_pref">
49 <td class="td-prefix issuetracker_pref">
50 <span class="entry">
50 <span class="entry">
51 ${entry.pref}
51 ${entry.pref}
52 </span>
52 </span>
53 <span class="edit">
53 <span class="edit">
54 ${h.text('new_pattern_prefix_'+uid, class_='medium-inline', value=entry.pref or '')}
54 ${h.text('new_pattern_prefix_'+uid, class_='medium-inline', value=entry.pref or '')}
55 </span>
55 </span>
56 </td>
56 </td>
57 <td class="td-action">
57 <td class="td-action">
58 <div class="grid_edit">
58 <div class="grid_edit">
59 <span class="entry">
59 <span class="entry">
60 <a class="edit_issuetracker_entry" href="">${_('Edit')}</a>
60 <a class="edit_issuetracker_entry" href="">${_('Edit')}</a>
61 </span>
61 </span>
62 <span class="edit">
62 <span class="edit">
63 ${h.hidden('uid', uid)}
63 ${h.hidden('uid', uid)}
64 </span>
64 </span>
65 </div>
65 </div>
66 <div class="grid_delete">
66 <div class="grid_delete">
67 <span class="entry">
67 <span class="entry">
68 <a class="btn btn-link btn-danger delete_issuetracker_entry" data-desc="${entry.desc}" data-uid="${uid}">
68 <a class="btn btn-link btn-danger delete_issuetracker_entry" data-desc="${entry.desc}" data-uid="${uid}">
69 ${_('Delete')}
69 ${_('Delete')}
70 </a>
70 </a>
71 </span>
71 </span>
72 <span class="edit">
72 <span class="edit">
73 <a class="btn btn-link btn-danger edit_issuetracker_cancel" data-uid="${uid}">${_('Cancel')}</a>
73 <a class="btn btn-link btn-danger edit_issuetracker_cancel" data-uid="${uid}">${_('Cancel')}</a>
74 </span>
74 </span>
75 </div>
75 </div>
76 </td>
76 </td>
77 </tr>
77 </tr>
78 %endfor
78 %endfor
79 <tr id="last-row"></tr>
79 <tr id="last-row"></tr>
80 </table>
80 </table>
81 <p>
81 <p>
82 <a id="add_pattern" class="link">
82 <a id="add_pattern" class="link">
83 ${_('Add new')}
83 ${_('Add new')}
84 </a>
84 </a>
85 </p>
85 </p>
86
86
87 <script type="text/javascript">
87 <script type="text/javascript">
88 var newEntryLabel = $('label[for="new_entry"]');
88 var newEntryLabel = $('label[for="new_entry"]');
89
89
90 var resetEntry = function() {
90 var resetEntry = function() {
91 newEntryLabel.text("${_('New Entry')}:");
91 newEntryLabel.text("${_('New Entry')}:");
92 };
92 };
93
93
94 var delete_pattern = function(entry) {
94 var delete_pattern = function(entry) {
95 if (confirm("${_('Confirm to remove this pattern:')} "+$(entry).data('desc'))) {
95 if (confirm("${_('Confirm to remove this pattern:')} "+$(entry).data('desc'))) {
96 var request = $.ajax({
96 var request = $.ajax({
97 type: "POST",
97 type: "POST",
98 url: "${delete_url}",
98 url: "${delete_url}",
99 data: {
99 data: {
100 '_method': 'delete',
100 '_method': 'delete',
101 'csrf_token': CSRF_TOKEN,
101 'csrf_token': CSRF_TOKEN,
102 'uid':$(entry).data('uid')
102 'uid':$(entry).data('uid')
103 },
103 },
104 success: function(){
104 success: function(){
105 location.reload();
105 location.reload();
106 },
106 },
107 error: function(data, textStatus, errorThrown){
107 error: function(data, textStatus, errorThrown){
108 alert("Error while deleting entry.\nError code {0} ({1}). URL: {2}".format(data.status,data.statusText,$(entry)[0].url));
108 alert("Error while deleting entry.\nError code {0} ({1}). URL: {2}".format(data.status,data.statusText,$(entry)[0].url));
109 }
109 }
110 });
110 });
111 };
111 };
112 }
112 }
113
113
114 $('.delete_issuetracker_entry').on('click', function(e){
114 $('.delete_issuetracker_entry').on('click', function(e){
115 e.preventDefault();
115 e.preventDefault();
116 delete_pattern(this);
116 delete_pattern(this);
117 });
117 });
118
118
119 $('.edit_issuetracker_entry').on('click', function(e){
119 $('.edit_issuetracker_entry').on('click', function(e){
120 e.preventDefault();
120 e.preventDefault();
121 $(this).parents('tr').addClass('editopen');
121 $(this).parents('tr').addClass('editopen');
122 });
122 });
123
123
124 $('.edit_issuetracker_cancel').on('click', function(e){
124 $('.edit_issuetracker_cancel').on('click', function(e){
125 e.preventDefault();
125 e.preventDefault();
126 $(this).parents('tr').removeClass('editopen');
126 $(this).parents('tr').removeClass('editopen');
127 // Reset to original value
127 // Reset to original value
128 var uid = $(this).data('uid');
128 var uid = $(this).data('uid');
129 $('#'+uid+' input').each(function(e) {
129 $('#'+uid+' input').each(function(e) {
130 this.value = this.defaultValue;
130 this.value = this.defaultValue;
131 });
131 });
132 });
132 });
133
133
134 $('input#reset').on('click', function(e) {
134 $('input#reset').on('click', function(e) {
135 resetEntry();
135 resetEntry();
136 });
136 });
137
137
138 $('#add_pattern').on('click', function(e) {
138 $('#add_pattern').on('click', function(e) {
139 addNewPatternInput();
139 addNewPatternInput();
140 });
140 });
141 </script>
141 </script>
142 </%def>
142 </%def>
143
143
144 <%def name="issue_tracker_new_row()">
144 <%def name="issue_tracker_new_row()">
145 <table id="add-row-tmpl" style="display: none;">
145 <table id="add-row-tmpl" style="display: none;">
146 <tbody>
146 <tbody>
147 <tr class="new_pattern">
147 <tr class="new_pattern">
148 <td class="td-description issuetracker_desc">
148 <td class="td-description issuetracker_desc">
149 <span class="entry">
149 <span class="entry">
150 <input class="medium-inline" id="description_##UUID##" name="new_pattern_description_##UUID##" value="##DESCRIPTION##" type="text">
150 <input class="medium-inline" id="description_##UUID##" name="new_pattern_description_##UUID##" value="##DESCRIPTION##" type="text">
151 </span>
151 </span>
152 </td>
152 </td>
153 <td class="td-regex issuetracker_pat">
153 <td class="td-regex issuetracker_pat">
154 <span class="entry">
154 <span class="entry">
155 <input class="medium-inline" id="pattern_##UUID##" name="new_pattern_pattern_##UUID##" placeholder="Pattern"
155 <input class="medium-inline" id="pattern_##UUID##" name="new_pattern_pattern_##UUID##" placeholder="Pattern"
156 value="##PATTERN##" type="text">
156 value="##PATTERN##" type="text">
157 </span>
157 </span>
158 </td>
158 </td>
159 <td class="td-url issuetracker_url">
159 <td class="td-url issuetracker_url">
160 <span class="entry">
160 <span class="entry">
161 <input class="medium-inline" id="url_##UUID##" name="new_pattern_url_##UUID##" placeholder="Url" value="##URL##" type="text">
161 <input class="medium-inline" id="url_##UUID##" name="new_pattern_url_##UUID##" placeholder="Url" value="##URL##" type="text">
162 </span>
162 </span>
163 </td>
163 </td>
164 <td class="td-prefix issuetracker_pref">
164 <td class="td-prefix issuetracker_pref">
165 <span class="entry">
165 <span class="entry">
166 <input class="medium-inline" id="prefix_##UUID##" name="new_pattern_prefix_##UUID##" placeholder="Prefix" value="##PREFIX##" type="text">
166 <input class="medium-inline" id="prefix_##UUID##" name="new_pattern_prefix_##UUID##" placeholder="Prefix" value="##PREFIX##" type="text">
167 </span>
167 </span>
168 </td>
168 </td>
169 <td class="td-action">
169 <td class="td-action">
170 </td>
170 </td>
171 <input id="uid_##UUID##" name="uid_##UUID##" type="hidden" value="">
171 <input id="uid_##UUID##" name="uid_##UUID##" type="hidden" value="">
172 </tr>
172 </tr>
173 </tbody>
173 </tbody>
174 </table>
174 </table>
175 </%def>
175 </%def>
176
176
177 <%def name="issue_tracker_settings_test(test_url)">
177 <%def name="issue_tracker_settings_test(test_url)">
178 <div class="form-vertical">
178 <div class="form-vertical">
179 <div class="fields">
179 <div class="fields">
180 <div class="field">
180 <div class="field">
181 <div class='textarea-full'>
181 <div class='textarea-full'>
182 <textarea id="test_pattern_data" >
182 <textarea id="test_pattern_data" >
183 This commit fixes ticket #451.
183 This commit fixes ticket #451.
184 This is an example text for testing issue tracker patterns, add a pattern here and
184 This is an example text for testing issue tracker patterns, add a pattern here and
185 hit preview to see the link
185 hit preview to see the link
186 </textarea>
186 </textarea>
187 </div>
187 </div>
188 </div>
188 </div>
189 </div>
189 </div>
190 <div class="test_pattern_preview">
190 <div class="test_pattern_preview">
191 <div id="test_pattern" class="btn btn-small" >${_('Preview')}</div>
191 <div id="test_pattern" class="btn btn-small" >${_('Preview')}</div>
192 <p>${_('Test Pattern Preview')}</p>
192 <p>${_('Test Pattern Preview')}</p>
193 <div id="test_pattern_result"></div>
193 <div id="test_pattern_result"></div>
194 </div>
194 </div>
195 </div>
195 </div>
196
196
197 <script type="text/javascript">
197 <script type="text/javascript">
198 $('#test_pattern').on('click', function(e) {
198 $('#test_pattern').on('click', function(e) {
199 $.ajax({
199 $.ajax({
200 type: "POST",
200 type: "POST",
201 url: "${test_url}",
201 url: "${test_url}",
202 data: {
202 data: {
203 'test_text': $('#test_pattern_data').val(),
203 'test_text': $('#test_pattern_data').val(),
204 'csrf_token': CSRF_TOKEN
204 'csrf_token': CSRF_TOKEN
205 },
205 },
206 success: function(data){
206 success: function(data){
207 $('#test_pattern_result').html(data);
207 $('#test_pattern_result').html(data);
208 },
208 },
209 error: function(jqXHR, textStatus, errorThrown){
209 error: function(jqXHR, textStatus, errorThrown){
210 $('#test_pattern_result').html('Error: ' + errorThrown);
210 $('#test_pattern_result').html('Error: ' + errorThrown);
211 }
211 }
212 });
212 });
213 $('#test_pattern_result').show();
213 $('#test_pattern_result').show();
214 });
214 });
215 </script>
215 </script>
216 </%def>
216 </%def>
217
217
218
218
@@ -1,322 +1,322 b''
1 ## snippet for displaying vcs settings
1 ## snippet for displaying vcs settings
2 ## usage:
2 ## usage:
3 ## <%namespace name="vcss" file="/base/vcssettings.mako"/>
3 ## <%namespace name="vcss" file="/base/vcssettings.mako"/>
4 ## ${vcss.vcs_settings_fields()}
4 ## ${vcss.vcs_settings_fields()}
5
5
6 <%def name="vcs_settings_fields(suffix='', svn_branch_patterns=None, svn_tag_patterns=None, repo_type=None, display_globals=False, allow_repo_location_change=False, **kwargs)">
6 <%def name="vcs_settings_fields(suffix='', svn_branch_patterns=None, svn_tag_patterns=None, repo_type=None, display_globals=False, allow_repo_location_change=False, **kwargs)">
7 % if display_globals:
7 % if display_globals:
8 <div class="panel panel-default">
8 <div class="panel panel-default">
9 <div class="panel-heading" id="general">
9 <div class="panel-heading" id="general">
10 <h3 class="panel-title">${_('General')}<a class="permalink" href="#general"> ΒΆ</a></h3>
10 <h3 class="panel-title">${_('General')}<a class="permalink" href="#general"> ΒΆ</a></h3>
11 </div>
11 </div>
12 <div class="panel-body">
12 <div class="panel-body">
13 <div class="field">
13 <div class="field">
14 <div class="checkbox">
14 <div class="checkbox">
15 ${h.checkbox('web_push_ssl' + suffix, 'True')}
15 ${h.checkbox('web_push_ssl' + suffix, 'True')}
16 <label for="web_push_ssl${suffix}">${_('Require SSL for vcs operations')}</label>
16 <label for="web_push_ssl${suffix}">${_('Require SSL for vcs operations')}</label>
17 </div>
17 </div>
18 <div class="label">
18 <div class="label">
19 <span class="help-block">${_('Activate to set RhodeCode to require SSL for pushing or pulling. If SSL certificate is missing it will return a HTTP Error 406: Not Acceptable.')}</span>
19 <span class="help-block">${_('Activate to set RhodeCode to require SSL for pushing or pulling. If SSL certificate is missing it will return a HTTP Error 406: Not Acceptable.')}</span>
20 </div>
20 </div>
21 </div>
21 </div>
22 </div>
22 </div>
23 </div>
23 </div>
24 % endif
24 % endif
25
25
26 % if display_globals:
26 % if display_globals:
27 <div class="panel panel-default">
27 <div class="panel panel-default">
28 <div class="panel-heading" id="vcs-storage-options">
28 <div class="panel-heading" id="vcs-storage-options">
29 <h3 class="panel-title">${_('Main Storage Location')}<a class="permalink" href="#vcs-storage-options"> ΒΆ</a></h3>
29 <h3 class="panel-title">${_('Main Storage Location')}<a class="permalink" href="#vcs-storage-options"> ΒΆ</a></h3>
30 </div>
30 </div>
31 <div class="panel-body">
31 <div class="panel-body">
32 <div class="field">
32 <div class="field">
33 <div class="inputx locked_input">
33 <div class="inputx locked_input">
34 %if allow_repo_location_change:
34 %if allow_repo_location_change:
35 ${h.text('paths_root_path',size=59,readonly="readonly", class_="disabled")}
35 ${h.text('paths_root_path',size=59,readonly="readonly", class_="disabled")}
36 <span id="path_unlock" class="tooltip"
36 <span id="path_unlock" class="tooltip"
37 title="${h.tooltip(_('Click to unlock. You must restart RhodeCode in order to make this setting take effect.'))}">
37 title="${h.tooltip(_('Click to unlock. You must restart RhodeCode in order to make this setting take effect.'))}">
38 <div class="btn btn-default lock_input_button"><i id="path_unlock_icon" class="icon-lock"></i></div>
38 <div class="btn btn-default lock_input_button"><i id="path_unlock_icon" class="icon-lock"></i></div>
39 </span>
39 </span>
40 %else:
40 %else:
41 ${_('Repository location change is disabled. You can enable this by changing the `allow_repo_location_change` inside .ini file.')}
41 ${_('Repository location change is disabled. You can enable this by changing the `allow_repo_location_change` inside .ini file.')}
42 ## form still requires this but we cannot internally change it anyway
42 ## form still requires this but we cannot internally change it anyway
43 ${h.hidden('paths_root_path',size=30,readonly="readonly", class_="disabled")}
43 ${h.hidden('paths_root_path',size=30,readonly="readonly", class_="disabled")}
44 %endif
44 %endif
45 </div>
45 </div>
46 </div>
46 </div>
47 <div class="label">
47 <div class="label">
48 <span class="help-block">${_('Filesystem location where repositories should be stored. After changing this value a restart and rescan of the repository folder are required.')}</span>
48 <span class="help-block">${_('Filesystem location where repositories should be stored. After changing this value a restart and rescan of the repository folder are required.')}</span>
49 </div>
49 </div>
50 </div>
50 </div>
51 </div>
51 </div>
52 % endif
52 % endif
53
53
54 % if display_globals or repo_type in ['git', 'hg']:
54 % if display_globals or repo_type in ['git', 'hg']:
55 <div class="panel panel-default">
55 <div class="panel panel-default">
56 <div class="panel-heading" id="vcs-hooks-options">
56 <div class="panel-heading" id="vcs-hooks-options">
57 <h3 class="panel-title">${_('Internal Hooks')}<a class="permalink" href="#vcs-hooks-options"> ΒΆ</a></h3>
57 <h3 class="panel-title">${_('Internal Hooks')}<a class="permalink" href="#vcs-hooks-options"> ΒΆ</a></h3>
58 </div>
58 </div>
59 <div class="panel-body">
59 <div class="panel-body">
60 <div class="field">
60 <div class="field">
61 <div class="checkbox">
61 <div class="checkbox">
62 ${h.checkbox('hooks_changegroup_repo_size' + suffix, 'True', **kwargs)}
62 ${h.checkbox('hooks_changegroup_repo_size' + suffix, 'True', **kwargs)}
63 <label for="hooks_changegroup_repo_size${suffix}">${_('Show repository size after push')}</label>
63 <label for="hooks_changegroup_repo_size${suffix}">${_('Show repository size after push')}</label>
64 </div>
64 </div>
65
65
66 <div class="label">
66 <div class="label">
67 <span class="help-block">${_('Trigger a hook that calculates repository size after each push.')}</span>
67 <span class="help-block">${_('Trigger a hook that calculates repository size after each push.')}</span>
68 </div>
68 </div>
69 <div class="checkbox">
69 <div class="checkbox">
70 ${h.checkbox('hooks_changegroup_push_logger' + suffix, 'True', **kwargs)}
70 ${h.checkbox('hooks_changegroup_push_logger' + suffix, 'True', **kwargs)}
71 <label for="hooks_changegroup_push_logger${suffix}">${_('Execute pre/post push hooks')}</label>
71 <label for="hooks_changegroup_push_logger${suffix}">${_('Execute pre/post push hooks')}</label>
72 </div>
72 </div>
73 <div class="label">
73 <div class="label">
74 <span class="help-block">${_('Execute Built in pre/post push hooks. This also executes rcextensions hooks.')}</span>
74 <span class="help-block">${_('Execute Built in pre/post push hooks. This also executes rcextensions hooks.')}</span>
75 </div>
75 </div>
76 <div class="checkbox">
76 <div class="checkbox">
77 ${h.checkbox('hooks_outgoing_pull_logger' + suffix, 'True', **kwargs)}
77 ${h.checkbox('hooks_outgoing_pull_logger' + suffix, 'True', **kwargs)}
78 <label for="hooks_outgoing_pull_logger${suffix}">${_('Execute pre/post pull hooks')}</label>
78 <label for="hooks_outgoing_pull_logger${suffix}">${_('Execute pre/post pull hooks')}</label>
79 </div>
79 </div>
80 <div class="label">
80 <div class="label">
81 <span class="help-block">${_('Execute Built in pre/post pull hooks. This also executes rcextensions hooks.')}</span>
81 <span class="help-block">${_('Execute Built in pre/post pull hooks. This also executes rcextensions hooks.')}</span>
82 </div>
82 </div>
83 </div>
83 </div>
84 </div>
84 </div>
85 </div>
85 </div>
86 % endif
86 % endif
87
87
88 % if display_globals or repo_type in ['hg']:
88 % if display_globals or repo_type in ['hg']:
89 <div class="panel panel-default">
89 <div class="panel panel-default">
90 <div class="panel-heading" id="vcs-hg-options">
90 <div class="panel-heading" id="vcs-hg-options">
91 <h3 class="panel-title">${_('Mercurial Settings')}<a class="permalink" href="#vcs-hg-options"> ΒΆ</a></h3>
91 <h3 class="panel-title">${_('Mercurial Settings')}<a class="permalink" href="#vcs-hg-options"> ΒΆ</a></h3>
92 </div>
92 </div>
93 <div class="panel-body">
93 <div class="panel-body">
94 <div class="checkbox">
94 <div class="checkbox">
95 ${h.checkbox('extensions_largefiles' + suffix, 'True', **kwargs)}
95 ${h.checkbox('extensions_largefiles' + suffix, 'True', **kwargs)}
96 <label for="extensions_largefiles${suffix}">${_('Enable largefiles extension')}</label>
96 <label for="extensions_largefiles${suffix}">${_('Enable largefiles extension')}</label>
97 </div>
97 </div>
98 <div class="label">
98 <div class="label">
99 % if display_globals:
99 % if display_globals:
100 <span class="help-block">${_('Enable Largefiles extensions for all repositories.')}</span>
100 <span class="help-block">${_('Enable Largefiles extensions for all repositories.')}</span>
101 % else:
101 % else:
102 <span class="help-block">${_('Enable Largefiles extensions for this repository.')}</span>
102 <span class="help-block">${_('Enable Largefiles extensions for this repository.')}</span>
103 % endif
103 % endif
104 </div>
104 </div>
105
105
106 % if display_globals:
106 % if display_globals:
107 <div class="field">
107 <div class="field">
108 <div class="input">
108 <div class="input">
109 ${h.text('largefiles_usercache' + suffix, size=59)}
109 ${h.text('largefiles_usercache' + suffix, size=59)}
110 </div>
110 </div>
111 </div>
111 </div>
112 <div class="label">
112 <div class="label">
113 <span class="help-block">${_('Filesystem location where Mercurial largefile objects should be stored.')}</span>
113 <span class="help-block">${_('Filesystem location where Mercurial largefile objects should be stored.')}</span>
114 </div>
114 </div>
115 % endif
115 % endif
116
116
117 <div class="checkbox">
117 <div class="checkbox">
118 ${h.checkbox('phases_publish' + suffix, 'True', **kwargs)}
118 ${h.checkbox('phases_publish' + suffix, 'True', **kwargs)}
119 <label for="phases_publish${suffix}">${_('Set repositories as publishing') if display_globals else _('Set repository as publishing')}</label>
119 <label for="phases_publish${suffix}">${_('Set repositories as publishing') if display_globals else _('Set repository as publishing')}</label>
120 </div>
120 </div>
121 <div class="label">
121 <div class="label">
122 <span class="help-block">${_('When this is enabled all commits in the repository are seen as public commits by clients.')}</span>
122 <span class="help-block">${_('When this is enabled all commits in the repository are seen as public commits by clients.')}</span>
123 </div>
123 </div>
124 % if display_globals:
124 % if display_globals:
125 <div class="checkbox">
125 <div class="checkbox">
126 ${h.checkbox('extensions_hgsubversion' + suffix,'True')}
126 ${h.checkbox('extensions_hgsubversion' + suffix,'True')}
127 <label for="extensions_hgsubversion${suffix}">${_('Enable hgsubversion extension')}</label>
127 <label for="extensions_hgsubversion${suffix}">${_('Enable hgsubversion extension')}</label>
128 </div>
128 </div>
129 <div class="label">
129 <div class="label">
130 <span class="help-block">${_('Requires hgsubversion library to be installed. Allows cloning remote SVN repositories and migrates them to Mercurial type.')}</span>
130 <span class="help-block">${_('Requires hgsubversion library to be installed. Allows cloning remote SVN repositories and migrates them to Mercurial type.')}</span>
131 </div>
131 </div>
132 % endif
132 % endif
133 </div>
133 </div>
134 </div>
134 </div>
135 ## LABS for HG
135 ## LABS for HG
136 % if c.labs_active:
136 % if c.labs_active:
137 <div class="panel panel-danger">
137 <div class="panel panel-danger">
138 <div class="panel-heading">
138 <div class="panel-heading">
139 <h3 class="panel-title">${_('Mercurial Labs Settings')} (${_('These features are considered experimental and may not work as expected.')})</h3>
139 <h3 class="panel-title">${_('Mercurial Labs Settings')} (${_('These features are considered experimental and may not work as expected.')})</h3>
140 </div>
140 </div>
141 <div class="panel-body">
141 <div class="panel-body">
142
142
143 <div class="checkbox">
143 <div class="checkbox">
144 ${h.checkbox('rhodecode_hg_use_rebase_for_merging' + suffix, 'True', **kwargs)}
144 ${h.checkbox('rhodecode_hg_use_rebase_for_merging' + suffix, 'True', **kwargs)}
145 <label for="rhodecode_hg_use_rebase_for_merging${suffix}">${_('Use rebase as merge strategy')}</label>
145 <label for="rhodecode_hg_use_rebase_for_merging${suffix}">${_('Use rebase as merge strategy')}</label>
146 </div>
146 </div>
147 <div class="label">
147 <div class="label">
148 <span class="help-block">${_('Use rebase instead of creating a merge commit when merging via web interface.')}</span>
148 <span class="help-block">${_('Use rebase instead of creating a merge commit when merging via web interface.')}</span>
149 </div>
149 </div>
150
150
151 </div>
151 </div>
152 </div>
152 </div>
153 % endif
153 % endif
154
154
155 % endif
155 % endif
156
156
157 % if display_globals or repo_type in ['git']:
157 % if display_globals or repo_type in ['git']:
158 <div class="panel panel-default">
158 <div class="panel panel-default">
159 <div class="panel-heading" id="vcs-git-options">
159 <div class="panel-heading" id="vcs-git-options">
160 <h3 class="panel-title">${_('Git Settings')}<a class="permalink" href="#vcs-git-options"> ΒΆ</a></h3>
160 <h3 class="panel-title">${_('Git Settings')}<a class="permalink" href="#vcs-git-options"> ΒΆ</a></h3>
161 </div>
161 </div>
162 <div class="panel-body">
162 <div class="panel-body">
163 <div class="checkbox">
163 <div class="checkbox">
164 ${h.checkbox('vcs_git_lfs_enabled' + suffix, 'True', **kwargs)}
164 ${h.checkbox('vcs_git_lfs_enabled' + suffix, 'True', **kwargs)}
165 <label for="vcs_git_lfs_enabled${suffix}">${_('Enable lfs extension')}</label>
165 <label for="vcs_git_lfs_enabled${suffix}">${_('Enable lfs extension')}</label>
166 </div>
166 </div>
167 <div class="label">
167 <div class="label">
168 % if display_globals:
168 % if display_globals:
169 <span class="help-block">${_('Enable lfs extensions for all repositories.')}</span>
169 <span class="help-block">${_('Enable lfs extensions for all repositories.')}</span>
170 % else:
170 % else:
171 <span class="help-block">${_('Enable lfs extensions for this repository.')}</span>
171 <span class="help-block">${_('Enable lfs extensions for this repository.')}</span>
172 % endif
172 % endif
173 </div>
173 </div>
174
174
175 % if display_globals:
175 % if display_globals:
176 <div class="field">
176 <div class="field">
177 <div class="input">
177 <div class="input">
178 ${h.text('vcs_git_lfs_store_location' + suffix, size=59)}
178 ${h.text('vcs_git_lfs_store_location' + suffix, size=59)}
179 </div>
179 </div>
180 </div>
180 </div>
181 <div class="label">
181 <div class="label">
182 <span class="help-block">${_('Filesystem location where Git lfs objects should be stored.')}</span>
182 <span class="help-block">${_('Filesystem location where Git lfs objects should be stored.')}</span>
183 </div>
183 </div>
184 % endif
184 % endif
185 </div>
185 </div>
186 </div>
186 </div>
187 % endif
187 % endif
188
188
189
189
190 % if display_globals:
190 % if display_globals:
191 <div class="panel panel-default">
191 <div class="panel panel-default">
192 <div class="panel-heading" id="vcs-global-svn-options">
192 <div class="panel-heading" id="vcs-global-svn-options">
193 <h3 class="panel-title">${_('Global Subversion Settings')}<a class="permalink" href="#vcs-global-svn-options"> ΒΆ</a></h3>
193 <h3 class="panel-title">${_('Global Subversion Settings')}<a class="permalink" href="#vcs-global-svn-options"> ΒΆ</a></h3>
194 </div>
194 </div>
195 <div class="panel-body">
195 <div class="panel-body">
196 <div class="field">
196 <div class="field">
197 <div class="checkbox">
197 <div class="checkbox">
198 ${h.checkbox('vcs_svn_proxy_http_requests_enabled' + suffix, 'True', **kwargs)}
198 ${h.checkbox('vcs_svn_proxy_http_requests_enabled' + suffix, 'True', **kwargs)}
199 <label for="vcs_svn_proxy_http_requests_enabled${suffix}">${_('Proxy subversion HTTP requests')}</label>
199 <label for="vcs_svn_proxy_http_requests_enabled${suffix}">${_('Proxy subversion HTTP requests')}</label>
200 </div>
200 </div>
201 <div class="label">
201 <div class="label">
202 <span class="help-block">
202 <span class="help-block">
203 ${_('Subversion HTTP Support. Enables communication with SVN over HTTP protocol.')}
203 ${_('Subversion HTTP Support. Enables communication with SVN over HTTP protocol.')}
204 <a href="${h.url('enterprise_svn_setup')}" target="_blank">${_('SVN Protocol setup Documentation')}</a>.
204 <a href="${h.route_url('enterprise_svn_setup')}" target="_blank">${_('SVN Protocol setup Documentation')}</a>.
205 </span>
205 </span>
206 </div>
206 </div>
207 </div>
207 </div>
208 <div class="field">
208 <div class="field">
209 <div class="label">
209 <div class="label">
210 <label for="vcs_svn_proxy_http_server_url">${_('Subversion HTTP Server URL')}</label><br/>
210 <label for="vcs_svn_proxy_http_server_url">${_('Subversion HTTP Server URL')}</label><br/>
211 </div>
211 </div>
212 <div class="input">
212 <div class="input">
213 ${h.text('vcs_svn_proxy_http_server_url',size=59)}
213 ${h.text('vcs_svn_proxy_http_server_url',size=59)}
214 % if c.svn_proxy_generate_config:
214 % if c.svn_proxy_generate_config:
215 <span class="buttons">
215 <span class="buttons">
216 <button class="btn btn-primary" id="vcs_svn_generate_cfg">${_('Generate Apache Config')}</button>
216 <button class="btn btn-primary" id="vcs_svn_generate_cfg">${_('Generate Apache Config')}</button>
217 </span>
217 </span>
218 % endif
218 % endif
219 </div>
219 </div>
220 </div>
220 </div>
221 </div>
221 </div>
222 </div>
222 </div>
223 % endif
223 % endif
224
224
225 % if display_globals or repo_type in ['svn']:
225 % if display_globals or repo_type in ['svn']:
226 <div class="panel panel-default">
226 <div class="panel panel-default">
227 <div class="panel-heading" id="vcs-svn-options">
227 <div class="panel-heading" id="vcs-svn-options">
228 <h3 class="panel-title">${_('Subversion Settings')}<a class="permalink" href="#vcs-svn-options"> ΒΆ</a></h3>
228 <h3 class="panel-title">${_('Subversion Settings')}<a class="permalink" href="#vcs-svn-options"> ΒΆ</a></h3>
229 </div>
229 </div>
230 <div class="panel-body">
230 <div class="panel-body">
231 <div class="field">
231 <div class="field">
232 <div class="content" >
232 <div class="content" >
233 <label>${_('Repository patterns')}</label><br/>
233 <label>${_('Repository patterns')}</label><br/>
234 </div>
234 </div>
235 </div>
235 </div>
236 <div class="label">
236 <div class="label">
237 <span class="help-block">${_('Patterns for identifying SVN branches and tags. For recursive search, use "*". Eg.: "/branches/*"')}</span>
237 <span class="help-block">${_('Patterns for identifying SVN branches and tags. For recursive search, use "*". Eg.: "/branches/*"')}</span>
238 </div>
238 </div>
239
239
240 <div class="field branch_patterns">
240 <div class="field branch_patterns">
241 <div class="input" >
241 <div class="input" >
242 <label>${_('Branches')}:</label><br/>
242 <label>${_('Branches')}:</label><br/>
243 </div>
243 </div>
244 % if svn_branch_patterns:
244 % if svn_branch_patterns:
245 % for branch in svn_branch_patterns:
245 % for branch in svn_branch_patterns:
246 <div class="input adjacent" id="${'id%s' % branch.ui_id}">
246 <div class="input adjacent" id="${'id%s' % branch.ui_id}">
247 ${h.hidden('branch_ui_key' + suffix, branch.ui_key)}
247 ${h.hidden('branch_ui_key' + suffix, branch.ui_key)}
248 ${h.text('branch_value_%d' % branch.ui_id + suffix, branch.ui_value, size=59, readonly="readonly", class_='disabled')}
248 ${h.text('branch_value_%d' % branch.ui_id + suffix, branch.ui_value, size=59, readonly="readonly", class_='disabled')}
249 % if kwargs.get('disabled') != 'disabled':
249 % if kwargs.get('disabled') != 'disabled':
250 <span class="btn btn-x" onclick="ajaxDeletePattern(${branch.ui_id},'${'id%s' % branch.ui_id}')">
250 <span class="btn btn-x" onclick="ajaxDeletePattern(${branch.ui_id},'${'id%s' % branch.ui_id}')">
251 ${_('Delete')}
251 ${_('Delete')}
252 </span>
252 </span>
253 % endif
253 % endif
254 </div>
254 </div>
255 % endfor
255 % endfor
256 %endif
256 %endif
257 </div>
257 </div>
258 % if kwargs.get('disabled') != 'disabled':
258 % if kwargs.get('disabled') != 'disabled':
259 <div class="field branch_patterns">
259 <div class="field branch_patterns">
260 <div class="input" >
260 <div class="input" >
261 ${h.text('new_svn_branch',size=59,placeholder='New branch pattern')}
261 ${h.text('new_svn_branch',size=59,placeholder='New branch pattern')}
262 </div>
262 </div>
263 </div>
263 </div>
264 % endif
264 % endif
265 <div class="field tag_patterns">
265 <div class="field tag_patterns">
266 <div class="input" >
266 <div class="input" >
267 <label>${_('Tags')}:</label><br/>
267 <label>${_('Tags')}:</label><br/>
268 </div>
268 </div>
269 % if svn_tag_patterns:
269 % if svn_tag_patterns:
270 % for tag in svn_tag_patterns:
270 % for tag in svn_tag_patterns:
271 <div class="input" id="${'id%s' % tag.ui_id + suffix}">
271 <div class="input" id="${'id%s' % tag.ui_id + suffix}">
272 ${h.hidden('tag_ui_key' + suffix, tag.ui_key)}
272 ${h.hidden('tag_ui_key' + suffix, tag.ui_key)}
273 ${h.text('tag_ui_value_new_%d' % tag.ui_id + suffix, tag.ui_value, size=59, readonly="readonly", class_='disabled tag_input')}
273 ${h.text('tag_ui_value_new_%d' % tag.ui_id + suffix, tag.ui_value, size=59, readonly="readonly", class_='disabled tag_input')}
274 % if kwargs.get('disabled') != 'disabled':
274 % if kwargs.get('disabled') != 'disabled':
275 <span class="btn btn-x" onclick="ajaxDeletePattern(${tag.ui_id},'${'id%s' % tag.ui_id}')">
275 <span class="btn btn-x" onclick="ajaxDeletePattern(${tag.ui_id},'${'id%s' % tag.ui_id}')">
276 ${_('Delete')}
276 ${_('Delete')}
277 </span>
277 </span>
278 %endif
278 %endif
279 </div>
279 </div>
280 % endfor
280 % endfor
281 % endif
281 % endif
282 </div>
282 </div>
283 % if kwargs.get('disabled') != 'disabled':
283 % if kwargs.get('disabled') != 'disabled':
284 <div class="field tag_patterns">
284 <div class="field tag_patterns">
285 <div class="input" >
285 <div class="input" >
286 ${h.text('new_svn_tag' + suffix, size=59, placeholder='New tag pattern')}
286 ${h.text('new_svn_tag' + suffix, size=59, placeholder='New tag pattern')}
287 </div>
287 </div>
288 </div>
288 </div>
289 %endif
289 %endif
290 </div>
290 </div>
291 </div>
291 </div>
292 % else:
292 % else:
293 ${h.hidden('new_svn_branch' + suffix, '')}
293 ${h.hidden('new_svn_branch' + suffix, '')}
294 ${h.hidden('new_svn_tag' + suffix, '')}
294 ${h.hidden('new_svn_tag' + suffix, '')}
295 % endif
295 % endif
296
296
297
297
298 % if display_globals or repo_type in ['hg', 'git']:
298 % if display_globals or repo_type in ['hg', 'git']:
299 <div class="panel panel-default">
299 <div class="panel panel-default">
300 <div class="panel-heading" id="vcs-pull-requests-options">
300 <div class="panel-heading" id="vcs-pull-requests-options">
301 <h3 class="panel-title">${_('Pull Request Settings')}<a class="permalink" href="#vcs-pull-requests-options"> ΒΆ</a></h3>
301 <h3 class="panel-title">${_('Pull Request Settings')}<a class="permalink" href="#vcs-pull-requests-options"> ΒΆ</a></h3>
302 </div>
302 </div>
303 <div class="panel-body">
303 <div class="panel-body">
304 <div class="checkbox">
304 <div class="checkbox">
305 ${h.checkbox('rhodecode_pr_merge_enabled' + suffix, 'True', **kwargs)}
305 ${h.checkbox('rhodecode_pr_merge_enabled' + suffix, 'True', **kwargs)}
306 <label for="rhodecode_pr_merge_enabled${suffix}">${_('Enable server-side merge for pull requests')}</label>
306 <label for="rhodecode_pr_merge_enabled${suffix}">${_('Enable server-side merge for pull requests')}</label>
307 </div>
307 </div>
308 <div class="label">
308 <div class="label">
309 <span class="help-block">${_('Note: when this feature is enabled, it only runs hooks defined in the rcextension package. Custom hooks added on the Admin -> Settings -> Hooks page will not be run when pull requests are automatically merged from the web interface.')}</span>
309 <span class="help-block">${_('Note: when this feature is enabled, it only runs hooks defined in the rcextension package. Custom hooks added on the Admin -> Settings -> Hooks page will not be run when pull requests are automatically merged from the web interface.')}</span>
310 </div>
310 </div>
311 <div class="checkbox">
311 <div class="checkbox">
312 ${h.checkbox('rhodecode_use_outdated_comments' + suffix, 'True', **kwargs)}
312 ${h.checkbox('rhodecode_use_outdated_comments' + suffix, 'True', **kwargs)}
313 <label for="rhodecode_use_outdated_comments${suffix}">${_('Invalidate and relocate inline comments during update')}</label>
313 <label for="rhodecode_use_outdated_comments${suffix}">${_('Invalidate and relocate inline comments during update')}</label>
314 </div>
314 </div>
315 <div class="label">
315 <div class="label">
316 <span class="help-block">${_('During the update of a pull request, the position of inline comments will be updated and outdated inline comments will be hidden.')}</span>
316 <span class="help-block">${_('During the update of a pull request, the position of inline comments will be updated and outdated inline comments will be hidden.')}</span>
317 </div>
317 </div>
318 </div>
318 </div>
319 </div>
319 </div>
320 % endif
320 % endif
321
321
322 </%def>
322 </%def>
@@ -1,405 +1,405 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 ## usage:
2 ## usage:
3 ## <%namespace name="comment" file="/changeset/changeset_file_comment.mako"/>
3 ## <%namespace name="comment" file="/changeset/changeset_file_comment.mako"/>
4 ## ${comment.comment_block(comment)}
4 ## ${comment.comment_block(comment)}
5 ##
5 ##
6 <%namespace name="base" file="/base/base.mako"/>
6 <%namespace name="base" file="/base/base.mako"/>
7
7
8 <%def name="comment_block(comment, inline=False)">
8 <%def name="comment_block(comment, inline=False)">
9 <% pr_index_ver = comment.get_index_version(getattr(c, 'versions', [])) %>
9 <% pr_index_ver = comment.get_index_version(getattr(c, 'versions', [])) %>
10 % if inline:
10 % if inline:
11 <% outdated_at_ver = comment.outdated_at_version(getattr(c, 'at_version_num', None)) %>
11 <% outdated_at_ver = comment.outdated_at_version(getattr(c, 'at_version_num', None)) %>
12 % else:
12 % else:
13 <% outdated_at_ver = comment.older_than_version(getattr(c, 'at_version_num', None)) %>
13 <% outdated_at_ver = comment.older_than_version(getattr(c, 'at_version_num', None)) %>
14 % endif
14 % endif
15
15
16
16
17 <div class="comment
17 <div class="comment
18 ${'comment-inline' if inline else 'comment-general'}
18 ${'comment-inline' if inline else 'comment-general'}
19 ${'comment-outdated' if outdated_at_ver else 'comment-current'}"
19 ${'comment-outdated' if outdated_at_ver else 'comment-current'}"
20 id="comment-${comment.comment_id}"
20 id="comment-${comment.comment_id}"
21 line="${comment.line_no}"
21 line="${comment.line_no}"
22 data-comment-id="${comment.comment_id}"
22 data-comment-id="${comment.comment_id}"
23 data-comment-type="${comment.comment_type}"
23 data-comment-type="${comment.comment_type}"
24 data-comment-inline=${h.json.dumps(inline)}
24 data-comment-inline=${h.json.dumps(inline)}
25 style="${'display: none;' if outdated_at_ver else ''}">
25 style="${'display: none;' if outdated_at_ver else ''}">
26
26
27 <div class="meta">
27 <div class="meta">
28 <div class="comment-type-label">
28 <div class="comment-type-label">
29 <div class="comment-label ${comment.comment_type or 'note'}" id="comment-label-${comment.comment_id}">
29 <div class="comment-label ${comment.comment_type or 'note'}" id="comment-label-${comment.comment_id}">
30 % if comment.comment_type == 'todo':
30 % if comment.comment_type == 'todo':
31 % if comment.resolved:
31 % if comment.resolved:
32 <div class="resolved tooltip" title="${_('Resolved by comment #{}').format(comment.resolved.comment_id)}">
32 <div class="resolved tooltip" title="${_('Resolved by comment #{}').format(comment.resolved.comment_id)}">
33 <a href="#comment-${comment.resolved.comment_id}">${comment.comment_type}</a>
33 <a href="#comment-${comment.resolved.comment_id}">${comment.comment_type}</a>
34 </div>
34 </div>
35 % else:
35 % else:
36 <div class="resolved tooltip" style="display: none">
36 <div class="resolved tooltip" style="display: none">
37 <span>${comment.comment_type}</span>
37 <span>${comment.comment_type}</span>
38 </div>
38 </div>
39 <div class="resolve tooltip" onclick="return Rhodecode.comments.createResolutionComment(${comment.comment_id});" title="${_('Click to resolve this comment')}">
39 <div class="resolve tooltip" onclick="return Rhodecode.comments.createResolutionComment(${comment.comment_id});" title="${_('Click to resolve this comment')}">
40 ${comment.comment_type}
40 ${comment.comment_type}
41 </div>
41 </div>
42 % endif
42 % endif
43 % else:
43 % else:
44 % if comment.resolved_comment:
44 % if comment.resolved_comment:
45 fix
45 fix
46 % else:
46 % else:
47 ${comment.comment_type or 'note'}
47 ${comment.comment_type or 'note'}
48 % endif
48 % endif
49 % endif
49 % endif
50 </div>
50 </div>
51 </div>
51 </div>
52
52
53 <div class="author ${'author-inline' if inline else 'author-general'}">
53 <div class="author ${'author-inline' if inline else 'author-general'}">
54 ${base.gravatar_with_user(comment.author.email, 16)}
54 ${base.gravatar_with_user(comment.author.email, 16)}
55 </div>
55 </div>
56 <div class="date">
56 <div class="date">
57 ${h.age_component(comment.modified_at, time_is_local=True)}
57 ${h.age_component(comment.modified_at, time_is_local=True)}
58 </div>
58 </div>
59 % if inline:
59 % if inline:
60 <span></span>
60 <span></span>
61 % else:
61 % else:
62 <div class="status-change">
62 <div class="status-change">
63 % if comment.pull_request:
63 % if comment.pull_request:
64 <a href="${h.url('pullrequest_show',repo_name=comment.pull_request.target_repo.repo_name,pull_request_id=comment.pull_request.pull_request_id)}">
64 <a href="${h.url('pullrequest_show',repo_name=comment.pull_request.target_repo.repo_name,pull_request_id=comment.pull_request.pull_request_id)}">
65 % if comment.status_change:
65 % if comment.status_change:
66 ${_('pull request #%s') % comment.pull_request.pull_request_id}:
66 ${_('pull request #%s') % comment.pull_request.pull_request_id}:
67 % else:
67 % else:
68 ${_('pull request #%s') % comment.pull_request.pull_request_id}
68 ${_('pull request #%s') % comment.pull_request.pull_request_id}
69 % endif
69 % endif
70 </a>
70 </a>
71 % else:
71 % else:
72 % if comment.status_change:
72 % if comment.status_change:
73 ${_('Status change on commit')}:
73 ${_('Status change on commit')}:
74 % endif
74 % endif
75 % endif
75 % endif
76 </div>
76 </div>
77 % endif
77 % endif
78
78
79 % if comment.status_change:
79 % if comment.status_change:
80 <div class="${'flag_status %s' % comment.status_change[0].status}"></div>
80 <div class="${'flag_status %s' % comment.status_change[0].status}"></div>
81 <div title="${_('Commit status')}" class="changeset-status-lbl">
81 <div title="${_('Commit status')}" class="changeset-status-lbl">
82 ${comment.status_change[0].status_lbl}
82 ${comment.status_change[0].status_lbl}
83 </div>
83 </div>
84 % endif
84 % endif
85
85
86 % if comment.resolved_comment:
86 % if comment.resolved_comment:
87 <a class="has-spacer-before" href="#comment-${comment.resolved_comment.comment_id}" onclick="Rhodecode.comments.scrollToComment($('#comment-${comment.resolved_comment.comment_id}'), 0, ${h.json.dumps(comment.resolved_comment.outdated)})">
87 <a class="has-spacer-before" href="#comment-${comment.resolved_comment.comment_id}" onclick="Rhodecode.comments.scrollToComment($('#comment-${comment.resolved_comment.comment_id}'), 0, ${h.json.dumps(comment.resolved_comment.outdated)})">
88 ${_('resolves comment #{}').format(comment.resolved_comment.comment_id)}
88 ${_('resolves comment #{}').format(comment.resolved_comment.comment_id)}
89 </a>
89 </a>
90 % endif
90 % endif
91
91
92 <a class="permalink" href="#comment-${comment.comment_id}"> &para;</a>
92 <a class="permalink" href="#comment-${comment.comment_id}"> &para;</a>
93
93
94 <div class="comment-links-block">
94 <div class="comment-links-block">
95 % if comment.pull_request and comment.pull_request.author.user_id == comment.author.user_id:
95 % if comment.pull_request and comment.pull_request.author.user_id == comment.author.user_id:
96 <span class="tag authortag tooltip" title="${_('Pull request author')}">
96 <span class="tag authortag tooltip" title="${_('Pull request author')}">
97 ${_('author')}
97 ${_('author')}
98 </span>
98 </span>
99 |
99 |
100 % endif
100 % endif
101 % if inline:
101 % if inline:
102 <div class="pr-version-inline">
102 <div class="pr-version-inline">
103 <a href="${h.url.current(version=comment.pull_request_version_id, anchor='comment-{}'.format(comment.comment_id))}">
103 <a href="${h.url.current(version=comment.pull_request_version_id, anchor='comment-{}'.format(comment.comment_id))}">
104 % if outdated_at_ver:
104 % if outdated_at_ver:
105 <code class="pr-version-num" title="${_('Outdated comment from pull request version {0}').format(pr_index_ver)}">
105 <code class="pr-version-num" title="${_('Outdated comment from pull request version {0}').format(pr_index_ver)}">
106 outdated ${'v{}'.format(pr_index_ver)} |
106 outdated ${'v{}'.format(pr_index_ver)} |
107 </code>
107 </code>
108 % elif pr_index_ver:
108 % elif pr_index_ver:
109 <code class="pr-version-num" title="${_('Comment from pull request version {0}').format(pr_index_ver)}">
109 <code class="pr-version-num" title="${_('Comment from pull request version {0}').format(pr_index_ver)}">
110 ${'v{}'.format(pr_index_ver)} |
110 ${'v{}'.format(pr_index_ver)} |
111 </code>
111 </code>
112 % endif
112 % endif
113 </a>
113 </a>
114 </div>
114 </div>
115 % else:
115 % else:
116 % if comment.pull_request_version_id and pr_index_ver:
116 % if comment.pull_request_version_id and pr_index_ver:
117 |
117 |
118 <div class="pr-version">
118 <div class="pr-version">
119 % if comment.outdated:
119 % if comment.outdated:
120 <a href="?version=${comment.pull_request_version_id}#comment-${comment.comment_id}">
120 <a href="?version=${comment.pull_request_version_id}#comment-${comment.comment_id}">
121 ${_('Outdated comment from pull request version {}').format(pr_index_ver)}
121 ${_('Outdated comment from pull request version {}').format(pr_index_ver)}
122 </a>
122 </a>
123 % else:
123 % else:
124 <div title="${_('Comment from pull request version {0}').format(pr_index_ver)}">
124 <div title="${_('Comment from pull request version {0}').format(pr_index_ver)}">
125 <a href="${h.url('pullrequest_show',repo_name=comment.pull_request.target_repo.repo_name,pull_request_id=comment.pull_request.pull_request_id, version=comment.pull_request_version_id)}">
125 <a href="${h.url('pullrequest_show',repo_name=comment.pull_request.target_repo.repo_name,pull_request_id=comment.pull_request.pull_request_id, version=comment.pull_request_version_id)}">
126 <code class="pr-version-num">
126 <code class="pr-version-num">
127 ${'v{}'.format(pr_index_ver)}
127 ${'v{}'.format(pr_index_ver)}
128 </code>
128 </code>
129 </a>
129 </a>
130 </div>
130 </div>
131 % endif
131 % endif
132 </div>
132 </div>
133 % endif
133 % endif
134 % endif
134 % endif
135
135
136 ## show delete comment if it's not a PR (regular comments) or it's PR that is not closed
136 ## show delete comment if it's not a PR (regular comments) or it's PR that is not closed
137 ## only super-admin, repo admin OR comment owner can delete, also hide delete if currently viewed comment is outdated
137 ## only super-admin, repo admin OR comment owner can delete, also hide delete if currently viewed comment is outdated
138 %if not outdated_at_ver and (not comment.pull_request or (comment.pull_request and not comment.pull_request.is_closed())):
138 %if not outdated_at_ver and (not comment.pull_request or (comment.pull_request and not comment.pull_request.is_closed())):
139 ## permissions to delete
139 ## permissions to delete
140 %if h.HasPermissionAny('hg.admin')() or h.HasRepoPermissionAny('repository.admin')(c.repo_name) or comment.author.user_id == c.rhodecode_user.user_id:
140 %if h.HasPermissionAny('hg.admin')() or h.HasRepoPermissionAny('repository.admin')(c.repo_name) or comment.author.user_id == c.rhodecode_user.user_id:
141 ## TODO: dan: add edit comment here
141 ## TODO: dan: add edit comment here
142 <a onclick="return Rhodecode.comments.deleteComment(this);" class="delete-comment"> ${_('Delete')}</a>
142 <a onclick="return Rhodecode.comments.deleteComment(this);" class="delete-comment"> ${_('Delete')}</a>
143 %else:
143 %else:
144 <button class="btn-link" disabled="disabled"> ${_('Delete')}</button>
144 <button class="btn-link" disabled="disabled"> ${_('Delete')}</button>
145 %endif
145 %endif
146 %else:
146 %else:
147 <button class="btn-link" disabled="disabled"> ${_('Delete')}</button>
147 <button class="btn-link" disabled="disabled"> ${_('Delete')}</button>
148 %endif
148 %endif
149
149
150 % if outdated_at_ver:
150 % if outdated_at_ver:
151 | <a onclick="return Rhodecode.comments.prevOutdatedComment(this);" class="prev-comment"> ${_('Prev')}</a>
151 | <a onclick="return Rhodecode.comments.prevOutdatedComment(this);" class="prev-comment"> ${_('Prev')}</a>
152 | <a onclick="return Rhodecode.comments.nextOutdatedComment(this);" class="next-comment"> ${_('Next')}</a>
152 | <a onclick="return Rhodecode.comments.nextOutdatedComment(this);" class="next-comment"> ${_('Next')}</a>
153 % else:
153 % else:
154 | <a onclick="return Rhodecode.comments.prevComment(this);" class="prev-comment"> ${_('Prev')}</a>
154 | <a onclick="return Rhodecode.comments.prevComment(this);" class="prev-comment"> ${_('Prev')}</a>
155 | <a onclick="return Rhodecode.comments.nextComment(this);" class="next-comment"> ${_('Next')}</a>
155 | <a onclick="return Rhodecode.comments.nextComment(this);" class="next-comment"> ${_('Next')}</a>
156 % endif
156 % endif
157
157
158 </div>
158 </div>
159 </div>
159 </div>
160 <div class="text">
160 <div class="text">
161 ${h.render(comment.text, renderer=comment.renderer, mentions=True)}
161 ${h.render(comment.text, renderer=comment.renderer, mentions=True)}
162 </div>
162 </div>
163
163
164 </div>
164 </div>
165 </%def>
165 </%def>
166
166
167 ## generate main comments
167 ## generate main comments
168 <%def name="generate_comments(comments, include_pull_request=False, is_pull_request=False)">
168 <%def name="generate_comments(comments, include_pull_request=False, is_pull_request=False)">
169 <div class="general-comments" id="comments">
169 <div class="general-comments" id="comments">
170 %for comment in comments:
170 %for comment in comments:
171 <div id="comment-tr-${comment.comment_id}">
171 <div id="comment-tr-${comment.comment_id}">
172 ## only render comments that are not from pull request, or from
172 ## only render comments that are not from pull request, or from
173 ## pull request and a status change
173 ## pull request and a status change
174 %if not comment.pull_request or (comment.pull_request and comment.status_change) or include_pull_request:
174 %if not comment.pull_request or (comment.pull_request and comment.status_change) or include_pull_request:
175 ${comment_block(comment)}
175 ${comment_block(comment)}
176 %endif
176 %endif
177 </div>
177 </div>
178 %endfor
178 %endfor
179 ## to anchor ajax comments
179 ## to anchor ajax comments
180 <div id="injected_page_comments"></div>
180 <div id="injected_page_comments"></div>
181 </div>
181 </div>
182 </%def>
182 </%def>
183
183
184
184
185 <%def name="comments(post_url, cur_status, is_pull_request=False, is_compare=False, change_status=True, form_extras=None)">
185 <%def name="comments(post_url, cur_status, is_pull_request=False, is_compare=False, change_status=True, form_extras=None)">
186
186
187 <div class="comments">
187 <div class="comments">
188 <%
188 <%
189 if is_pull_request:
189 if is_pull_request:
190 placeholder = _('Leave a comment on this Pull Request.')
190 placeholder = _('Leave a comment on this Pull Request.')
191 elif is_compare:
191 elif is_compare:
192 placeholder = _('Leave a comment on {} commits in this range.').format(len(form_extras))
192 placeholder = _('Leave a comment on {} commits in this range.').format(len(form_extras))
193 else:
193 else:
194 placeholder = _('Leave a comment on this Commit.')
194 placeholder = _('Leave a comment on this Commit.')
195 %>
195 %>
196
196
197 % if c.rhodecode_user.username != h.DEFAULT_USER:
197 % if c.rhodecode_user.username != h.DEFAULT_USER:
198 <div class="js-template" id="cb-comment-general-form-template">
198 <div class="js-template" id="cb-comment-general-form-template">
199 ## template generated for injection
199 ## template generated for injection
200 ${comment_form(form_type='general', review_statuses=c.commit_statuses, form_extras=form_extras)}
200 ${comment_form(form_type='general', review_statuses=c.commit_statuses, form_extras=form_extras)}
201 </div>
201 </div>
202
202
203 <div id="cb-comment-general-form-placeholder" class="comment-form ac">
203 <div id="cb-comment-general-form-placeholder" class="comment-form ac">
204 ## inject form here
204 ## inject form here
205 </div>
205 </div>
206 <script type="text/javascript">
206 <script type="text/javascript">
207 var lineNo = 'general';
207 var lineNo = 'general';
208 var resolvesCommentId = null;
208 var resolvesCommentId = null;
209 var generalCommentForm = Rhodecode.comments.createGeneralComment(
209 var generalCommentForm = Rhodecode.comments.createGeneralComment(
210 lineNo, "${placeholder}", resolvesCommentId);
210 lineNo, "${placeholder}", resolvesCommentId);
211
211
212 // set custom success callback on rangeCommit
212 // set custom success callback on rangeCommit
213 % if is_compare:
213 % if is_compare:
214 generalCommentForm.setHandleFormSubmit(function(o) {
214 generalCommentForm.setHandleFormSubmit(function(o) {
215 var self = generalCommentForm;
215 var self = generalCommentForm;
216
216
217 var text = self.cm.getValue();
217 var text = self.cm.getValue();
218 var status = self.getCommentStatus();
218 var status = self.getCommentStatus();
219 var commentType = self.getCommentType();
219 var commentType = self.getCommentType();
220
220
221 if (text === "" && !status) {
221 if (text === "" && !status) {
222 return;
222 return;
223 }
223 }
224
224
225 // we can pick which commits we want to make the comment by
225 // we can pick which commits we want to make the comment by
226 // selecting them via click on preview pane, this will alter the hidden inputs
226 // selecting them via click on preview pane, this will alter the hidden inputs
227 var cherryPicked = $('#changeset_compare_view_content .compare_select.hl').length > 0;
227 var cherryPicked = $('#changeset_compare_view_content .compare_select.hl').length > 0;
228
228
229 var commitIds = [];
229 var commitIds = [];
230 $('#changeset_compare_view_content .compare_select').each(function(el) {
230 $('#changeset_compare_view_content .compare_select').each(function(el) {
231 var commitId = this.id.replace('row-', '');
231 var commitId = this.id.replace('row-', '');
232 if ($(this).hasClass('hl') || !cherryPicked) {
232 if ($(this).hasClass('hl') || !cherryPicked) {
233 $("input[data-commit-id='{0}']".format(commitId)).val(commitId);
233 $("input[data-commit-id='{0}']".format(commitId)).val(commitId);
234 commitIds.push(commitId);
234 commitIds.push(commitId);
235 } else {
235 } else {
236 $("input[data-commit-id='{0}']".format(commitId)).val('')
236 $("input[data-commit-id='{0}']".format(commitId)).val('')
237 }
237 }
238 });
238 });
239
239
240 self.setActionButtonsDisabled(true);
240 self.setActionButtonsDisabled(true);
241 self.cm.setOption("readOnly", true);
241 self.cm.setOption("readOnly", true);
242 var postData = {
242 var postData = {
243 'text': text,
243 'text': text,
244 'changeset_status': status,
244 'changeset_status': status,
245 'comment_type': commentType,
245 'comment_type': commentType,
246 'commit_ids': commitIds,
246 'commit_ids': commitIds,
247 'csrf_token': CSRF_TOKEN
247 'csrf_token': CSRF_TOKEN
248 };
248 };
249
249
250 var submitSuccessCallback = function(o) {
250 var submitSuccessCallback = function(o) {
251 location.reload(true);
251 location.reload(true);
252 };
252 };
253 var submitFailCallback = function(){
253 var submitFailCallback = function(){
254 self.resetCommentFormState(text)
254 self.resetCommentFormState(text)
255 };
255 };
256 self.submitAjaxPOST(
256 self.submitAjaxPOST(
257 self.submitUrl, postData, submitSuccessCallback, submitFailCallback);
257 self.submitUrl, postData, submitSuccessCallback, submitFailCallback);
258 });
258 });
259 % endif
259 % endif
260
260
261
261
262 </script>
262 </script>
263 % else:
263 % else:
264 ## form state when not logged in
264 ## form state when not logged in
265 <div class="comment-form ac">
265 <div class="comment-form ac">
266
266
267 <div class="comment-area">
267 <div class="comment-area">
268 <div class="comment-area-header">
268 <div class="comment-area-header">
269 <ul class="nav-links clearfix">
269 <ul class="nav-links clearfix">
270 <li class="active">
270 <li class="active">
271 <a class="disabled" href="#edit-btn" disabled="disabled" onclick="return false">${_('Write')}</a>
271 <a class="disabled" href="#edit-btn" disabled="disabled" onclick="return false">${_('Write')}</a>
272 </li>
272 </li>
273 <li class="">
273 <li class="">
274 <a class="disabled" href="#preview-btn" disabled="disabled" onclick="return false">${_('Preview')}</a>
274 <a class="disabled" href="#preview-btn" disabled="disabled" onclick="return false">${_('Preview')}</a>
275 </li>
275 </li>
276 </ul>
276 </ul>
277 </div>
277 </div>
278
278
279 <div class="comment-area-write" style="display: block;">
279 <div class="comment-area-write" style="display: block;">
280 <div id="edit-container">
280 <div id="edit-container">
281 <div style="padding: 40px 0">
281 <div style="padding: 40px 0">
282 ${_('You need to be logged in to leave comments.')}
282 ${_('You need to be logged in to leave comments.')}
283 <a href="${h.route_path('login', _query={'came_from': h.url.current()})}">${_('Login now')}</a>
283 <a href="${h.route_path('login', _query={'came_from': h.url.current()})}">${_('Login now')}</a>
284 </div>
284 </div>
285 </div>
285 </div>
286 <div id="preview-container" class="clearfix" style="display: none;">
286 <div id="preview-container" class="clearfix" style="display: none;">
287 <div id="preview-box" class="preview-box"></div>
287 <div id="preview-box" class="preview-box"></div>
288 </div>
288 </div>
289 </div>
289 </div>
290
290
291 <div class="comment-area-footer">
291 <div class="comment-area-footer">
292 <div class="toolbar">
292 <div class="toolbar">
293 <div class="toolbar-text">
293 <div class="toolbar-text">
294 </div>
294 </div>
295 </div>
295 </div>
296 </div>
296 </div>
297 </div>
297 </div>
298
298
299 <div class="comment-footer">
299 <div class="comment-footer">
300 </div>
300 </div>
301
301
302 </div>
302 </div>
303 % endif
303 % endif
304
304
305 <script type="text/javascript">
305 <script type="text/javascript">
306 bindToggleButtons();
306 bindToggleButtons();
307 </script>
307 </script>
308 </div>
308 </div>
309 </%def>
309 </%def>
310
310
311
311
312 <%def name="comment_form(form_type, form_id='', lineno_id='{1}', review_statuses=None, form_extras=None)">
312 <%def name="comment_form(form_type, form_id='', lineno_id='{1}', review_statuses=None, form_extras=None)">
313 ## comment injected based on assumption that user is logged in
313 ## comment injected based on assumption that user is logged in
314
314
315 <form ${'id="{}"'.format(form_id) if form_id else '' |n} action="#" method="GET">
315 <form ${'id="{}"'.format(form_id) if form_id else '' |n} action="#" method="GET">
316
316
317 <div class="comment-area">
317 <div class="comment-area">
318 <div class="comment-area-header">
318 <div class="comment-area-header">
319 <ul class="nav-links clearfix">
319 <ul class="nav-links clearfix">
320 <li class="active">
320 <li class="active">
321 <a href="#edit-btn" tabindex="-1" id="edit-btn_${lineno_id}">${_('Write')}</a>
321 <a href="#edit-btn" tabindex="-1" id="edit-btn_${lineno_id}">${_('Write')}</a>
322 </li>
322 </li>
323 <li class="">
323 <li class="">
324 <a href="#preview-btn" tabindex="-1" id="preview-btn_${lineno_id}">${_('Preview')}</a>
324 <a href="#preview-btn" tabindex="-1" id="preview-btn_${lineno_id}">${_('Preview')}</a>
325 </li>
325 </li>
326 <li class="pull-right">
326 <li class="pull-right">
327 <select class="comment-type" id="comment_type_${lineno_id}" name="comment_type">
327 <select class="comment-type" id="comment_type_${lineno_id}" name="comment_type">
328 % for val in c.visual.comment_types:
328 % for val in c.visual.comment_types:
329 <option value="${val}">${val.upper()}</option>
329 <option value="${val}">${val.upper()}</option>
330 % endfor
330 % endfor
331 </select>
331 </select>
332 </li>
332 </li>
333 </ul>
333 </ul>
334 </div>
334 </div>
335
335
336 <div class="comment-area-write" style="display: block;">
336 <div class="comment-area-write" style="display: block;">
337 <div id="edit-container_${lineno_id}">
337 <div id="edit-container_${lineno_id}">
338 <textarea id="text_${lineno_id}" name="text" class="comment-block-ta ac-input"></textarea>
338 <textarea id="text_${lineno_id}" name="text" class="comment-block-ta ac-input"></textarea>
339 </div>
339 </div>
340 <div id="preview-container_${lineno_id}" class="clearfix" style="display: none;">
340 <div id="preview-container_${lineno_id}" class="clearfix" style="display: none;">
341 <div id="preview-box_${lineno_id}" class="preview-box"></div>
341 <div id="preview-box_${lineno_id}" class="preview-box"></div>
342 </div>
342 </div>
343 </div>
343 </div>
344
344
345 <div class="comment-area-footer">
345 <div class="comment-area-footer">
346 <div class="toolbar">
346 <div class="toolbar">
347 <div class="toolbar-text">
347 <div class="toolbar-text">
348 ${(_('Comments parsed using %s syntax with %s, and %s actions support.') % (
348 ${(_('Comments parsed using %s syntax with %s, and %s actions support.') % (
349 ('<a href="%s">%s</a>' % (h.url('%s_help' % c.visual.default_renderer), c.visual.default_renderer.upper())),
349 ('<a href="%s">%s</a>' % (h.route_url('%s_help' % c.visual.default_renderer), c.visual.default_renderer.upper())),
350 ('<span class="tooltip" title="%s">@mention</span>' % _('Use @username inside this text to send notification to this RhodeCode user')),
350 ('<span class="tooltip" title="%s">@mention</span>' % _('Use @username inside this text to send notification to this RhodeCode user')),
351 ('<span class="tooltip" title="%s">`/`</span>' % _('Start typing with / for certain actions to be triggered via text box.'))
351 ('<span class="tooltip" title="%s">`/`</span>' % _('Start typing with / for certain actions to be triggered via text box.'))
352 )
352 )
353 )|n}
353 )|n}
354 </div>
354 </div>
355 </div>
355 </div>
356 </div>
356 </div>
357 </div>
357 </div>
358
358
359 <div class="comment-footer">
359 <div class="comment-footer">
360
360
361 % if review_statuses:
361 % if review_statuses:
362 <div class="status_box">
362 <div class="status_box">
363 <select id="change_status_${lineno_id}" name="changeset_status">
363 <select id="change_status_${lineno_id}" name="changeset_status">
364 <option></option> ## Placeholder
364 <option></option> ## Placeholder
365 % for status, lbl in review_statuses:
365 % for status, lbl in review_statuses:
366 <option value="${status}" data-status="${status}">${lbl}</option>
366 <option value="${status}" data-status="${status}">${lbl}</option>
367 %if is_pull_request and change_status and status in ('approved', 'rejected'):
367 %if is_pull_request and change_status and status in ('approved', 'rejected'):
368 <option value="${status}_closed" data-status="${status}">${lbl} & ${_('Closed')}</option>
368 <option value="${status}_closed" data-status="${status}">${lbl} & ${_('Closed')}</option>
369 %endif
369 %endif
370 % endfor
370 % endfor
371 </select>
371 </select>
372 </div>
372 </div>
373 % endif
373 % endif
374
374
375 ## inject extra inputs into the form
375 ## inject extra inputs into the form
376 % if form_extras and isinstance(form_extras, (list, tuple)):
376 % if form_extras and isinstance(form_extras, (list, tuple)):
377 <div id="comment_form_extras">
377 <div id="comment_form_extras">
378 % for form_ex_el in form_extras:
378 % for form_ex_el in form_extras:
379 ${form_ex_el|n}
379 ${form_ex_el|n}
380 % endfor
380 % endfor
381 </div>
381 </div>
382 % endif
382 % endif
383
383
384 <div class="action-buttons">
384 <div class="action-buttons">
385 ## inline for has a file, and line-number together with cancel hide button.
385 ## inline for has a file, and line-number together with cancel hide button.
386 % if form_type == 'inline':
386 % if form_type == 'inline':
387 <input type="hidden" name="f_path" value="{0}">
387 <input type="hidden" name="f_path" value="{0}">
388 <input type="hidden" name="line" value="${lineno_id}">
388 <input type="hidden" name="line" value="${lineno_id}">
389 <button type="button" class="cb-comment-cancel" onclick="return Rhodecode.comments.cancelComment(this);">
389 <button type="button" class="cb-comment-cancel" onclick="return Rhodecode.comments.cancelComment(this);">
390 ${_('Cancel')}
390 ${_('Cancel')}
391 </button>
391 </button>
392 % endif
392 % endif
393
393
394 % if form_type != 'inline':
394 % if form_type != 'inline':
395 <div class="action-buttons-extra"></div>
395 <div class="action-buttons-extra"></div>
396 % endif
396 % endif
397
397
398 ${h.submit('save', _('Comment'), class_='btn btn-success comment-button-input')}
398 ${h.submit('save', _('Comment'), class_='btn btn-success comment-button-input')}
399
399
400 </div>
400 </div>
401 </div>
401 </div>
402
402
403 </form>
403 </form>
404
404
405 </%def> No newline at end of file
405 </%def>
@@ -1,77 +1,77 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <!DOCTYPE html>
2 <!DOCTYPE html>
3 <html xmlns="http://www.w3.org/1999/xhtml">
3 <html xmlns="http://www.w3.org/1999/xhtml">
4 <head>
4 <head>
5 <title>Error - ${c.error_message}</title>
5 <title>Error - ${c.error_message}</title>
6 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
6 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
7 <meta name="robots" content="index, nofollow"/>
7 <meta name="robots" content="index, nofollow"/>
8 <link rel="icon" href="${h.asset('images/favicon.ico')}" sizes="16x16 32x32" type="image/png" />
8 <link rel="icon" href="${h.asset('images/favicon.ico')}" sizes="16x16 32x32" type="image/png" />
9
9
10 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
10 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
11 %if c.redirect_time:
11 %if c.redirect_time:
12 <meta http-equiv="refresh" content="${c.redirect_time}; url=${c.url_redirect}"/>
12 <meta http-equiv="refresh" content="${c.redirect_time}; url=${c.url_redirect}"/>
13 %endif
13 %endif
14
14
15 <link rel="stylesheet" type="text/css" href="${h.asset('css/style.css', ver=c.rhodecode_version_hash)}" media="screen"/>
15 <link rel="stylesheet" type="text/css" href="${h.asset('css/style.css', ver=c.rhodecode_version_hash)}" media="screen"/>
16 <!--[if IE]>
16 <!--[if IE]>
17 <link rel="stylesheet" type="text/css" href="${h.asset('css/ie.css')}" media="screen"/>
17 <link rel="stylesheet" type="text/css" href="${h.asset('css/ie.css')}" media="screen"/>
18 <![endif]-->
18 <![endif]-->
19 <style>body { background:#eeeeee; }</style>
19 <style>body { background:#eeeeee; }</style>
20 <script type="text/javascript">
20 <script type="text/javascript">
21 // register templateContext to pass template variables to JS
21 // register templateContext to pass template variables to JS
22 var templateContext = {timeago: {}};
22 var templateContext = {timeago: {}};
23 </script>
23 </script>
24 <script type="text/javascript" src="${h.asset('js/scripts.js', ver=c.rhodecode_version_hash)}"></script>
24 <script type="text/javascript" src="${h.asset('js/scripts.js', ver=c.rhodecode_version_hash)}"></script>
25 </head>
25 </head>
26 <body>
26 <body>
27 <% messages = h.flash.pop_messages() %>
27 <% messages = h.flash.pop_messages() %>
28
28
29 <div class="wrapper error_page">
29 <div class="wrapper error_page">
30 <div class="sidebar">
30 <div class="sidebar">
31 <a href="${h.url('home')}"><img class="error-page-logo" src="${h.asset('images/RhodeCode_Logo_Black.png')}" alt="RhodeCode"/></a>
31 <a href="${h.url('home')}"><img class="error-page-logo" src="${h.asset('images/RhodeCode_Logo_Black.png')}" alt="RhodeCode"/></a>
32 </div>
32 </div>
33 <div class="main-content">
33 <div class="main-content">
34 <h1>
34 <h1>
35 <span class="error-branding">
35 <span class="error-branding">
36 ${h.branding(c.rhodecode_name)}
36 ${h.branding(c.rhodecode_name)}
37 </span><br/>
37 </span><br/>
38 ${c.error_message} | <span class="error_message">${c.error_explanation}</span>
38 ${c.error_message} | <span class="error_message">${c.error_explanation}</span>
39 </h1>
39 </h1>
40 % if messages:
40 % if messages:
41 % for message in messages:
41 % for message in messages:
42 <div class="alert alert-${message.category}">${message}</div>
42 <div class="alert alert-${message.category}">${message}</div>
43 % endfor
43 % endfor
44 % endif
44 % endif
45 %if c.redirect_time:
45 %if c.redirect_time:
46 <p>${_('You will be redirected to %s in %s seconds') % (c.redirect_module,c.redirect_time)}</p>
46 <p>${_('You will be redirected to %s in %s seconds') % (c.redirect_module,c.redirect_time)}</p>
47 %endif
47 %endif
48 <div class="inner-column">
48 <div class="inner-column">
49 <h4>Possible Causes</h4>
49 <h4>Possible Causes</h4>
50 <ul>
50 <ul>
51 % if c.causes:
51 % if c.causes:
52 %for cause in c.causes:
52 %for cause in c.causes:
53 <li>${cause}</li>
53 <li>${cause}</li>
54 %endfor
54 %endfor
55 %else:
55 %else:
56 <li>The resource may have been deleted.</li>
56 <li>The resource may have been deleted.</li>
57 <li>You may not have access to this repository.</li>
57 <li>You may not have access to this repository.</li>
58 <li>The link may be incorrect.</li>
58 <li>The link may be incorrect.</li>
59 %endif
59 %endif
60 </ul>
60 </ul>
61 </div>
61 </div>
62 <div class="inner-column">
62 <div class="inner-column">
63 <h4>Support</h4>
63 <h4>Support</h4>
64 <p>For support, go to <a href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a>.
64 <p>For support, go to <a href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a>.
65 It may be useful to include your log file; see the log file locations <a href="${h.url('enterprise_log_file_locations')}">here</a>.
65 It may be useful to include your log file; see the log file locations <a href="${h.route_url('enterprise_log_file_locations')}">here</a>.
66 </p>
66 </p>
67 </div>
67 </div>
68 <div class="inner-column">
68 <div class="inner-column">
69 <h4>Documentation</h4>
69 <h4>Documentation</h4>
70 <p>For more information, see <a href="${h.url('enterprise_docs')}">docs.rhodecode.com</a>.</p>
70 <p>For more information, see <a href="${h.route_url('enterprise_docs')}">docs.rhodecode.com</a>.</p>
71 </div>
71 </div>
72 </div>
72 </div>
73 </div>
73 </div>
74
74
75 </body>
75 </body>
76
76
77 </html>
77 </html>
@@ -1,202 +1,202 b''
1 <%def name="refs_counters(branches, closed_branches, tags, bookmarks)">
1 <%def name="refs_counters(branches, closed_branches, tags, bookmarks)">
2 <span class="branchtag tag">
2 <span class="branchtag tag">
3 <a href="${h.url('branches_home',repo_name=c.repo_name)}" class="childs">
3 <a href="${h.url('branches_home',repo_name=c.repo_name)}" class="childs">
4 <i class="icon-branch"></i>${ungettext(
4 <i class="icon-branch"></i>${ungettext(
5 '%(num)s Branch','%(num)s Branches', len(branches)) % {'num': len(branches)}}</a>
5 '%(num)s Branch','%(num)s Branches', len(branches)) % {'num': len(branches)}}</a>
6 </span>
6 </span>
7
7
8 %if closed_branches:
8 %if closed_branches:
9 <span class="branchtag tag">
9 <span class="branchtag tag">
10 <a href="${h.url('branches_home',repo_name=c.repo_name)}" class="childs">
10 <a href="${h.url('branches_home',repo_name=c.repo_name)}" class="childs">
11 <i class="icon-branch"></i>${ungettext(
11 <i class="icon-branch"></i>${ungettext(
12 '%(num)s Closed Branch', '%(num)s Closed Branches', len(closed_branches)) % {'num': len(closed_branches)}}</a>
12 '%(num)s Closed Branch', '%(num)s Closed Branches', len(closed_branches)) % {'num': len(closed_branches)}}</a>
13 </span>
13 </span>
14 %endif
14 %endif
15
15
16 <span class="tagtag tag">
16 <span class="tagtag tag">
17 <a href="${h.url('tags_home',repo_name=c.repo_name)}" class="childs">
17 <a href="${h.url('tags_home',repo_name=c.repo_name)}" class="childs">
18 <i class="icon-tag"></i>${ungettext(
18 <i class="icon-tag"></i>${ungettext(
19 '%(num)s Tag', '%(num)s Tags', len(tags)) % {'num': len(tags)}}</a>
19 '%(num)s Tag', '%(num)s Tags', len(tags)) % {'num': len(tags)}}</a>
20 </span>
20 </span>
21
21
22 %if bookmarks:
22 %if bookmarks:
23 <span class="booktag tag">
23 <span class="booktag tag">
24 <a href="${h.url('bookmarks_home',repo_name=c.repo_name)}" class="childs">
24 <a href="${h.url('bookmarks_home',repo_name=c.repo_name)}" class="childs">
25 <i class="icon-bookmark"></i>${ungettext(
25 <i class="icon-bookmark"></i>${ungettext(
26 '%(num)s Bookmark', '%(num)s Bookmarks', len(bookmarks)) % {'num': len(bookmarks)}}</a>
26 '%(num)s Bookmark', '%(num)s Bookmarks', len(bookmarks)) % {'num': len(bookmarks)}}</a>
27 </span>
27 </span>
28 %endif
28 %endif
29 </%def>
29 </%def>
30
30
31 <%def name="summary_detail(breadcrumbs_links, show_downloads=True)">
31 <%def name="summary_detail(breadcrumbs_links, show_downloads=True)">
32 <% summary = lambda n:{False:'summary-short'}.get(n) %>
32 <% summary = lambda n:{False:'summary-short'}.get(n) %>
33
33
34 <div id="summary-menu-stats" class="summary-detail">
34 <div id="summary-menu-stats" class="summary-detail">
35 <div class="summary-detail-header">
35 <div class="summary-detail-header">
36 <div class="breadcrumbs files_location">
36 <div class="breadcrumbs files_location">
37 <h4>
37 <h4>
38 ${breadcrumbs_links}
38 ${breadcrumbs_links}
39 </h4>
39 </h4>
40 </div>
40 </div>
41 <div id="summary_details_expand" class="btn-collapse" data-toggle="summary-details">
41 <div id="summary_details_expand" class="btn-collapse" data-toggle="summary-details">
42 ${_('Show More')}
42 ${_('Show More')}
43 </div>
43 </div>
44 </div>
44 </div>
45
45
46 <div class="fieldset">
46 <div class="fieldset">
47 %if h.is_svn_without_proxy(c.rhodecode_db_repo):
47 %if h.is_svn_without_proxy(c.rhodecode_db_repo):
48 <div class="left-label disabled">
48 <div class="left-label disabled">
49 ${_('Read-only url')}:
49 ${_('Read-only url')}:
50 </div>
50 </div>
51 <div class="right-content disabled">
51 <div class="right-content disabled">
52 <input type="text" class="input-monospace" id="clone_url" disabled value="${c.clone_repo_url}"/>
52 <input type="text" class="input-monospace" id="clone_url" disabled value="${c.clone_repo_url}"/>
53 <input type="text" class="input-monospace" id="clone_url_id" disabled value="${c.clone_repo_url_id}" style="display: none;"/>
53 <input type="text" class="input-monospace" id="clone_url_id" disabled value="${c.clone_repo_url_id}" style="display: none;"/>
54 <a id="clone_by_name" class="clone" style="display: none;">${_('Show by Name')}</a>
54 <a id="clone_by_name" class="clone" style="display: none;">${_('Show by Name')}</a>
55 <a id="clone_by_id" class="clone">${_('Show by ID')}</a>
55 <a id="clone_by_id" class="clone">${_('Show by ID')}</a>
56 <p class="help-block">${_('SVN Protocol is disabled. To enable it, see the')} <a href="${h.url('enterprise_svn_setup')}" target="_blank">${_('documentation here')}</a>.</p>
56 <p class="help-block">${_('SVN Protocol is disabled. To enable it, see the')} <a href="${h.route_url('enterprise_svn_setup')}" target="_blank">${_('documentation here')}</a>.</p>
57 </div>
57 </div>
58 %else:
58 %else:
59 <div class="left-label">
59 <div class="left-label">
60 ${_('Clone url')}:
60 ${_('Clone url')}:
61 </div>
61 </div>
62 <div class="right-content">
62 <div class="right-content">
63 <input type="text" class="input-monospace" id="clone_url" readonly="readonly" value="${c.clone_repo_url}"/>
63 <input type="text" class="input-monospace" id="clone_url" readonly="readonly" value="${c.clone_repo_url}"/>
64 <input type="text" class="input-monospace" id="clone_url_id" readonly="readonly" value="${c.clone_repo_url_id}" style="display: none;"/>
64 <input type="text" class="input-monospace" id="clone_url_id" readonly="readonly" value="${c.clone_repo_url_id}" style="display: none;"/>
65 <a id="clone_by_name" class="clone" style="display: none;">${_('Show by Name')}</a>
65 <a id="clone_by_name" class="clone" style="display: none;">${_('Show by Name')}</a>
66 <a id="clone_by_id" class="clone">${_('Show by ID')}</a>
66 <a id="clone_by_id" class="clone">${_('Show by ID')}</a>
67 </div>
67 </div>
68 %endif
68 %endif
69 </div>
69 </div>
70
70
71 <div class="fieldset collapsable-content" data-toggle="summary-details" style="display: none;">
71 <div class="fieldset collapsable-content" data-toggle="summary-details" style="display: none;">
72 <div class="left-label">
72 <div class="left-label">
73 ${_('Description')}:
73 ${_('Description')}:
74 </div>
74 </div>
75 <div class="right-content">
75 <div class="right-content">
76 %if c.visual.stylify_metatags:
76 %if c.visual.stylify_metatags:
77 <div class="input ${summary(c.show_stats)} desc">${h.urlify_text(h.escaped_stylize(c.rhodecode_db_repo.description))}</div>
77 <div class="input ${summary(c.show_stats)} desc">${h.urlify_text(h.escaped_stylize(c.rhodecode_db_repo.description))}</div>
78 %else:
78 %else:
79 <div class="input ${summary(c.show_stats)} desc">${h.urlify_text(h.html_escape(c.rhodecode_db_repo.description))}</div>
79 <div class="input ${summary(c.show_stats)} desc">${h.urlify_text(h.html_escape(c.rhodecode_db_repo.description))}</div>
80 %endif
80 %endif
81 </div>
81 </div>
82 </div>
82 </div>
83
83
84 <div class="fieldset collapsable-content" data-toggle="summary-details" style="display: none;">
84 <div class="fieldset collapsable-content" data-toggle="summary-details" style="display: none;">
85 <div class="left-label">
85 <div class="left-label">
86 ${_('Information')}:
86 ${_('Information')}:
87 </div>
87 </div>
88 <div class="right-content">
88 <div class="right-content">
89
89
90 <div class="repo-size">
90 <div class="repo-size">
91 <% commit_rev = c.rhodecode_db_repo.changeset_cache.get('revision') %>
91 <% commit_rev = c.rhodecode_db_repo.changeset_cache.get('revision') %>
92
92
93 ## commits
93 ## commits
94 % if commit_rev == -1:
94 % if commit_rev == -1:
95 ${ungettext('%(num)s Commit', '%(num)s Commits', 0) % {'num': 0}},
95 ${ungettext('%(num)s Commit', '%(num)s Commits', 0) % {'num': 0}},
96 % else:
96 % else:
97 <a href="${h.url('changelog_home', repo_name=c.repo_name)}">
97 <a href="${h.url('changelog_home', repo_name=c.repo_name)}">
98 ${ungettext('%(num)s Commit', '%(num)s Commits', commit_rev) % {'num': commit_rev}}</a>,
98 ${ungettext('%(num)s Commit', '%(num)s Commits', commit_rev) % {'num': commit_rev}}</a>,
99 % endif
99 % endif
100
100
101 ## forks
101 ## forks
102 <a title="${_('Number of Repository Forks')}" href="${h.url('repo_forks_home', repo_name=c.repo_name)}">
102 <a title="${_('Number of Repository Forks')}" href="${h.url('repo_forks_home', repo_name=c.repo_name)}">
103 ${c.repository_forks} ${ungettext('Fork', 'Forks', c.repository_forks)}</a>,
103 ${c.repository_forks} ${ungettext('Fork', 'Forks', c.repository_forks)}</a>,
104
104
105 ## repo size
105 ## repo size
106 % if commit_rev == -1:
106 % if commit_rev == -1:
107 <span class="stats-bullet">0 B</span>
107 <span class="stats-bullet">0 B</span>
108 % else:
108 % else:
109 <span class="stats-bullet" id="repo_size_container">
109 <span class="stats-bullet" id="repo_size_container">
110 ${_('Calculating Repository Size...')}
110 ${_('Calculating Repository Size...')}
111 </span>
111 </span>
112 % endif
112 % endif
113 </div>
113 </div>
114
114
115 <div class="commit-info">
115 <div class="commit-info">
116 <div class="tags">
116 <div class="tags">
117 % if c.rhodecode_repo:
117 % if c.rhodecode_repo:
118 ${refs_counters(
118 ${refs_counters(
119 c.rhodecode_repo.branches,
119 c.rhodecode_repo.branches,
120 c.rhodecode_repo.branches_closed,
120 c.rhodecode_repo.branches_closed,
121 c.rhodecode_repo.tags,
121 c.rhodecode_repo.tags,
122 c.rhodecode_repo.bookmarks)}
122 c.rhodecode_repo.bookmarks)}
123 % else:
123 % else:
124 ## missing requirements can make c.rhodecode_repo None
124 ## missing requirements can make c.rhodecode_repo None
125 ${refs_counters([], [], [], [])}
125 ${refs_counters([], [], [], [])}
126 % endif
126 % endif
127 </div>
127 </div>
128 </div>
128 </div>
129
129
130 </div>
130 </div>
131 </div>
131 </div>
132
132
133 <div class="fieldset collapsable-content" data-toggle="summary-details" style="display: none;">
133 <div class="fieldset collapsable-content" data-toggle="summary-details" style="display: none;">
134 <div class="left-label">
134 <div class="left-label">
135 ${_('Statistics')}:
135 ${_('Statistics')}:
136 </div>
136 </div>
137 <div class="right-content">
137 <div class="right-content">
138 <div class="input ${summary(c.show_stats)} statistics">
138 <div class="input ${summary(c.show_stats)} statistics">
139 % if c.show_stats:
139 % if c.show_stats:
140 <div id="lang_stats" class="enabled">
140 <div id="lang_stats" class="enabled">
141 ${_('Calculating Code Statistics...')}
141 ${_('Calculating Code Statistics...')}
142 </div>
142 </div>
143 % else:
143 % else:
144 <span class="disabled">
144 <span class="disabled">
145 ${_('Statistics are disabled for this repository')}
145 ${_('Statistics are disabled for this repository')}
146 </span>
146 </span>
147 % if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
147 % if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
148 , ${h.link_to(_('enable statistics'),h.url('edit_repo',repo_name=c.repo_name, anchor='repo_enable_statistics'))}
148 , ${h.link_to(_('enable statistics'),h.url('edit_repo',repo_name=c.repo_name, anchor='repo_enable_statistics'))}
149 % endif
149 % endif
150 % endif
150 % endif
151 </div>
151 </div>
152
152
153 </div>
153 </div>
154 </div>
154 </div>
155
155
156 % if show_downloads:
156 % if show_downloads:
157 <div class="fieldset collapsable-content" data-toggle="summary-details" style="display: none;">
157 <div class="fieldset collapsable-content" data-toggle="summary-details" style="display: none;">
158 <div class="left-label">
158 <div class="left-label">
159 ${_('Downloads')}:
159 ${_('Downloads')}:
160 </div>
160 </div>
161 <div class="right-content">
161 <div class="right-content">
162 <div class="input ${summary(c.show_stats)} downloads">
162 <div class="input ${summary(c.show_stats)} downloads">
163 % if c.rhodecode_repo and len(c.rhodecode_repo.revisions) == 0:
163 % if c.rhodecode_repo and len(c.rhodecode_repo.revisions) == 0:
164 <span class="disabled">
164 <span class="disabled">
165 ${_('There are no downloads yet')}
165 ${_('There are no downloads yet')}
166 </span>
166 </span>
167 % elif not c.enable_downloads:
167 % elif not c.enable_downloads:
168 <span class="disabled">
168 <span class="disabled">
169 ${_('Downloads are disabled for this repository')}
169 ${_('Downloads are disabled for this repository')}
170 </span>
170 </span>
171 % if h.HasPermissionAll('hg.admin')('enable downloads on from summary'):
171 % if h.HasPermissionAll('hg.admin')('enable downloads on from summary'):
172 , ${h.link_to(_('enable downloads'),h.url('edit_repo',repo_name=c.repo_name, anchor='repo_enable_downloads'))}
172 , ${h.link_to(_('enable downloads'),h.url('edit_repo',repo_name=c.repo_name, anchor='repo_enable_downloads'))}
173 % endif
173 % endif
174 % else:
174 % else:
175 <span class="enabled">
175 <span class="enabled">
176 <a id="archive_link" class="btn btn-small" href="${h.url('files_archive_home',repo_name=c.rhodecode_db_repo.repo_name,fname='tip.zip')}">
176 <a id="archive_link" class="btn btn-small" href="${h.url('files_archive_home',repo_name=c.rhodecode_db_repo.repo_name,fname='tip.zip')}">
177 <i class="icon-archive"></i> tip.zip
177 <i class="icon-archive"></i> tip.zip
178 ## replaced by some JS on select
178 ## replaced by some JS on select
179 </a>
179 </a>
180 </span>
180 </span>
181 ${h.hidden('download_options')}
181 ${h.hidden('download_options')}
182 % endif
182 % endif
183 </div>
183 </div>
184 </div>
184 </div>
185 </div>
185 </div>
186 % endif
186 % endif
187
187
188 </div><!--end summary-detail-->
188 </div><!--end summary-detail-->
189 </%def>
189 </%def>
190
190
191 <%def name="summary_stats(gravatar_function)">
191 <%def name="summary_stats(gravatar_function)">
192 <div class="sidebar-right">
192 <div class="sidebar-right">
193 <div class="summary-detail-header">
193 <div class="summary-detail-header">
194 <h4 class="item">
194 <h4 class="item">
195 ${_('Owner')}
195 ${_('Owner')}
196 </h4>
196 </h4>
197 </div>
197 </div>
198 <div class="sidebar-right-content">
198 <div class="sidebar-right-content">
199 ${gravatar_function(c.rhodecode_db_repo.user.email, 16)}
199 ${gravatar_function(c.rhodecode_db_repo.user.email, 16)}
200 </div>
200 </div>
201 </div><!--end sidebar-right-->
201 </div><!--end sidebar-right-->
202 </%def>
202 </%def>
@@ -1,40 +1,45 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
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
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
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/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import mock
21 import pytest
22
22 import requests
23 from rhodecode.config import routing_links
23 from rhodecode.config import routing_links
24
24
25
25
26 def test_connect_redirection_links():
26 def check_connection():
27 link_config = [
27 try:
28 {"name": "example_link",
28 response = requests.get('https://rhodecode.com')
29 "external_target": "http://example.com",
29 return response.status_code == 200
30 "target": "https://rhodecode.com/r/v1/enterprise/example",
30 except Exception as e:
31 },
31 print(e)
32 ]
32
33 return False
34
33
35
34 rmap = mock.Mock()
36 connection_available = pytest.mark.skipif(
35 with mock.patch.object(routing_links, 'link_config', link_config):
37 not check_connection(), reason="No outside internet connection available")
36 routing_links.connect_redirection_links(rmap)
38
37
39
38 rmap.connect.assert_called_with(
40 @connection_available
39 link_config[0]['name'], link_config[0]['target'],
41 def test_connect_redirection_links():
40 _static=True)
42
43 for link_data in routing_links.link_config:
44 response = requests.get(link_data['target'])
45 assert response.url == link_data['external_target']
General Comments 0
You need to be logged in to leave comments. Login now