##// END OF EJS Templates
feature: Go To switcher now searches commit hashes as well
dan -
r62:81398162 default
parent child Browse files
Show More
@@ -1,1085 +1,1085 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 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 from routes import Mapper
32 from routes import Mapper
33
33
34 from rhodecode.config import routing_links
34 from rhodecode.config import routing_links
35
35
36 # prefix for non repository related links needs to be prefixed with `/`
36 # prefix for non repository related links needs to be prefixed with `/`
37 ADMIN_PREFIX = '/_admin'
37 ADMIN_PREFIX = '/_admin'
38
38
39 # Default requirements for URL parts
39 # Default requirements for URL parts
40 URL_NAME_REQUIREMENTS = {
40 URL_NAME_REQUIREMENTS = {
41 # 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
42 'group_name': r'.*?[^/]',
42 'group_name': r'.*?[^/]',
43 # repo names can have a slash in them, but they must not end with a slash
43 # repo names can have a slash in them, but they must not end with a slash
44 'repo_name': r'.*?[^/]',
44 'repo_name': r'.*?[^/]',
45 # file path eats up everything at the end
45 # file path eats up everything at the end
46 'f_path': r'.*',
46 'f_path': r'.*',
47 # reference types
47 # reference types
48 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
48 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
49 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
49 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
50 }
50 }
51
51
52
52
53 def make_map(config):
53 def make_map(config):
54 """Create, configure and return the routes Mapper"""
54 """Create, configure and return the routes Mapper"""
55 rmap = Mapper(directory=config['pylons.paths']['controllers'],
55 rmap = Mapper(directory=config['pylons.paths']['controllers'],
56 always_scan=config['debug'])
56 always_scan=config['debug'])
57 rmap.minimization = False
57 rmap.minimization = False
58 rmap.explicit = False
58 rmap.explicit = False
59
59
60 from rhodecode.lib.utils2 import str2bool
60 from rhodecode.lib.utils2 import str2bool
61 from rhodecode.model import repo, repo_group
61 from rhodecode.model import repo, repo_group
62
62
63 def check_repo(environ, match_dict):
63 def check_repo(environ, match_dict):
64 """
64 """
65 check for valid repository for proper 404 handling
65 check for valid repository for proper 404 handling
66
66
67 :param environ:
67 :param environ:
68 :param match_dict:
68 :param match_dict:
69 """
69 """
70 repo_name = match_dict.get('repo_name')
70 repo_name = match_dict.get('repo_name')
71
71
72 if match_dict.get('f_path'):
72 if match_dict.get('f_path'):
73 # fix for multiple initial slashes that causes errors
73 # fix for multiple initial slashes that causes errors
74 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
74 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
75 repo_model = repo.RepoModel()
75 repo_model = repo.RepoModel()
76 by_name_match = repo_model.get_by_repo_name(repo_name)
76 by_name_match = repo_model.get_by_repo_name(repo_name)
77 # if we match quickly from database, short circuit the operation,
77 # if we match quickly from database, short circuit the operation,
78 # and validate repo based on the type.
78 # and validate repo based on the type.
79 if by_name_match:
79 if by_name_match:
80 return True
80 return True
81
81
82 by_id_match = repo_model.get_repo_by_id(repo_name)
82 by_id_match = repo_model.get_repo_by_id(repo_name)
83 if by_id_match:
83 if by_id_match:
84 repo_name = by_id_match.repo_name
84 repo_name = by_id_match.repo_name
85 match_dict['repo_name'] = repo_name
85 match_dict['repo_name'] = repo_name
86 return True
86 return True
87
87
88 return False
88 return False
89
89
90 def check_group(environ, match_dict):
90 def check_group(environ, match_dict):
91 """
91 """
92 check for valid repository group path for proper 404 handling
92 check for valid repository group path for proper 404 handling
93
93
94 :param environ:
94 :param environ:
95 :param match_dict:
95 :param match_dict:
96 """
96 """
97 repo_group_name = match_dict.get('group_name')
97 repo_group_name = match_dict.get('group_name')
98 repo_group_model = repo_group.RepoGroupModel()
98 repo_group_model = repo_group.RepoGroupModel()
99 by_name_match = repo_group_model.get_by_group_name(repo_group_name)
99 by_name_match = repo_group_model.get_by_group_name(repo_group_name)
100 if by_name_match:
100 if by_name_match:
101 return True
101 return True
102
102
103 return False
103 return False
104
104
105 def check_user_group(environ, match_dict):
105 def check_user_group(environ, match_dict):
106 """
106 """
107 check for valid user group for proper 404 handling
107 check for valid user group for proper 404 handling
108
108
109 :param environ:
109 :param environ:
110 :param match_dict:
110 :param match_dict:
111 """
111 """
112 return True
112 return True
113
113
114 def check_int(environ, match_dict):
114 def check_int(environ, match_dict):
115 return match_dict.get('id').isdigit()
115 return match_dict.get('id').isdigit()
116
116
117 # The ErrorController route (handles 404/500 error pages); it should
117 # The ErrorController route (handles 404/500 error pages); it should
118 # likely stay at the top, ensuring it can always be resolved
118 # likely stay at the top, ensuring it can always be resolved
119 rmap.connect('/error/{action}', controller='error')
119 rmap.connect('/error/{action}', controller='error')
120 rmap.connect('/error/{action}/{id}', controller='error')
120 rmap.connect('/error/{action}/{id}', controller='error')
121
121
122 #==========================================================================
122 #==========================================================================
123 # CUSTOM ROUTES HERE
123 # CUSTOM ROUTES HERE
124 #==========================================================================
124 #==========================================================================
125
125
126 # MAIN PAGE
126 # MAIN PAGE
127 rmap.connect('home', '/', controller='home', action='index')
127 rmap.connect('home', '/', controller='home', action='index')
128 rmap.connect('repo_switcher_data', '/_repos_and_groups', controller='home',
128 rmap.connect('goto_switcher_data', '/_goto_data', controller='home',
129 action='repo_switcher_data')
129 action='goto_switcher_data')
130 rmap.connect('repo_list_data', '/_repos', controller='home',
130 rmap.connect('repo_list_data', '/_repos', controller='home',
131 action='repo_list_data')
131 action='repo_list_data')
132
132
133 rmap.connect('user_autocomplete_data', '/_users', controller='home',
133 rmap.connect('user_autocomplete_data', '/_users', controller='home',
134 action='user_autocomplete_data')
134 action='user_autocomplete_data')
135 rmap.connect('user_group_autocomplete_data', '/_user_groups', controller='home',
135 rmap.connect('user_group_autocomplete_data', '/_user_groups', controller='home',
136 action='user_group_autocomplete_data')
136 action='user_group_autocomplete_data')
137
137
138 rmap.connect(
138 rmap.connect(
139 'user_profile', '/_profiles/{username}', controller='users',
139 'user_profile', '/_profiles/{username}', controller='users',
140 action='user_profile')
140 action='user_profile')
141
141
142 # TODO: johbo: Static links, to be replaced by our redirection mechanism
142 # TODO: johbo: Static links, to be replaced by our redirection mechanism
143 rmap.connect('rst_help',
143 rmap.connect('rst_help',
144 'http://docutils.sourceforge.net/docs/user/rst/quickref.html',
144 'http://docutils.sourceforge.net/docs/user/rst/quickref.html',
145 _static=True)
145 _static=True)
146 rmap.connect('markdown_help',
146 rmap.connect('markdown_help',
147 'http://daringfireball.net/projects/markdown/syntax',
147 'http://daringfireball.net/projects/markdown/syntax',
148 _static=True)
148 _static=True)
149 rmap.connect('rhodecode_official', 'https://rhodecode.com', _static=True)
149 rmap.connect('rhodecode_official', 'https://rhodecode.com', _static=True)
150 rmap.connect('rhodecode_support', 'https://rhodecode.com/help/', _static=True)
150 rmap.connect('rhodecode_support', 'https://rhodecode.com/help/', _static=True)
151 rmap.connect('rhodecode_translations', 'https://rhodecode.com/translate/enterprise', _static=True)
151 rmap.connect('rhodecode_translations', 'https://rhodecode.com/translate/enterprise', _static=True)
152 # TODO: anderson - making this a static link since redirect won't play
152 # TODO: anderson - making this a static link since redirect won't play
153 # nice with POST requests
153 # nice with POST requests
154 rmap.connect('enterprise_license_convert_from_old',
154 rmap.connect('enterprise_license_convert_from_old',
155 'https://rhodecode.com/u/license-upgrade',
155 'https://rhodecode.com/u/license-upgrade',
156 _static=True)
156 _static=True)
157
157
158 routing_links.connect_redirection_links(rmap)
158 routing_links.connect_redirection_links(rmap)
159
159
160 rmap.connect('ping', '%s/ping' % (ADMIN_PREFIX,), controller='home', action='ping')
160 rmap.connect('ping', '%s/ping' % (ADMIN_PREFIX,), controller='home', action='ping')
161 rmap.connect('error_test', '%s/error_test' % (ADMIN_PREFIX,), controller='home', action='error_test')
161 rmap.connect('error_test', '%s/error_test' % (ADMIN_PREFIX,), controller='home', action='error_test')
162
162
163 # ADMIN REPOSITORY ROUTES
163 # ADMIN REPOSITORY ROUTES
164 with rmap.submapper(path_prefix=ADMIN_PREFIX,
164 with rmap.submapper(path_prefix=ADMIN_PREFIX,
165 controller='admin/repos') as m:
165 controller='admin/repos') as m:
166 m.connect('repos', '/repos',
166 m.connect('repos', '/repos',
167 action='create', conditions={'method': ['POST']})
167 action='create', conditions={'method': ['POST']})
168 m.connect('repos', '/repos',
168 m.connect('repos', '/repos',
169 action='index', conditions={'method': ['GET']})
169 action='index', conditions={'method': ['GET']})
170 m.connect('new_repo', '/create_repository',
170 m.connect('new_repo', '/create_repository',
171 action='create_repository', conditions={'method': ['GET']})
171 action='create_repository', conditions={'method': ['GET']})
172 m.connect('/repos/{repo_name}',
172 m.connect('/repos/{repo_name}',
173 action='update', conditions={'method': ['PUT'],
173 action='update', conditions={'method': ['PUT'],
174 'function': check_repo},
174 'function': check_repo},
175 requirements=URL_NAME_REQUIREMENTS)
175 requirements=URL_NAME_REQUIREMENTS)
176 m.connect('delete_repo', '/repos/{repo_name}',
176 m.connect('delete_repo', '/repos/{repo_name}',
177 action='delete', conditions={'method': ['DELETE']},
177 action='delete', conditions={'method': ['DELETE']},
178 requirements=URL_NAME_REQUIREMENTS)
178 requirements=URL_NAME_REQUIREMENTS)
179 m.connect('repo', '/repos/{repo_name}',
179 m.connect('repo', '/repos/{repo_name}',
180 action='show', conditions={'method': ['GET'],
180 action='show', conditions={'method': ['GET'],
181 'function': check_repo},
181 'function': check_repo},
182 requirements=URL_NAME_REQUIREMENTS)
182 requirements=URL_NAME_REQUIREMENTS)
183
183
184 # ADMIN REPOSITORY GROUPS ROUTES
184 # ADMIN REPOSITORY GROUPS ROUTES
185 with rmap.submapper(path_prefix=ADMIN_PREFIX,
185 with rmap.submapper(path_prefix=ADMIN_PREFIX,
186 controller='admin/repo_groups') as m:
186 controller='admin/repo_groups') as m:
187 m.connect('repo_groups', '/repo_groups',
187 m.connect('repo_groups', '/repo_groups',
188 action='create', conditions={'method': ['POST']})
188 action='create', conditions={'method': ['POST']})
189 m.connect('repo_groups', '/repo_groups',
189 m.connect('repo_groups', '/repo_groups',
190 action='index', conditions={'method': ['GET']})
190 action='index', conditions={'method': ['GET']})
191 m.connect('new_repo_group', '/repo_groups/new',
191 m.connect('new_repo_group', '/repo_groups/new',
192 action='new', conditions={'method': ['GET']})
192 action='new', conditions={'method': ['GET']})
193 m.connect('update_repo_group', '/repo_groups/{group_name}',
193 m.connect('update_repo_group', '/repo_groups/{group_name}',
194 action='update', conditions={'method': ['PUT'],
194 action='update', conditions={'method': ['PUT'],
195 'function': check_group},
195 'function': check_group},
196 requirements=URL_NAME_REQUIREMENTS)
196 requirements=URL_NAME_REQUIREMENTS)
197
197
198 # EXTRAS REPO GROUP ROUTES
198 # EXTRAS REPO GROUP ROUTES
199 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
199 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
200 action='edit',
200 action='edit',
201 conditions={'method': ['GET'], 'function': check_group},
201 conditions={'method': ['GET'], 'function': check_group},
202 requirements=URL_NAME_REQUIREMENTS)
202 requirements=URL_NAME_REQUIREMENTS)
203 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
203 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
204 action='edit',
204 action='edit',
205 conditions={'method': ['PUT'], 'function': check_group},
205 conditions={'method': ['PUT'], 'function': check_group},
206 requirements=URL_NAME_REQUIREMENTS)
206 requirements=URL_NAME_REQUIREMENTS)
207
207
208 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
208 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
209 action='edit_repo_group_advanced',
209 action='edit_repo_group_advanced',
210 conditions={'method': ['GET'], 'function': check_group},
210 conditions={'method': ['GET'], 'function': check_group},
211 requirements=URL_NAME_REQUIREMENTS)
211 requirements=URL_NAME_REQUIREMENTS)
212 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
212 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
213 action='edit_repo_group_advanced',
213 action='edit_repo_group_advanced',
214 conditions={'method': ['PUT'], 'function': check_group},
214 conditions={'method': ['PUT'], 'function': check_group},
215 requirements=URL_NAME_REQUIREMENTS)
215 requirements=URL_NAME_REQUIREMENTS)
216
216
217 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
217 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
218 action='edit_repo_group_perms',
218 action='edit_repo_group_perms',
219 conditions={'method': ['GET'], 'function': check_group},
219 conditions={'method': ['GET'], 'function': check_group},
220 requirements=URL_NAME_REQUIREMENTS)
220 requirements=URL_NAME_REQUIREMENTS)
221 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
221 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
222 action='update_perms',
222 action='update_perms',
223 conditions={'method': ['PUT'], 'function': check_group},
223 conditions={'method': ['PUT'], 'function': check_group},
224 requirements=URL_NAME_REQUIREMENTS)
224 requirements=URL_NAME_REQUIREMENTS)
225
225
226 m.connect('delete_repo_group', '/repo_groups/{group_name}',
226 m.connect('delete_repo_group', '/repo_groups/{group_name}',
227 action='delete', conditions={'method': ['DELETE'],
227 action='delete', conditions={'method': ['DELETE'],
228 'function': check_group},
228 'function': check_group},
229 requirements=URL_NAME_REQUIREMENTS)
229 requirements=URL_NAME_REQUIREMENTS)
230
230
231 # ADMIN USER ROUTES
231 # ADMIN USER ROUTES
232 with rmap.submapper(path_prefix=ADMIN_PREFIX,
232 with rmap.submapper(path_prefix=ADMIN_PREFIX,
233 controller='admin/users') as m:
233 controller='admin/users') as m:
234 m.connect('users', '/users',
234 m.connect('users', '/users',
235 action='create', conditions={'method': ['POST']})
235 action='create', conditions={'method': ['POST']})
236 m.connect('users', '/users',
236 m.connect('users', '/users',
237 action='index', conditions={'method': ['GET']})
237 action='index', conditions={'method': ['GET']})
238 m.connect('new_user', '/users/new',
238 m.connect('new_user', '/users/new',
239 action='new', conditions={'method': ['GET']})
239 action='new', conditions={'method': ['GET']})
240 m.connect('update_user', '/users/{user_id}',
240 m.connect('update_user', '/users/{user_id}',
241 action='update', conditions={'method': ['PUT']})
241 action='update', conditions={'method': ['PUT']})
242 m.connect('delete_user', '/users/{user_id}',
242 m.connect('delete_user', '/users/{user_id}',
243 action='delete', conditions={'method': ['DELETE']})
243 action='delete', conditions={'method': ['DELETE']})
244 m.connect('edit_user', '/users/{user_id}/edit',
244 m.connect('edit_user', '/users/{user_id}/edit',
245 action='edit', conditions={'method': ['GET']})
245 action='edit', conditions={'method': ['GET']})
246 m.connect('user', '/users/{user_id}',
246 m.connect('user', '/users/{user_id}',
247 action='show', conditions={'method': ['GET']})
247 action='show', conditions={'method': ['GET']})
248 m.connect('force_password_reset_user', '/users/{user_id}/password_reset',
248 m.connect('force_password_reset_user', '/users/{user_id}/password_reset',
249 action='reset_password', conditions={'method': ['POST']})
249 action='reset_password', conditions={'method': ['POST']})
250 m.connect('create_personal_repo_group', '/users/{user_id}/create_repo_group',
250 m.connect('create_personal_repo_group', '/users/{user_id}/create_repo_group',
251 action='create_personal_repo_group', conditions={'method': ['POST']})
251 action='create_personal_repo_group', conditions={'method': ['POST']})
252
252
253 # EXTRAS USER ROUTES
253 # EXTRAS USER ROUTES
254 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
254 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
255 action='edit_advanced', conditions={'method': ['GET']})
255 action='edit_advanced', conditions={'method': ['GET']})
256 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
256 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
257 action='update_advanced', conditions={'method': ['PUT']})
257 action='update_advanced', conditions={'method': ['PUT']})
258
258
259 m.connect('edit_user_auth_tokens', '/users/{user_id}/edit/auth_tokens',
259 m.connect('edit_user_auth_tokens', '/users/{user_id}/edit/auth_tokens',
260 action='edit_auth_tokens', conditions={'method': ['GET']})
260 action='edit_auth_tokens', conditions={'method': ['GET']})
261 m.connect('edit_user_auth_tokens', '/users/{user_id}/edit/auth_tokens',
261 m.connect('edit_user_auth_tokens', '/users/{user_id}/edit/auth_tokens',
262 action='add_auth_token', conditions={'method': ['PUT']})
262 action='add_auth_token', conditions={'method': ['PUT']})
263 m.connect('edit_user_auth_tokens', '/users/{user_id}/edit/auth_tokens',
263 m.connect('edit_user_auth_tokens', '/users/{user_id}/edit/auth_tokens',
264 action='delete_auth_token', conditions={'method': ['DELETE']})
264 action='delete_auth_token', conditions={'method': ['DELETE']})
265
265
266 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
266 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
267 action='edit_global_perms', conditions={'method': ['GET']})
267 action='edit_global_perms', conditions={'method': ['GET']})
268 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
268 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
269 action='update_global_perms', conditions={'method': ['PUT']})
269 action='update_global_perms', conditions={'method': ['PUT']})
270
270
271 m.connect('edit_user_perms_summary', '/users/{user_id}/edit/permissions_summary',
271 m.connect('edit_user_perms_summary', '/users/{user_id}/edit/permissions_summary',
272 action='edit_perms_summary', conditions={'method': ['GET']})
272 action='edit_perms_summary', conditions={'method': ['GET']})
273
273
274 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
274 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
275 action='edit_emails', conditions={'method': ['GET']})
275 action='edit_emails', conditions={'method': ['GET']})
276 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
276 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
277 action='add_email', conditions={'method': ['PUT']})
277 action='add_email', conditions={'method': ['PUT']})
278 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
278 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
279 action='delete_email', conditions={'method': ['DELETE']})
279 action='delete_email', conditions={'method': ['DELETE']})
280
280
281 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
281 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
282 action='edit_ips', conditions={'method': ['GET']})
282 action='edit_ips', conditions={'method': ['GET']})
283 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
283 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
284 action='add_ip', conditions={'method': ['PUT']})
284 action='add_ip', conditions={'method': ['PUT']})
285 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
285 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
286 action='delete_ip', conditions={'method': ['DELETE']})
286 action='delete_ip', conditions={'method': ['DELETE']})
287
287
288 # ADMIN USER GROUPS REST ROUTES
288 # ADMIN USER GROUPS REST ROUTES
289 with rmap.submapper(path_prefix=ADMIN_PREFIX,
289 with rmap.submapper(path_prefix=ADMIN_PREFIX,
290 controller='admin/user_groups') as m:
290 controller='admin/user_groups') as m:
291 m.connect('users_groups', '/user_groups',
291 m.connect('users_groups', '/user_groups',
292 action='create', conditions={'method': ['POST']})
292 action='create', conditions={'method': ['POST']})
293 m.connect('users_groups', '/user_groups',
293 m.connect('users_groups', '/user_groups',
294 action='index', conditions={'method': ['GET']})
294 action='index', conditions={'method': ['GET']})
295 m.connect('new_users_group', '/user_groups/new',
295 m.connect('new_users_group', '/user_groups/new',
296 action='new', conditions={'method': ['GET']})
296 action='new', conditions={'method': ['GET']})
297 m.connect('update_users_group', '/user_groups/{user_group_id}',
297 m.connect('update_users_group', '/user_groups/{user_group_id}',
298 action='update', conditions={'method': ['PUT']})
298 action='update', conditions={'method': ['PUT']})
299 m.connect('delete_users_group', '/user_groups/{user_group_id}',
299 m.connect('delete_users_group', '/user_groups/{user_group_id}',
300 action='delete', conditions={'method': ['DELETE']})
300 action='delete', conditions={'method': ['DELETE']})
301 m.connect('edit_users_group', '/user_groups/{user_group_id}/edit',
301 m.connect('edit_users_group', '/user_groups/{user_group_id}/edit',
302 action='edit', conditions={'method': ['GET']},
302 action='edit', conditions={'method': ['GET']},
303 function=check_user_group)
303 function=check_user_group)
304
304
305 # EXTRAS USER GROUP ROUTES
305 # EXTRAS USER GROUP ROUTES
306 m.connect('edit_user_group_global_perms', '/user_groups/{user_group_id}/edit/global_permissions',
306 m.connect('edit_user_group_global_perms', '/user_groups/{user_group_id}/edit/global_permissions',
307 action='edit_global_perms', conditions={'method': ['GET']})
307 action='edit_global_perms', conditions={'method': ['GET']})
308 m.connect('edit_user_group_global_perms', '/user_groups/{user_group_id}/edit/global_permissions',
308 m.connect('edit_user_group_global_perms', '/user_groups/{user_group_id}/edit/global_permissions',
309 action='update_global_perms', conditions={'method': ['PUT']})
309 action='update_global_perms', conditions={'method': ['PUT']})
310 m.connect('edit_user_group_perms_summary', '/user_groups/{user_group_id}/edit/permissions_summary',
310 m.connect('edit_user_group_perms_summary', '/user_groups/{user_group_id}/edit/permissions_summary',
311 action='edit_perms_summary', conditions={'method': ['GET']})
311 action='edit_perms_summary', conditions={'method': ['GET']})
312
312
313 m.connect('edit_user_group_perms', '/user_groups/{user_group_id}/edit/permissions',
313 m.connect('edit_user_group_perms', '/user_groups/{user_group_id}/edit/permissions',
314 action='edit_perms', conditions={'method': ['GET']})
314 action='edit_perms', conditions={'method': ['GET']})
315 m.connect('edit_user_group_perms', '/user_groups/{user_group_id}/edit/permissions',
315 m.connect('edit_user_group_perms', '/user_groups/{user_group_id}/edit/permissions',
316 action='update_perms', conditions={'method': ['PUT']})
316 action='update_perms', conditions={'method': ['PUT']})
317
317
318 m.connect('edit_user_group_advanced', '/user_groups/{user_group_id}/edit/advanced',
318 m.connect('edit_user_group_advanced', '/user_groups/{user_group_id}/edit/advanced',
319 action='edit_advanced', conditions={'method': ['GET']})
319 action='edit_advanced', conditions={'method': ['GET']})
320
320
321 m.connect('edit_user_group_members', '/user_groups/{user_group_id}/edit/members',
321 m.connect('edit_user_group_members', '/user_groups/{user_group_id}/edit/members',
322 action='edit_members', conditions={'method': ['GET']})
322 action='edit_members', conditions={'method': ['GET']})
323
323
324 # ADMIN PERMISSIONS ROUTES
324 # ADMIN PERMISSIONS ROUTES
325 with rmap.submapper(path_prefix=ADMIN_PREFIX,
325 with rmap.submapper(path_prefix=ADMIN_PREFIX,
326 controller='admin/permissions') as m:
326 controller='admin/permissions') as m:
327 m.connect('admin_permissions_application', '/permissions/application',
327 m.connect('admin_permissions_application', '/permissions/application',
328 action='permission_application_update', conditions={'method': ['POST']})
328 action='permission_application_update', conditions={'method': ['POST']})
329 m.connect('admin_permissions_application', '/permissions/application',
329 m.connect('admin_permissions_application', '/permissions/application',
330 action='permission_application', conditions={'method': ['GET']})
330 action='permission_application', conditions={'method': ['GET']})
331
331
332 m.connect('admin_permissions_global', '/permissions/global',
332 m.connect('admin_permissions_global', '/permissions/global',
333 action='permission_global_update', conditions={'method': ['POST']})
333 action='permission_global_update', conditions={'method': ['POST']})
334 m.connect('admin_permissions_global', '/permissions/global',
334 m.connect('admin_permissions_global', '/permissions/global',
335 action='permission_global', conditions={'method': ['GET']})
335 action='permission_global', conditions={'method': ['GET']})
336
336
337 m.connect('admin_permissions_object', '/permissions/object',
337 m.connect('admin_permissions_object', '/permissions/object',
338 action='permission_objects_update', conditions={'method': ['POST']})
338 action='permission_objects_update', conditions={'method': ['POST']})
339 m.connect('admin_permissions_object', '/permissions/object',
339 m.connect('admin_permissions_object', '/permissions/object',
340 action='permission_objects', conditions={'method': ['GET']})
340 action='permission_objects', conditions={'method': ['GET']})
341
341
342 m.connect('admin_permissions_ips', '/permissions/ips',
342 m.connect('admin_permissions_ips', '/permissions/ips',
343 action='permission_ips', conditions={'method': ['POST']})
343 action='permission_ips', conditions={'method': ['POST']})
344 m.connect('admin_permissions_ips', '/permissions/ips',
344 m.connect('admin_permissions_ips', '/permissions/ips',
345 action='permission_ips', conditions={'method': ['GET']})
345 action='permission_ips', conditions={'method': ['GET']})
346
346
347 m.connect('admin_permissions_overview', '/permissions/overview',
347 m.connect('admin_permissions_overview', '/permissions/overview',
348 action='permission_perms', conditions={'method': ['GET']})
348 action='permission_perms', conditions={'method': ['GET']})
349
349
350 # ADMIN DEFAULTS REST ROUTES
350 # ADMIN DEFAULTS REST ROUTES
351 with rmap.submapper(path_prefix=ADMIN_PREFIX,
351 with rmap.submapper(path_prefix=ADMIN_PREFIX,
352 controller='admin/defaults') as m:
352 controller='admin/defaults') as m:
353 m.connect('admin_defaults_repositories', '/defaults/repositories',
353 m.connect('admin_defaults_repositories', '/defaults/repositories',
354 action='update_repository_defaults', conditions={'method': ['POST']})
354 action='update_repository_defaults', conditions={'method': ['POST']})
355 m.connect('admin_defaults_repositories', '/defaults/repositories',
355 m.connect('admin_defaults_repositories', '/defaults/repositories',
356 action='index', conditions={'method': ['GET']})
356 action='index', conditions={'method': ['GET']})
357
357
358 # ADMIN DEBUG STYLE ROUTES
358 # ADMIN DEBUG STYLE ROUTES
359 if str2bool(config.get('debug_style')):
359 if str2bool(config.get('debug_style')):
360 with rmap.submapper(path_prefix=ADMIN_PREFIX + '/debug_style',
360 with rmap.submapper(path_prefix=ADMIN_PREFIX + '/debug_style',
361 controller='debug_style') as m:
361 controller='debug_style') as m:
362 m.connect('debug_style_home', '',
362 m.connect('debug_style_home', '',
363 action='index', conditions={'method': ['GET']})
363 action='index', conditions={'method': ['GET']})
364 m.connect('debug_style_template', '/t/{t_path}',
364 m.connect('debug_style_template', '/t/{t_path}',
365 action='template', conditions={'method': ['GET']})
365 action='template', conditions={'method': ['GET']})
366
366
367 # ADMIN SETTINGS ROUTES
367 # ADMIN SETTINGS ROUTES
368 with rmap.submapper(path_prefix=ADMIN_PREFIX,
368 with rmap.submapper(path_prefix=ADMIN_PREFIX,
369 controller='admin/settings') as m:
369 controller='admin/settings') as m:
370
370
371 # default
371 # default
372 m.connect('admin_settings', '/settings',
372 m.connect('admin_settings', '/settings',
373 action='settings_global_update',
373 action='settings_global_update',
374 conditions={'method': ['POST']})
374 conditions={'method': ['POST']})
375 m.connect('admin_settings', '/settings',
375 m.connect('admin_settings', '/settings',
376 action='settings_global', conditions={'method': ['GET']})
376 action='settings_global', conditions={'method': ['GET']})
377
377
378 m.connect('admin_settings_vcs', '/settings/vcs',
378 m.connect('admin_settings_vcs', '/settings/vcs',
379 action='settings_vcs_update',
379 action='settings_vcs_update',
380 conditions={'method': ['POST']})
380 conditions={'method': ['POST']})
381 m.connect('admin_settings_vcs', '/settings/vcs',
381 m.connect('admin_settings_vcs', '/settings/vcs',
382 action='settings_vcs',
382 action='settings_vcs',
383 conditions={'method': ['GET']})
383 conditions={'method': ['GET']})
384 m.connect('admin_settings_vcs', '/settings/vcs',
384 m.connect('admin_settings_vcs', '/settings/vcs',
385 action='delete_svn_pattern',
385 action='delete_svn_pattern',
386 conditions={'method': ['DELETE']})
386 conditions={'method': ['DELETE']})
387
387
388 m.connect('admin_settings_mapping', '/settings/mapping',
388 m.connect('admin_settings_mapping', '/settings/mapping',
389 action='settings_mapping_update',
389 action='settings_mapping_update',
390 conditions={'method': ['POST']})
390 conditions={'method': ['POST']})
391 m.connect('admin_settings_mapping', '/settings/mapping',
391 m.connect('admin_settings_mapping', '/settings/mapping',
392 action='settings_mapping', conditions={'method': ['GET']})
392 action='settings_mapping', conditions={'method': ['GET']})
393
393
394 m.connect('admin_settings_global', '/settings/global',
394 m.connect('admin_settings_global', '/settings/global',
395 action='settings_global_update',
395 action='settings_global_update',
396 conditions={'method': ['POST']})
396 conditions={'method': ['POST']})
397 m.connect('admin_settings_global', '/settings/global',
397 m.connect('admin_settings_global', '/settings/global',
398 action='settings_global', conditions={'method': ['GET']})
398 action='settings_global', conditions={'method': ['GET']})
399
399
400 m.connect('admin_settings_visual', '/settings/visual',
400 m.connect('admin_settings_visual', '/settings/visual',
401 action='settings_visual_update',
401 action='settings_visual_update',
402 conditions={'method': ['POST']})
402 conditions={'method': ['POST']})
403 m.connect('admin_settings_visual', '/settings/visual',
403 m.connect('admin_settings_visual', '/settings/visual',
404 action='settings_visual', conditions={'method': ['GET']})
404 action='settings_visual', conditions={'method': ['GET']})
405
405
406 m.connect('admin_settings_issuetracker',
406 m.connect('admin_settings_issuetracker',
407 '/settings/issue-tracker', action='settings_issuetracker',
407 '/settings/issue-tracker', action='settings_issuetracker',
408 conditions={'method': ['GET']})
408 conditions={'method': ['GET']})
409 m.connect('admin_settings_issuetracker_save',
409 m.connect('admin_settings_issuetracker_save',
410 '/settings/issue-tracker/save',
410 '/settings/issue-tracker/save',
411 action='settings_issuetracker_save',
411 action='settings_issuetracker_save',
412 conditions={'method': ['POST']})
412 conditions={'method': ['POST']})
413 m.connect('admin_issuetracker_test', '/settings/issue-tracker/test',
413 m.connect('admin_issuetracker_test', '/settings/issue-tracker/test',
414 action='settings_issuetracker_test',
414 action='settings_issuetracker_test',
415 conditions={'method': ['POST']})
415 conditions={'method': ['POST']})
416 m.connect('admin_issuetracker_delete',
416 m.connect('admin_issuetracker_delete',
417 '/settings/issue-tracker/delete',
417 '/settings/issue-tracker/delete',
418 action='settings_issuetracker_delete',
418 action='settings_issuetracker_delete',
419 conditions={'method': ['DELETE']})
419 conditions={'method': ['DELETE']})
420
420
421 m.connect('admin_settings_email', '/settings/email',
421 m.connect('admin_settings_email', '/settings/email',
422 action='settings_email_update',
422 action='settings_email_update',
423 conditions={'method': ['POST']})
423 conditions={'method': ['POST']})
424 m.connect('admin_settings_email', '/settings/email',
424 m.connect('admin_settings_email', '/settings/email',
425 action='settings_email', conditions={'method': ['GET']})
425 action='settings_email', conditions={'method': ['GET']})
426
426
427 m.connect('admin_settings_hooks', '/settings/hooks',
427 m.connect('admin_settings_hooks', '/settings/hooks',
428 action='settings_hooks_update',
428 action='settings_hooks_update',
429 conditions={'method': ['POST', 'DELETE']})
429 conditions={'method': ['POST', 'DELETE']})
430 m.connect('admin_settings_hooks', '/settings/hooks',
430 m.connect('admin_settings_hooks', '/settings/hooks',
431 action='settings_hooks', conditions={'method': ['GET']})
431 action='settings_hooks', conditions={'method': ['GET']})
432
432
433 m.connect('admin_settings_search', '/settings/search',
433 m.connect('admin_settings_search', '/settings/search',
434 action='settings_search', conditions={'method': ['GET']})
434 action='settings_search', conditions={'method': ['GET']})
435
435
436 m.connect('admin_settings_system', '/settings/system',
436 m.connect('admin_settings_system', '/settings/system',
437 action='settings_system', conditions={'method': ['GET']})
437 action='settings_system', conditions={'method': ['GET']})
438
438
439 m.connect('admin_settings_system_update', '/settings/system/updates',
439 m.connect('admin_settings_system_update', '/settings/system/updates',
440 action='settings_system_update', conditions={'method': ['GET']})
440 action='settings_system_update', conditions={'method': ['GET']})
441
441
442 m.connect('admin_settings_supervisor', '/settings/supervisor',
442 m.connect('admin_settings_supervisor', '/settings/supervisor',
443 action='settings_supervisor', conditions={'method': ['GET']})
443 action='settings_supervisor', conditions={'method': ['GET']})
444 m.connect('admin_settings_supervisor_log', '/settings/supervisor/{procid}/log',
444 m.connect('admin_settings_supervisor_log', '/settings/supervisor/{procid}/log',
445 action='settings_supervisor_log', conditions={'method': ['GET']})
445 action='settings_supervisor_log', conditions={'method': ['GET']})
446
446
447 m.connect('admin_settings_labs', '/settings/labs',
447 m.connect('admin_settings_labs', '/settings/labs',
448 action='settings_labs_update',
448 action='settings_labs_update',
449 conditions={'method': ['POST']})
449 conditions={'method': ['POST']})
450 m.connect('admin_settings_labs', '/settings/labs',
450 m.connect('admin_settings_labs', '/settings/labs',
451 action='settings_labs', conditions={'method': ['GET']})
451 action='settings_labs', conditions={'method': ['GET']})
452
452
453 m.connect('admin_settings_open_source', '/settings/open_source',
453 m.connect('admin_settings_open_source', '/settings/open_source',
454 action='settings_open_source',
454 action='settings_open_source',
455 conditions={'method': ['GET']})
455 conditions={'method': ['GET']})
456
456
457 # ADMIN MY ACCOUNT
457 # ADMIN MY ACCOUNT
458 with rmap.submapper(path_prefix=ADMIN_PREFIX,
458 with rmap.submapper(path_prefix=ADMIN_PREFIX,
459 controller='admin/my_account') as m:
459 controller='admin/my_account') as m:
460
460
461 m.connect('my_account', '/my_account',
461 m.connect('my_account', '/my_account',
462 action='my_account', conditions={'method': ['GET']})
462 action='my_account', conditions={'method': ['GET']})
463 m.connect('my_account_edit', '/my_account/edit',
463 m.connect('my_account_edit', '/my_account/edit',
464 action='my_account_edit', conditions={'method': ['GET']})
464 action='my_account_edit', conditions={'method': ['GET']})
465 m.connect('my_account', '/my_account',
465 m.connect('my_account', '/my_account',
466 action='my_account_update', conditions={'method': ['POST']})
466 action='my_account_update', conditions={'method': ['POST']})
467
467
468 m.connect('my_account_password', '/my_account/password',
468 m.connect('my_account_password', '/my_account/password',
469 action='my_account_password', conditions={'method': ['GET']})
469 action='my_account_password', conditions={'method': ['GET']})
470 m.connect('my_account_password', '/my_account/password',
470 m.connect('my_account_password', '/my_account/password',
471 action='my_account_password_update', conditions={'method': ['POST']})
471 action='my_account_password_update', conditions={'method': ['POST']})
472
472
473 m.connect('my_account_repos', '/my_account/repos',
473 m.connect('my_account_repos', '/my_account/repos',
474 action='my_account_repos', conditions={'method': ['GET']})
474 action='my_account_repos', conditions={'method': ['GET']})
475
475
476 m.connect('my_account_watched', '/my_account/watched',
476 m.connect('my_account_watched', '/my_account/watched',
477 action='my_account_watched', conditions={'method': ['GET']})
477 action='my_account_watched', conditions={'method': ['GET']})
478
478
479 m.connect('my_account_pullrequests', '/my_account/pull_requests',
479 m.connect('my_account_pullrequests', '/my_account/pull_requests',
480 action='my_account_pullrequests', conditions={'method': ['GET']})
480 action='my_account_pullrequests', conditions={'method': ['GET']})
481
481
482 m.connect('my_account_perms', '/my_account/perms',
482 m.connect('my_account_perms', '/my_account/perms',
483 action='my_account_perms', conditions={'method': ['GET']})
483 action='my_account_perms', conditions={'method': ['GET']})
484
484
485 m.connect('my_account_emails', '/my_account/emails',
485 m.connect('my_account_emails', '/my_account/emails',
486 action='my_account_emails', conditions={'method': ['GET']})
486 action='my_account_emails', conditions={'method': ['GET']})
487 m.connect('my_account_emails', '/my_account/emails',
487 m.connect('my_account_emails', '/my_account/emails',
488 action='my_account_emails_add', conditions={'method': ['POST']})
488 action='my_account_emails_add', conditions={'method': ['POST']})
489 m.connect('my_account_emails', '/my_account/emails',
489 m.connect('my_account_emails', '/my_account/emails',
490 action='my_account_emails_delete', conditions={'method': ['DELETE']})
490 action='my_account_emails_delete', conditions={'method': ['DELETE']})
491
491
492 m.connect('my_account_auth_tokens', '/my_account/auth_tokens',
492 m.connect('my_account_auth_tokens', '/my_account/auth_tokens',
493 action='my_account_auth_tokens', conditions={'method': ['GET']})
493 action='my_account_auth_tokens', conditions={'method': ['GET']})
494 m.connect('my_account_auth_tokens', '/my_account/auth_tokens',
494 m.connect('my_account_auth_tokens', '/my_account/auth_tokens',
495 action='my_account_auth_tokens_add', conditions={'method': ['POST']})
495 action='my_account_auth_tokens_add', conditions={'method': ['POST']})
496 m.connect('my_account_auth_tokens', '/my_account/auth_tokens',
496 m.connect('my_account_auth_tokens', '/my_account/auth_tokens',
497 action='my_account_auth_tokens_delete', conditions={'method': ['DELETE']})
497 action='my_account_auth_tokens_delete', conditions={'method': ['DELETE']})
498
498
499 # NOTIFICATION REST ROUTES
499 # NOTIFICATION REST ROUTES
500 with rmap.submapper(path_prefix=ADMIN_PREFIX,
500 with rmap.submapper(path_prefix=ADMIN_PREFIX,
501 controller='admin/notifications') as m:
501 controller='admin/notifications') as m:
502 m.connect('notifications', '/notifications',
502 m.connect('notifications', '/notifications',
503 action='index', conditions={'method': ['GET']})
503 action='index', conditions={'method': ['GET']})
504 m.connect('notifications_mark_all_read', '/notifications/mark_all_read',
504 m.connect('notifications_mark_all_read', '/notifications/mark_all_read',
505 action='mark_all_read', conditions={'method': ['POST']})
505 action='mark_all_read', conditions={'method': ['POST']})
506
506
507 m.connect('/notifications/{notification_id}',
507 m.connect('/notifications/{notification_id}',
508 action='update', conditions={'method': ['PUT']})
508 action='update', conditions={'method': ['PUT']})
509 m.connect('/notifications/{notification_id}',
509 m.connect('/notifications/{notification_id}',
510 action='delete', conditions={'method': ['DELETE']})
510 action='delete', conditions={'method': ['DELETE']})
511 m.connect('notification', '/notifications/{notification_id}',
511 m.connect('notification', '/notifications/{notification_id}',
512 action='show', conditions={'method': ['GET']})
512 action='show', conditions={'method': ['GET']})
513
513
514 # ADMIN GIST
514 # ADMIN GIST
515 with rmap.submapper(path_prefix=ADMIN_PREFIX,
515 with rmap.submapper(path_prefix=ADMIN_PREFIX,
516 controller='admin/gists') as m:
516 controller='admin/gists') as m:
517 m.connect('gists', '/gists',
517 m.connect('gists', '/gists',
518 action='create', conditions={'method': ['POST']})
518 action='create', conditions={'method': ['POST']})
519 m.connect('gists', '/gists',
519 m.connect('gists', '/gists',
520 action='index', conditions={'method': ['GET']})
520 action='index', conditions={'method': ['GET']})
521 m.connect('new_gist', '/gists/new',
521 m.connect('new_gist', '/gists/new',
522 action='new', conditions={'method': ['GET']})
522 action='new', conditions={'method': ['GET']})
523
523
524 m.connect('/gists/{gist_id}',
524 m.connect('/gists/{gist_id}',
525 action='delete', conditions={'method': ['DELETE']})
525 action='delete', conditions={'method': ['DELETE']})
526 m.connect('edit_gist', '/gists/{gist_id}/edit',
526 m.connect('edit_gist', '/gists/{gist_id}/edit',
527 action='edit_form', conditions={'method': ['GET']})
527 action='edit_form', conditions={'method': ['GET']})
528 m.connect('edit_gist', '/gists/{gist_id}/edit',
528 m.connect('edit_gist', '/gists/{gist_id}/edit',
529 action='edit', conditions={'method': ['POST']})
529 action='edit', conditions={'method': ['POST']})
530 m.connect(
530 m.connect(
531 'edit_gist_check_revision', '/gists/{gist_id}/edit/check_revision',
531 'edit_gist_check_revision', '/gists/{gist_id}/edit/check_revision',
532 action='check_revision', conditions={'method': ['GET']})
532 action='check_revision', conditions={'method': ['GET']})
533
533
534 m.connect('gist', '/gists/{gist_id}',
534 m.connect('gist', '/gists/{gist_id}',
535 action='show', conditions={'method': ['GET']})
535 action='show', conditions={'method': ['GET']})
536 m.connect('gist_rev', '/gists/{gist_id}/{revision}',
536 m.connect('gist_rev', '/gists/{gist_id}/{revision}',
537 revision='tip',
537 revision='tip',
538 action='show', conditions={'method': ['GET']})
538 action='show', conditions={'method': ['GET']})
539 m.connect('formatted_gist', '/gists/{gist_id}/{revision}/{format}',
539 m.connect('formatted_gist', '/gists/{gist_id}/{revision}/{format}',
540 revision='tip',
540 revision='tip',
541 action='show', conditions={'method': ['GET']})
541 action='show', conditions={'method': ['GET']})
542 m.connect('formatted_gist_file', '/gists/{gist_id}/{revision}/{format}/{f_path}',
542 m.connect('formatted_gist_file', '/gists/{gist_id}/{revision}/{format}/{f_path}',
543 revision='tip',
543 revision='tip',
544 action='show', conditions={'method': ['GET']},
544 action='show', conditions={'method': ['GET']},
545 requirements=URL_NAME_REQUIREMENTS)
545 requirements=URL_NAME_REQUIREMENTS)
546
546
547 # ADMIN MAIN PAGES
547 # ADMIN MAIN PAGES
548 with rmap.submapper(path_prefix=ADMIN_PREFIX,
548 with rmap.submapper(path_prefix=ADMIN_PREFIX,
549 controller='admin/admin') as m:
549 controller='admin/admin') as m:
550 m.connect('admin_home', '', action='index')
550 m.connect('admin_home', '', action='index')
551 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
551 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
552 action='add_repo')
552 action='add_repo')
553 m.connect(
553 m.connect(
554 'pull_requests_global', '/pull_requests/{pull_request_id:[0-9]+}',
554 'pull_requests_global', '/pull_requests/{pull_request_id:[0-9]+}',
555 action='pull_requests')
555 action='pull_requests')
556
556
557 # USER JOURNAL
557 # USER JOURNAL
558 rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,),
558 rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,),
559 controller='journal', action='index')
559 controller='journal', action='index')
560 rmap.connect('journal_rss', '%s/journal/rss' % (ADMIN_PREFIX,),
560 rmap.connect('journal_rss', '%s/journal/rss' % (ADMIN_PREFIX,),
561 controller='journal', action='journal_rss')
561 controller='journal', action='journal_rss')
562 rmap.connect('journal_atom', '%s/journal/atom' % (ADMIN_PREFIX,),
562 rmap.connect('journal_atom', '%s/journal/atom' % (ADMIN_PREFIX,),
563 controller='journal', action='journal_atom')
563 controller='journal', action='journal_atom')
564
564
565 rmap.connect('public_journal', '%s/public_journal' % (ADMIN_PREFIX,),
565 rmap.connect('public_journal', '%s/public_journal' % (ADMIN_PREFIX,),
566 controller='journal', action='public_journal')
566 controller='journal', action='public_journal')
567
567
568 rmap.connect('public_journal_rss', '%s/public_journal/rss' % (ADMIN_PREFIX,),
568 rmap.connect('public_journal_rss', '%s/public_journal/rss' % (ADMIN_PREFIX,),
569 controller='journal', action='public_journal_rss')
569 controller='journal', action='public_journal_rss')
570
570
571 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % (ADMIN_PREFIX,),
571 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % (ADMIN_PREFIX,),
572 controller='journal', action='public_journal_rss')
572 controller='journal', action='public_journal_rss')
573
573
574 rmap.connect('public_journal_atom',
574 rmap.connect('public_journal_atom',
575 '%s/public_journal/atom' % (ADMIN_PREFIX,), controller='journal',
575 '%s/public_journal/atom' % (ADMIN_PREFIX,), controller='journal',
576 action='public_journal_atom')
576 action='public_journal_atom')
577
577
578 rmap.connect('public_journal_atom_old',
578 rmap.connect('public_journal_atom_old',
579 '%s/public_journal_atom' % (ADMIN_PREFIX,), controller='journal',
579 '%s/public_journal_atom' % (ADMIN_PREFIX,), controller='journal',
580 action='public_journal_atom')
580 action='public_journal_atom')
581
581
582 rmap.connect('toggle_following', '%s/toggle_following' % (ADMIN_PREFIX,),
582 rmap.connect('toggle_following', '%s/toggle_following' % (ADMIN_PREFIX,),
583 controller='journal', action='toggle_following',
583 controller='journal', action='toggle_following',
584 conditions={'method': ['POST']})
584 conditions={'method': ['POST']})
585
585
586 # FULL TEXT SEARCH
586 # FULL TEXT SEARCH
587 rmap.connect('search', '%s/search' % (ADMIN_PREFIX,),
587 rmap.connect('search', '%s/search' % (ADMIN_PREFIX,),
588 controller='search')
588 controller='search')
589 rmap.connect('search_repo_home', '/{repo_name}/search',
589 rmap.connect('search_repo_home', '/{repo_name}/search',
590 controller='search',
590 controller='search',
591 action='index',
591 action='index',
592 conditions={'function': check_repo},
592 conditions={'function': check_repo},
593 requirements=URL_NAME_REQUIREMENTS)
593 requirements=URL_NAME_REQUIREMENTS)
594
594
595 # FEEDS
595 # FEEDS
596 rmap.connect('rss_feed_home', '/{repo_name}/feed/rss',
596 rmap.connect('rss_feed_home', '/{repo_name}/feed/rss',
597 controller='feed', action='rss',
597 controller='feed', action='rss',
598 conditions={'function': check_repo},
598 conditions={'function': check_repo},
599 requirements=URL_NAME_REQUIREMENTS)
599 requirements=URL_NAME_REQUIREMENTS)
600
600
601 rmap.connect('atom_feed_home', '/{repo_name}/feed/atom',
601 rmap.connect('atom_feed_home', '/{repo_name}/feed/atom',
602 controller='feed', action='atom',
602 controller='feed', action='atom',
603 conditions={'function': check_repo},
603 conditions={'function': check_repo},
604 requirements=URL_NAME_REQUIREMENTS)
604 requirements=URL_NAME_REQUIREMENTS)
605
605
606 #==========================================================================
606 #==========================================================================
607 # REPOSITORY ROUTES
607 # REPOSITORY ROUTES
608 #==========================================================================
608 #==========================================================================
609
609
610 rmap.connect('repo_creating_home', '/{repo_name}/repo_creating',
610 rmap.connect('repo_creating_home', '/{repo_name}/repo_creating',
611 controller='admin/repos', action='repo_creating',
611 controller='admin/repos', action='repo_creating',
612 requirements=URL_NAME_REQUIREMENTS)
612 requirements=URL_NAME_REQUIREMENTS)
613 rmap.connect('repo_check_home', '/{repo_name}/crepo_check',
613 rmap.connect('repo_check_home', '/{repo_name}/crepo_check',
614 controller='admin/repos', action='repo_check',
614 controller='admin/repos', action='repo_check',
615 requirements=URL_NAME_REQUIREMENTS)
615 requirements=URL_NAME_REQUIREMENTS)
616
616
617 rmap.connect('repo_stats', '/{repo_name}/repo_stats/{commit_id}',
617 rmap.connect('repo_stats', '/{repo_name}/repo_stats/{commit_id}',
618 controller='summary', action='repo_stats',
618 controller='summary', action='repo_stats',
619 conditions={'function': check_repo},
619 conditions={'function': check_repo},
620 requirements=URL_NAME_REQUIREMENTS)
620 requirements=URL_NAME_REQUIREMENTS)
621
621
622 rmap.connect('repo_refs_data', '/{repo_name}/refs-data',
622 rmap.connect('repo_refs_data', '/{repo_name}/refs-data',
623 controller='summary', action='repo_refs_data',
623 controller='summary', action='repo_refs_data',
624 requirements=URL_NAME_REQUIREMENTS)
624 requirements=URL_NAME_REQUIREMENTS)
625 rmap.connect('repo_refs_changelog_data', '/{repo_name}/refs-data-changelog',
625 rmap.connect('repo_refs_changelog_data', '/{repo_name}/refs-data-changelog',
626 controller='summary', action='repo_refs_changelog_data',
626 controller='summary', action='repo_refs_changelog_data',
627 requirements=URL_NAME_REQUIREMENTS)
627 requirements=URL_NAME_REQUIREMENTS)
628
628
629 rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}',
629 rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}',
630 controller='changeset', revision='tip',
630 controller='changeset', revision='tip',
631 conditions={'function': check_repo},
631 conditions={'function': check_repo},
632 requirements=URL_NAME_REQUIREMENTS)
632 requirements=URL_NAME_REQUIREMENTS)
633 rmap.connect('changeset_children', '/{repo_name}/changeset_children/{revision}',
633 rmap.connect('changeset_children', '/{repo_name}/changeset_children/{revision}',
634 controller='changeset', revision='tip', action='changeset_children',
634 controller='changeset', revision='tip', action='changeset_children',
635 conditions={'function': check_repo},
635 conditions={'function': check_repo},
636 requirements=URL_NAME_REQUIREMENTS)
636 requirements=URL_NAME_REQUIREMENTS)
637 rmap.connect('changeset_parents', '/{repo_name}/changeset_parents/{revision}',
637 rmap.connect('changeset_parents', '/{repo_name}/changeset_parents/{revision}',
638 controller='changeset', revision='tip', action='changeset_parents',
638 controller='changeset', revision='tip', action='changeset_parents',
639 conditions={'function': check_repo},
639 conditions={'function': check_repo},
640 requirements=URL_NAME_REQUIREMENTS)
640 requirements=URL_NAME_REQUIREMENTS)
641
641
642 # repo edit options
642 # repo edit options
643 rmap.connect('edit_repo', '/{repo_name}/settings',
643 rmap.connect('edit_repo', '/{repo_name}/settings',
644 controller='admin/repos', action='edit',
644 controller='admin/repos', action='edit',
645 conditions={'method': ['GET'], 'function': check_repo},
645 conditions={'method': ['GET'], 'function': check_repo},
646 requirements=URL_NAME_REQUIREMENTS)
646 requirements=URL_NAME_REQUIREMENTS)
647
647
648 rmap.connect('edit_repo_perms', '/{repo_name}/settings/permissions',
648 rmap.connect('edit_repo_perms', '/{repo_name}/settings/permissions',
649 controller='admin/repos', action='edit_permissions',
649 controller='admin/repos', action='edit_permissions',
650 conditions={'method': ['GET'], 'function': check_repo},
650 conditions={'method': ['GET'], 'function': check_repo},
651 requirements=URL_NAME_REQUIREMENTS)
651 requirements=URL_NAME_REQUIREMENTS)
652 rmap.connect('edit_repo_perms_update', '/{repo_name}/settings/permissions',
652 rmap.connect('edit_repo_perms_update', '/{repo_name}/settings/permissions',
653 controller='admin/repos', action='edit_permissions_update',
653 controller='admin/repos', action='edit_permissions_update',
654 conditions={'method': ['PUT'], 'function': check_repo},
654 conditions={'method': ['PUT'], 'function': check_repo},
655 requirements=URL_NAME_REQUIREMENTS)
655 requirements=URL_NAME_REQUIREMENTS)
656
656
657 rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields',
657 rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields',
658 controller='admin/repos', action='edit_fields',
658 controller='admin/repos', action='edit_fields',
659 conditions={'method': ['GET'], 'function': check_repo},
659 conditions={'method': ['GET'], 'function': check_repo},
660 requirements=URL_NAME_REQUIREMENTS)
660 requirements=URL_NAME_REQUIREMENTS)
661 rmap.connect('create_repo_fields', '/{repo_name}/settings/fields/new',
661 rmap.connect('create_repo_fields', '/{repo_name}/settings/fields/new',
662 controller='admin/repos', action='create_repo_field',
662 controller='admin/repos', action='create_repo_field',
663 conditions={'method': ['PUT'], 'function': check_repo},
663 conditions={'method': ['PUT'], 'function': check_repo},
664 requirements=URL_NAME_REQUIREMENTS)
664 requirements=URL_NAME_REQUIREMENTS)
665 rmap.connect('delete_repo_fields', '/{repo_name}/settings/fields/{field_id}',
665 rmap.connect('delete_repo_fields', '/{repo_name}/settings/fields/{field_id}',
666 controller='admin/repos', action='delete_repo_field',
666 controller='admin/repos', action='delete_repo_field',
667 conditions={'method': ['DELETE'], 'function': check_repo},
667 conditions={'method': ['DELETE'], 'function': check_repo},
668 requirements=URL_NAME_REQUIREMENTS)
668 requirements=URL_NAME_REQUIREMENTS)
669
669
670 rmap.connect('edit_repo_advanced', '/{repo_name}/settings/advanced',
670 rmap.connect('edit_repo_advanced', '/{repo_name}/settings/advanced',
671 controller='admin/repos', action='edit_advanced',
671 controller='admin/repos', action='edit_advanced',
672 conditions={'method': ['GET'], 'function': check_repo},
672 conditions={'method': ['GET'], 'function': check_repo},
673 requirements=URL_NAME_REQUIREMENTS)
673 requirements=URL_NAME_REQUIREMENTS)
674
674
675 rmap.connect('edit_repo_advanced_locking', '/{repo_name}/settings/advanced/locking',
675 rmap.connect('edit_repo_advanced_locking', '/{repo_name}/settings/advanced/locking',
676 controller='admin/repos', action='edit_advanced_locking',
676 controller='admin/repos', action='edit_advanced_locking',
677 conditions={'method': ['PUT'], 'function': check_repo},
677 conditions={'method': ['PUT'], 'function': check_repo},
678 requirements=URL_NAME_REQUIREMENTS)
678 requirements=URL_NAME_REQUIREMENTS)
679 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
679 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
680 controller='admin/repos', action='toggle_locking',
680 controller='admin/repos', action='toggle_locking',
681 conditions={'method': ['GET'], 'function': check_repo},
681 conditions={'method': ['GET'], 'function': check_repo},
682 requirements=URL_NAME_REQUIREMENTS)
682 requirements=URL_NAME_REQUIREMENTS)
683
683
684 rmap.connect('edit_repo_advanced_journal', '/{repo_name}/settings/advanced/journal',
684 rmap.connect('edit_repo_advanced_journal', '/{repo_name}/settings/advanced/journal',
685 controller='admin/repos', action='edit_advanced_journal',
685 controller='admin/repos', action='edit_advanced_journal',
686 conditions={'method': ['PUT'], 'function': check_repo},
686 conditions={'method': ['PUT'], 'function': check_repo},
687 requirements=URL_NAME_REQUIREMENTS)
687 requirements=URL_NAME_REQUIREMENTS)
688
688
689 rmap.connect('edit_repo_advanced_fork', '/{repo_name}/settings/advanced/fork',
689 rmap.connect('edit_repo_advanced_fork', '/{repo_name}/settings/advanced/fork',
690 controller='admin/repos', action='edit_advanced_fork',
690 controller='admin/repos', action='edit_advanced_fork',
691 conditions={'method': ['PUT'], 'function': check_repo},
691 conditions={'method': ['PUT'], 'function': check_repo},
692 requirements=URL_NAME_REQUIREMENTS)
692 requirements=URL_NAME_REQUIREMENTS)
693
693
694 rmap.connect('edit_repo_caches', '/{repo_name}/settings/caches',
694 rmap.connect('edit_repo_caches', '/{repo_name}/settings/caches',
695 controller='admin/repos', action='edit_caches_form',
695 controller='admin/repos', action='edit_caches_form',
696 conditions={'method': ['GET'], 'function': check_repo},
696 conditions={'method': ['GET'], 'function': check_repo},
697 requirements=URL_NAME_REQUIREMENTS)
697 requirements=URL_NAME_REQUIREMENTS)
698 rmap.connect('edit_repo_caches', '/{repo_name}/settings/caches',
698 rmap.connect('edit_repo_caches', '/{repo_name}/settings/caches',
699 controller='admin/repos', action='edit_caches',
699 controller='admin/repos', action='edit_caches',
700 conditions={'method': ['PUT'], 'function': check_repo},
700 conditions={'method': ['PUT'], 'function': check_repo},
701 requirements=URL_NAME_REQUIREMENTS)
701 requirements=URL_NAME_REQUIREMENTS)
702
702
703 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
703 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
704 controller='admin/repos', action='edit_remote_form',
704 controller='admin/repos', action='edit_remote_form',
705 conditions={'method': ['GET'], 'function': check_repo},
705 conditions={'method': ['GET'], 'function': check_repo},
706 requirements=URL_NAME_REQUIREMENTS)
706 requirements=URL_NAME_REQUIREMENTS)
707 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
707 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
708 controller='admin/repos', action='edit_remote',
708 controller='admin/repos', action='edit_remote',
709 conditions={'method': ['PUT'], 'function': check_repo},
709 conditions={'method': ['PUT'], 'function': check_repo},
710 requirements=URL_NAME_REQUIREMENTS)
710 requirements=URL_NAME_REQUIREMENTS)
711
711
712 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
712 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
713 controller='admin/repos', action='edit_statistics_form',
713 controller='admin/repos', action='edit_statistics_form',
714 conditions={'method': ['GET'], 'function': check_repo},
714 conditions={'method': ['GET'], 'function': check_repo},
715 requirements=URL_NAME_REQUIREMENTS)
715 requirements=URL_NAME_REQUIREMENTS)
716 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
716 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
717 controller='admin/repos', action='edit_statistics',
717 controller='admin/repos', action='edit_statistics',
718 conditions={'method': ['PUT'], 'function': check_repo},
718 conditions={'method': ['PUT'], 'function': check_repo},
719 requirements=URL_NAME_REQUIREMENTS)
719 requirements=URL_NAME_REQUIREMENTS)
720 rmap.connect('repo_settings_issuetracker',
720 rmap.connect('repo_settings_issuetracker',
721 '/{repo_name}/settings/issue-tracker',
721 '/{repo_name}/settings/issue-tracker',
722 controller='admin/repos', action='repo_issuetracker',
722 controller='admin/repos', action='repo_issuetracker',
723 conditions={'method': ['GET'], 'function': check_repo},
723 conditions={'method': ['GET'], 'function': check_repo},
724 requirements=URL_NAME_REQUIREMENTS)
724 requirements=URL_NAME_REQUIREMENTS)
725 rmap.connect('repo_issuetracker_test',
725 rmap.connect('repo_issuetracker_test',
726 '/{repo_name}/settings/issue-tracker/test',
726 '/{repo_name}/settings/issue-tracker/test',
727 controller='admin/repos', action='repo_issuetracker_test',
727 controller='admin/repos', action='repo_issuetracker_test',
728 conditions={'method': ['POST'], 'function': check_repo},
728 conditions={'method': ['POST'], 'function': check_repo},
729 requirements=URL_NAME_REQUIREMENTS)
729 requirements=URL_NAME_REQUIREMENTS)
730 rmap.connect('repo_issuetracker_delete',
730 rmap.connect('repo_issuetracker_delete',
731 '/{repo_name}/settings/issue-tracker/delete',
731 '/{repo_name}/settings/issue-tracker/delete',
732 controller='admin/repos', action='repo_issuetracker_delete',
732 controller='admin/repos', action='repo_issuetracker_delete',
733 conditions={'method': ['DELETE'], 'function': check_repo},
733 conditions={'method': ['DELETE'], 'function': check_repo},
734 requirements=URL_NAME_REQUIREMENTS)
734 requirements=URL_NAME_REQUIREMENTS)
735 rmap.connect('repo_issuetracker_save',
735 rmap.connect('repo_issuetracker_save',
736 '/{repo_name}/settings/issue-tracker/save',
736 '/{repo_name}/settings/issue-tracker/save',
737 controller='admin/repos', action='repo_issuetracker_save',
737 controller='admin/repos', action='repo_issuetracker_save',
738 conditions={'method': ['POST'], 'function': check_repo},
738 conditions={'method': ['POST'], 'function': check_repo},
739 requirements=URL_NAME_REQUIREMENTS)
739 requirements=URL_NAME_REQUIREMENTS)
740 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
740 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
741 controller='admin/repos', action='repo_settings_vcs_update',
741 controller='admin/repos', action='repo_settings_vcs_update',
742 conditions={'method': ['POST'], 'function': check_repo},
742 conditions={'method': ['POST'], 'function': check_repo},
743 requirements=URL_NAME_REQUIREMENTS)
743 requirements=URL_NAME_REQUIREMENTS)
744 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
744 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
745 controller='admin/repos', action='repo_settings_vcs',
745 controller='admin/repos', action='repo_settings_vcs',
746 conditions={'method': ['GET'], 'function': check_repo},
746 conditions={'method': ['GET'], 'function': check_repo},
747 requirements=URL_NAME_REQUIREMENTS)
747 requirements=URL_NAME_REQUIREMENTS)
748 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
748 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
749 controller='admin/repos', action='repo_delete_svn_pattern',
749 controller='admin/repos', action='repo_delete_svn_pattern',
750 conditions={'method': ['DELETE'], 'function': check_repo},
750 conditions={'method': ['DELETE'], 'function': check_repo},
751 requirements=URL_NAME_REQUIREMENTS)
751 requirements=URL_NAME_REQUIREMENTS)
752
752
753 # still working url for backward compat.
753 # still working url for backward compat.
754 rmap.connect('raw_changeset_home_depraced',
754 rmap.connect('raw_changeset_home_depraced',
755 '/{repo_name}/raw-changeset/{revision}',
755 '/{repo_name}/raw-changeset/{revision}',
756 controller='changeset', action='changeset_raw',
756 controller='changeset', action='changeset_raw',
757 revision='tip', conditions={'function': check_repo},
757 revision='tip', conditions={'function': check_repo},
758 requirements=URL_NAME_REQUIREMENTS)
758 requirements=URL_NAME_REQUIREMENTS)
759
759
760 # new URLs
760 # new URLs
761 rmap.connect('changeset_raw_home',
761 rmap.connect('changeset_raw_home',
762 '/{repo_name}/changeset-diff/{revision}',
762 '/{repo_name}/changeset-diff/{revision}',
763 controller='changeset', action='changeset_raw',
763 controller='changeset', action='changeset_raw',
764 revision='tip', conditions={'function': check_repo},
764 revision='tip', conditions={'function': check_repo},
765 requirements=URL_NAME_REQUIREMENTS)
765 requirements=URL_NAME_REQUIREMENTS)
766
766
767 rmap.connect('changeset_patch_home',
767 rmap.connect('changeset_patch_home',
768 '/{repo_name}/changeset-patch/{revision}',
768 '/{repo_name}/changeset-patch/{revision}',
769 controller='changeset', action='changeset_patch',
769 controller='changeset', action='changeset_patch',
770 revision='tip', conditions={'function': check_repo},
770 revision='tip', conditions={'function': check_repo},
771 requirements=URL_NAME_REQUIREMENTS)
771 requirements=URL_NAME_REQUIREMENTS)
772
772
773 rmap.connect('changeset_download_home',
773 rmap.connect('changeset_download_home',
774 '/{repo_name}/changeset-download/{revision}',
774 '/{repo_name}/changeset-download/{revision}',
775 controller='changeset', action='changeset_download',
775 controller='changeset', action='changeset_download',
776 revision='tip', conditions={'function': check_repo},
776 revision='tip', conditions={'function': check_repo},
777 requirements=URL_NAME_REQUIREMENTS)
777 requirements=URL_NAME_REQUIREMENTS)
778
778
779 rmap.connect('changeset_comment',
779 rmap.connect('changeset_comment',
780 '/{repo_name}/changeset/{revision}/comment',
780 '/{repo_name}/changeset/{revision}/comment',
781 controller='changeset', revision='tip', action='comment',
781 controller='changeset', revision='tip', action='comment',
782 conditions={'function': check_repo},
782 conditions={'function': check_repo},
783 requirements=URL_NAME_REQUIREMENTS)
783 requirements=URL_NAME_REQUIREMENTS)
784
784
785 rmap.connect('changeset_comment_preview',
785 rmap.connect('changeset_comment_preview',
786 '/{repo_name}/changeset/comment/preview',
786 '/{repo_name}/changeset/comment/preview',
787 controller='changeset', action='preview_comment',
787 controller='changeset', action='preview_comment',
788 conditions={'function': check_repo, 'method': ['POST']},
788 conditions={'function': check_repo, 'method': ['POST']},
789 requirements=URL_NAME_REQUIREMENTS)
789 requirements=URL_NAME_REQUIREMENTS)
790
790
791 rmap.connect('changeset_comment_delete',
791 rmap.connect('changeset_comment_delete',
792 '/{repo_name}/changeset/comment/{comment_id}/delete',
792 '/{repo_name}/changeset/comment/{comment_id}/delete',
793 controller='changeset', action='delete_comment',
793 controller='changeset', action='delete_comment',
794 conditions={'function': check_repo, 'method': ['DELETE']},
794 conditions={'function': check_repo, 'method': ['DELETE']},
795 requirements=URL_NAME_REQUIREMENTS)
795 requirements=URL_NAME_REQUIREMENTS)
796
796
797 rmap.connect('changeset_info', '/changeset_info/{repo_name}/{revision}',
797 rmap.connect('changeset_info', '/changeset_info/{repo_name}/{revision}',
798 controller='changeset', action='changeset_info',
798 controller='changeset', action='changeset_info',
799 requirements=URL_NAME_REQUIREMENTS)
799 requirements=URL_NAME_REQUIREMENTS)
800
800
801 rmap.connect('compare_home',
801 rmap.connect('compare_home',
802 '/{repo_name}/compare',
802 '/{repo_name}/compare',
803 controller='compare', action='index',
803 controller='compare', action='index',
804 conditions={'function': check_repo},
804 conditions={'function': check_repo},
805 requirements=URL_NAME_REQUIREMENTS)
805 requirements=URL_NAME_REQUIREMENTS)
806
806
807 rmap.connect('compare_url',
807 rmap.connect('compare_url',
808 '/{repo_name}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}',
808 '/{repo_name}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}',
809 controller='compare', action='compare',
809 controller='compare', action='compare',
810 conditions={'function': check_repo},
810 conditions={'function': check_repo},
811 requirements=URL_NAME_REQUIREMENTS)
811 requirements=URL_NAME_REQUIREMENTS)
812
812
813 rmap.connect('pullrequest_home',
813 rmap.connect('pullrequest_home',
814 '/{repo_name}/pull-request/new', controller='pullrequests',
814 '/{repo_name}/pull-request/new', controller='pullrequests',
815 action='index', conditions={'function': check_repo,
815 action='index', conditions={'function': check_repo,
816 'method': ['GET']},
816 'method': ['GET']},
817 requirements=URL_NAME_REQUIREMENTS)
817 requirements=URL_NAME_REQUIREMENTS)
818
818
819 rmap.connect('pullrequest',
819 rmap.connect('pullrequest',
820 '/{repo_name}/pull-request/new', controller='pullrequests',
820 '/{repo_name}/pull-request/new', controller='pullrequests',
821 action='create', conditions={'function': check_repo,
821 action='create', conditions={'function': check_repo,
822 'method': ['POST']},
822 'method': ['POST']},
823 requirements=URL_NAME_REQUIREMENTS)
823 requirements=URL_NAME_REQUIREMENTS)
824
824
825 rmap.connect('pullrequest_repo_refs',
825 rmap.connect('pullrequest_repo_refs',
826 '/{repo_name}/pull-request/refs/{target_repo_name:.*?[^/]}',
826 '/{repo_name}/pull-request/refs/{target_repo_name:.*?[^/]}',
827 controller='pullrequests',
827 controller='pullrequests',
828 action='get_repo_refs',
828 action='get_repo_refs',
829 conditions={'function': check_repo, 'method': ['GET']},
829 conditions={'function': check_repo, 'method': ['GET']},
830 requirements=URL_NAME_REQUIREMENTS)
830 requirements=URL_NAME_REQUIREMENTS)
831
831
832 rmap.connect('pullrequest_repo_destinations',
832 rmap.connect('pullrequest_repo_destinations',
833 '/{repo_name}/pull-request/repo-destinations',
833 '/{repo_name}/pull-request/repo-destinations',
834 controller='pullrequests',
834 controller='pullrequests',
835 action='get_repo_destinations',
835 action='get_repo_destinations',
836 conditions={'function': check_repo, 'method': ['GET']},
836 conditions={'function': check_repo, 'method': ['GET']},
837 requirements=URL_NAME_REQUIREMENTS)
837 requirements=URL_NAME_REQUIREMENTS)
838
838
839 rmap.connect('pullrequest_show',
839 rmap.connect('pullrequest_show',
840 '/{repo_name}/pull-request/{pull_request_id}',
840 '/{repo_name}/pull-request/{pull_request_id}',
841 controller='pullrequests',
841 controller='pullrequests',
842 action='show', conditions={'function': check_repo,
842 action='show', conditions={'function': check_repo,
843 'method': ['GET']},
843 'method': ['GET']},
844 requirements=URL_NAME_REQUIREMENTS)
844 requirements=URL_NAME_REQUIREMENTS)
845
845
846 rmap.connect('pullrequest_update',
846 rmap.connect('pullrequest_update',
847 '/{repo_name}/pull-request/{pull_request_id}',
847 '/{repo_name}/pull-request/{pull_request_id}',
848 controller='pullrequests',
848 controller='pullrequests',
849 action='update', conditions={'function': check_repo,
849 action='update', conditions={'function': check_repo,
850 'method': ['PUT']},
850 'method': ['PUT']},
851 requirements=URL_NAME_REQUIREMENTS)
851 requirements=URL_NAME_REQUIREMENTS)
852
852
853 rmap.connect('pullrequest_merge',
853 rmap.connect('pullrequest_merge',
854 '/{repo_name}/pull-request/{pull_request_id}',
854 '/{repo_name}/pull-request/{pull_request_id}',
855 controller='pullrequests',
855 controller='pullrequests',
856 action='merge', conditions={'function': check_repo,
856 action='merge', conditions={'function': check_repo,
857 'method': ['POST']},
857 'method': ['POST']},
858 requirements=URL_NAME_REQUIREMENTS)
858 requirements=URL_NAME_REQUIREMENTS)
859
859
860 rmap.connect('pullrequest_delete',
860 rmap.connect('pullrequest_delete',
861 '/{repo_name}/pull-request/{pull_request_id}',
861 '/{repo_name}/pull-request/{pull_request_id}',
862 controller='pullrequests',
862 controller='pullrequests',
863 action='delete', conditions={'function': check_repo,
863 action='delete', conditions={'function': check_repo,
864 'method': ['DELETE']},
864 'method': ['DELETE']},
865 requirements=URL_NAME_REQUIREMENTS)
865 requirements=URL_NAME_REQUIREMENTS)
866
866
867 rmap.connect('pullrequest_show_all',
867 rmap.connect('pullrequest_show_all',
868 '/{repo_name}/pull-request',
868 '/{repo_name}/pull-request',
869 controller='pullrequests',
869 controller='pullrequests',
870 action='show_all', conditions={'function': check_repo,
870 action='show_all', conditions={'function': check_repo,
871 'method': ['GET']},
871 'method': ['GET']},
872 requirements=URL_NAME_REQUIREMENTS)
872 requirements=URL_NAME_REQUIREMENTS)
873
873
874 rmap.connect('pullrequest_comment',
874 rmap.connect('pullrequest_comment',
875 '/{repo_name}/pull-request-comment/{pull_request_id}',
875 '/{repo_name}/pull-request-comment/{pull_request_id}',
876 controller='pullrequests',
876 controller='pullrequests',
877 action='comment', conditions={'function': check_repo,
877 action='comment', conditions={'function': check_repo,
878 'method': ['POST']},
878 'method': ['POST']},
879 requirements=URL_NAME_REQUIREMENTS)
879 requirements=URL_NAME_REQUIREMENTS)
880
880
881 rmap.connect('pullrequest_comment_delete',
881 rmap.connect('pullrequest_comment_delete',
882 '/{repo_name}/pull-request-comment/{comment_id}/delete',
882 '/{repo_name}/pull-request-comment/{comment_id}/delete',
883 controller='pullrequests', action='delete_comment',
883 controller='pullrequests', action='delete_comment',
884 conditions={'function': check_repo, 'method': ['DELETE']},
884 conditions={'function': check_repo, 'method': ['DELETE']},
885 requirements=URL_NAME_REQUIREMENTS)
885 requirements=URL_NAME_REQUIREMENTS)
886
886
887 rmap.connect('summary_home_explicit', '/{repo_name}/summary',
887 rmap.connect('summary_home_explicit', '/{repo_name}/summary',
888 controller='summary', conditions={'function': check_repo},
888 controller='summary', conditions={'function': check_repo},
889 requirements=URL_NAME_REQUIREMENTS)
889 requirements=URL_NAME_REQUIREMENTS)
890
890
891 rmap.connect('branches_home', '/{repo_name}/branches',
891 rmap.connect('branches_home', '/{repo_name}/branches',
892 controller='branches', conditions={'function': check_repo},
892 controller='branches', conditions={'function': check_repo},
893 requirements=URL_NAME_REQUIREMENTS)
893 requirements=URL_NAME_REQUIREMENTS)
894
894
895 rmap.connect('tags_home', '/{repo_name}/tags',
895 rmap.connect('tags_home', '/{repo_name}/tags',
896 controller='tags', conditions={'function': check_repo},
896 controller='tags', conditions={'function': check_repo},
897 requirements=URL_NAME_REQUIREMENTS)
897 requirements=URL_NAME_REQUIREMENTS)
898
898
899 rmap.connect('bookmarks_home', '/{repo_name}/bookmarks',
899 rmap.connect('bookmarks_home', '/{repo_name}/bookmarks',
900 controller='bookmarks', conditions={'function': check_repo},
900 controller='bookmarks', conditions={'function': check_repo},
901 requirements=URL_NAME_REQUIREMENTS)
901 requirements=URL_NAME_REQUIREMENTS)
902
902
903 rmap.connect('changelog_home', '/{repo_name}/changelog',
903 rmap.connect('changelog_home', '/{repo_name}/changelog',
904 controller='changelog', conditions={'function': check_repo},
904 controller='changelog', conditions={'function': check_repo},
905 requirements=URL_NAME_REQUIREMENTS)
905 requirements=URL_NAME_REQUIREMENTS)
906
906
907 rmap.connect('changelog_summary_home', '/{repo_name}/changelog_summary',
907 rmap.connect('changelog_summary_home', '/{repo_name}/changelog_summary',
908 controller='changelog', action='changelog_summary',
908 controller='changelog', action='changelog_summary',
909 conditions={'function': check_repo},
909 conditions={'function': check_repo},
910 requirements=URL_NAME_REQUIREMENTS)
910 requirements=URL_NAME_REQUIREMENTS)
911
911
912 rmap.connect('changelog_file_home', '/{repo_name}/changelog/{revision}/{f_path}',
912 rmap.connect('changelog_file_home', '/{repo_name}/changelog/{revision}/{f_path}',
913 controller='changelog', f_path=None,
913 controller='changelog', f_path=None,
914 conditions={'function': check_repo},
914 conditions={'function': check_repo},
915 requirements=URL_NAME_REQUIREMENTS)
915 requirements=URL_NAME_REQUIREMENTS)
916
916
917 rmap.connect('changelog_details', '/{repo_name}/changelog_details/{cs}',
917 rmap.connect('changelog_details', '/{repo_name}/changelog_details/{cs}',
918 controller='changelog', action='changelog_details',
918 controller='changelog', action='changelog_details',
919 conditions={'function': check_repo},
919 conditions={'function': check_repo},
920 requirements=URL_NAME_REQUIREMENTS)
920 requirements=URL_NAME_REQUIREMENTS)
921
921
922 rmap.connect('files_home',
922 rmap.connect('files_home',
923 '/{repo_name}/files/{revision}/{f_path}',
923 '/{repo_name}/files/{revision}/{f_path}',
924 controller='files', revision='tip', f_path='',
924 controller='files', revision='tip', f_path='',
925 conditions={'function': check_repo},
925 conditions={'function': check_repo},
926 requirements=URL_NAME_REQUIREMENTS)
926 requirements=URL_NAME_REQUIREMENTS)
927
927
928 rmap.connect('files_home_simple_catchrev',
928 rmap.connect('files_home_simple_catchrev',
929 '/{repo_name}/files/{revision}',
929 '/{repo_name}/files/{revision}',
930 controller='files', revision='tip', f_path='',
930 controller='files', revision='tip', f_path='',
931 conditions={'function': check_repo},
931 conditions={'function': check_repo},
932 requirements=URL_NAME_REQUIREMENTS)
932 requirements=URL_NAME_REQUIREMENTS)
933
933
934 rmap.connect('files_home_simple_catchall',
934 rmap.connect('files_home_simple_catchall',
935 '/{repo_name}/files',
935 '/{repo_name}/files',
936 controller='files', revision='tip', f_path='',
936 controller='files', revision='tip', f_path='',
937 conditions={'function': check_repo},
937 conditions={'function': check_repo},
938 requirements=URL_NAME_REQUIREMENTS)
938 requirements=URL_NAME_REQUIREMENTS)
939
939
940 rmap.connect('files_history_home',
940 rmap.connect('files_history_home',
941 '/{repo_name}/history/{revision}/{f_path}',
941 '/{repo_name}/history/{revision}/{f_path}',
942 controller='files', action='history', revision='tip', f_path='',
942 controller='files', action='history', revision='tip', f_path='',
943 conditions={'function': check_repo},
943 conditions={'function': check_repo},
944 requirements=URL_NAME_REQUIREMENTS)
944 requirements=URL_NAME_REQUIREMENTS)
945
945
946 rmap.connect('files_authors_home',
946 rmap.connect('files_authors_home',
947 '/{repo_name}/authors/{revision}/{f_path}',
947 '/{repo_name}/authors/{revision}/{f_path}',
948 controller='files', action='authors', revision='tip', f_path='',
948 controller='files', action='authors', revision='tip', f_path='',
949 conditions={'function': check_repo},
949 conditions={'function': check_repo},
950 requirements=URL_NAME_REQUIREMENTS)
950 requirements=URL_NAME_REQUIREMENTS)
951
951
952 rmap.connect('files_diff_home', '/{repo_name}/diff/{f_path}',
952 rmap.connect('files_diff_home', '/{repo_name}/diff/{f_path}',
953 controller='files', action='diff', f_path='',
953 controller='files', action='diff', f_path='',
954 conditions={'function': check_repo},
954 conditions={'function': check_repo},
955 requirements=URL_NAME_REQUIREMENTS)
955 requirements=URL_NAME_REQUIREMENTS)
956
956
957 rmap.connect('files_diff_2way_home',
957 rmap.connect('files_diff_2way_home',
958 '/{repo_name}/diff-2way/{f_path}',
958 '/{repo_name}/diff-2way/{f_path}',
959 controller='files', action='diff_2way', f_path='',
959 controller='files', action='diff_2way', f_path='',
960 conditions={'function': check_repo},
960 conditions={'function': check_repo},
961 requirements=URL_NAME_REQUIREMENTS)
961 requirements=URL_NAME_REQUIREMENTS)
962
962
963 rmap.connect('files_rawfile_home',
963 rmap.connect('files_rawfile_home',
964 '/{repo_name}/rawfile/{revision}/{f_path}',
964 '/{repo_name}/rawfile/{revision}/{f_path}',
965 controller='files', action='rawfile', revision='tip',
965 controller='files', action='rawfile', revision='tip',
966 f_path='', conditions={'function': check_repo},
966 f_path='', conditions={'function': check_repo},
967 requirements=URL_NAME_REQUIREMENTS)
967 requirements=URL_NAME_REQUIREMENTS)
968
968
969 rmap.connect('files_raw_home',
969 rmap.connect('files_raw_home',
970 '/{repo_name}/raw/{revision}/{f_path}',
970 '/{repo_name}/raw/{revision}/{f_path}',
971 controller='files', action='raw', revision='tip', f_path='',
971 controller='files', action='raw', revision='tip', f_path='',
972 conditions={'function': check_repo},
972 conditions={'function': check_repo},
973 requirements=URL_NAME_REQUIREMENTS)
973 requirements=URL_NAME_REQUIREMENTS)
974
974
975 rmap.connect('files_render_home',
975 rmap.connect('files_render_home',
976 '/{repo_name}/render/{revision}/{f_path}',
976 '/{repo_name}/render/{revision}/{f_path}',
977 controller='files', action='index', revision='tip', f_path='',
977 controller='files', action='index', revision='tip', f_path='',
978 rendered=True, conditions={'function': check_repo},
978 rendered=True, conditions={'function': check_repo},
979 requirements=URL_NAME_REQUIREMENTS)
979 requirements=URL_NAME_REQUIREMENTS)
980
980
981 rmap.connect('files_annotate_home',
981 rmap.connect('files_annotate_home',
982 '/{repo_name}/annotate/{revision}/{f_path}',
982 '/{repo_name}/annotate/{revision}/{f_path}',
983 controller='files', action='index', revision='tip',
983 controller='files', action='index', revision='tip',
984 f_path='', annotate=True, conditions={'function': check_repo},
984 f_path='', annotate=True, conditions={'function': check_repo},
985 requirements=URL_NAME_REQUIREMENTS)
985 requirements=URL_NAME_REQUIREMENTS)
986
986
987 rmap.connect('files_edit',
987 rmap.connect('files_edit',
988 '/{repo_name}/edit/{revision}/{f_path}',
988 '/{repo_name}/edit/{revision}/{f_path}',
989 controller='files', action='edit', revision='tip',
989 controller='files', action='edit', revision='tip',
990 f_path='',
990 f_path='',
991 conditions={'function': check_repo, 'method': ['POST']},
991 conditions={'function': check_repo, 'method': ['POST']},
992 requirements=URL_NAME_REQUIREMENTS)
992 requirements=URL_NAME_REQUIREMENTS)
993
993
994 rmap.connect('files_edit_home',
994 rmap.connect('files_edit_home',
995 '/{repo_name}/edit/{revision}/{f_path}',
995 '/{repo_name}/edit/{revision}/{f_path}',
996 controller='files', action='edit_home', revision='tip',
996 controller='files', action='edit_home', revision='tip',
997 f_path='', conditions={'function': check_repo},
997 f_path='', conditions={'function': check_repo},
998 requirements=URL_NAME_REQUIREMENTS)
998 requirements=URL_NAME_REQUIREMENTS)
999
999
1000 rmap.connect('files_add',
1000 rmap.connect('files_add',
1001 '/{repo_name}/add/{revision}/{f_path}',
1001 '/{repo_name}/add/{revision}/{f_path}',
1002 controller='files', action='add', revision='tip',
1002 controller='files', action='add', revision='tip',
1003 f_path='',
1003 f_path='',
1004 conditions={'function': check_repo, 'method': ['POST']},
1004 conditions={'function': check_repo, 'method': ['POST']},
1005 requirements=URL_NAME_REQUIREMENTS)
1005 requirements=URL_NAME_REQUIREMENTS)
1006
1006
1007 rmap.connect('files_add_home',
1007 rmap.connect('files_add_home',
1008 '/{repo_name}/add/{revision}/{f_path}',
1008 '/{repo_name}/add/{revision}/{f_path}',
1009 controller='files', action='add_home', revision='tip',
1009 controller='files', action='add_home', revision='tip',
1010 f_path='', conditions={'function': check_repo},
1010 f_path='', conditions={'function': check_repo},
1011 requirements=URL_NAME_REQUIREMENTS)
1011 requirements=URL_NAME_REQUIREMENTS)
1012
1012
1013 rmap.connect('files_delete',
1013 rmap.connect('files_delete',
1014 '/{repo_name}/delete/{revision}/{f_path}',
1014 '/{repo_name}/delete/{revision}/{f_path}',
1015 controller='files', action='delete', revision='tip',
1015 controller='files', action='delete', revision='tip',
1016 f_path='',
1016 f_path='',
1017 conditions={'function': check_repo, 'method': ['POST']},
1017 conditions={'function': check_repo, 'method': ['POST']},
1018 requirements=URL_NAME_REQUIREMENTS)
1018 requirements=URL_NAME_REQUIREMENTS)
1019
1019
1020 rmap.connect('files_delete_home',
1020 rmap.connect('files_delete_home',
1021 '/{repo_name}/delete/{revision}/{f_path}',
1021 '/{repo_name}/delete/{revision}/{f_path}',
1022 controller='files', action='delete_home', revision='tip',
1022 controller='files', action='delete_home', revision='tip',
1023 f_path='', conditions={'function': check_repo},
1023 f_path='', conditions={'function': check_repo},
1024 requirements=URL_NAME_REQUIREMENTS)
1024 requirements=URL_NAME_REQUIREMENTS)
1025
1025
1026 rmap.connect('files_archive_home', '/{repo_name}/archive/{fname}',
1026 rmap.connect('files_archive_home', '/{repo_name}/archive/{fname}',
1027 controller='files', action='archivefile',
1027 controller='files', action='archivefile',
1028 conditions={'function': check_repo},
1028 conditions={'function': check_repo},
1029 requirements=URL_NAME_REQUIREMENTS)
1029 requirements=URL_NAME_REQUIREMENTS)
1030
1030
1031 rmap.connect('files_nodelist_home',
1031 rmap.connect('files_nodelist_home',
1032 '/{repo_name}/nodelist/{revision}/{f_path}',
1032 '/{repo_name}/nodelist/{revision}/{f_path}',
1033 controller='files', action='nodelist',
1033 controller='files', action='nodelist',
1034 conditions={'function': check_repo},
1034 conditions={'function': check_repo},
1035 requirements=URL_NAME_REQUIREMENTS)
1035 requirements=URL_NAME_REQUIREMENTS)
1036
1036
1037 rmap.connect('files_metadata_list_home',
1037 rmap.connect('files_metadata_list_home',
1038 '/{repo_name}/metadata_list/{revision}/{f_path}',
1038 '/{repo_name}/metadata_list/{revision}/{f_path}',
1039 controller='files', action='metadata_list',
1039 controller='files', action='metadata_list',
1040 conditions={'function': check_repo},
1040 conditions={'function': check_repo},
1041 requirements=URL_NAME_REQUIREMENTS)
1041 requirements=URL_NAME_REQUIREMENTS)
1042
1042
1043 rmap.connect('repo_fork_create_home', '/{repo_name}/fork',
1043 rmap.connect('repo_fork_create_home', '/{repo_name}/fork',
1044 controller='forks', action='fork_create',
1044 controller='forks', action='fork_create',
1045 conditions={'function': check_repo, 'method': ['POST']},
1045 conditions={'function': check_repo, 'method': ['POST']},
1046 requirements=URL_NAME_REQUIREMENTS)
1046 requirements=URL_NAME_REQUIREMENTS)
1047
1047
1048 rmap.connect('repo_fork_home', '/{repo_name}/fork',
1048 rmap.connect('repo_fork_home', '/{repo_name}/fork',
1049 controller='forks', action='fork',
1049 controller='forks', action='fork',
1050 conditions={'function': check_repo},
1050 conditions={'function': check_repo},
1051 requirements=URL_NAME_REQUIREMENTS)
1051 requirements=URL_NAME_REQUIREMENTS)
1052
1052
1053 rmap.connect('repo_forks_home', '/{repo_name}/forks',
1053 rmap.connect('repo_forks_home', '/{repo_name}/forks',
1054 controller='forks', action='forks',
1054 controller='forks', action='forks',
1055 conditions={'function': check_repo},
1055 conditions={'function': check_repo},
1056 requirements=URL_NAME_REQUIREMENTS)
1056 requirements=URL_NAME_REQUIREMENTS)
1057
1057
1058 rmap.connect('repo_followers_home', '/{repo_name}/followers',
1058 rmap.connect('repo_followers_home', '/{repo_name}/followers',
1059 controller='followers', action='followers',
1059 controller='followers', action='followers',
1060 conditions={'function': check_repo},
1060 conditions={'function': check_repo},
1061 requirements=URL_NAME_REQUIREMENTS)
1061 requirements=URL_NAME_REQUIREMENTS)
1062
1062
1063 # must be here for proper group/repo catching pattern
1063 # must be here for proper group/repo catching pattern
1064 _connect_with_slash(
1064 _connect_with_slash(
1065 rmap, 'repo_group_home', '/{group_name}',
1065 rmap, 'repo_group_home', '/{group_name}',
1066 controller='home', action='index_repo_group',
1066 controller='home', action='index_repo_group',
1067 conditions={'function': check_group},
1067 conditions={'function': check_group},
1068 requirements=URL_NAME_REQUIREMENTS)
1068 requirements=URL_NAME_REQUIREMENTS)
1069
1069
1070 # catch all, at the end
1070 # catch all, at the end
1071 _connect_with_slash(
1071 _connect_with_slash(
1072 rmap, 'summary_home', '/{repo_name}',
1072 rmap, 'summary_home', '/{repo_name}',
1073 controller='summary', action='index',
1073 controller='summary', action='index',
1074 conditions={'function': check_repo},
1074 conditions={'function': check_repo},
1075 requirements=URL_NAME_REQUIREMENTS)
1075 requirements=URL_NAME_REQUIREMENTS)
1076
1076
1077 return rmap
1077 return rmap
1078
1078
1079
1079
1080 def _connect_with_slash(mapper, name, path, *args, **kwargs):
1080 def _connect_with_slash(mapper, name, path, *args, **kwargs):
1081 """
1081 """
1082 Connect a route with an optional trailing slash in `path`.
1082 Connect a route with an optional trailing slash in `path`.
1083 """
1083 """
1084 mapper.connect(name + '_slash', path + '/', *args, **kwargs)
1084 mapper.connect(name + '_slash', path + '/', *args, **kwargs)
1085 mapper.connect(name, path, *args, **kwargs)
1085 mapper.connect(name, path, *args, **kwargs)
@@ -1,232 +1,277 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 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 Home controller for RhodeCode Enterprise
22 Home controller for RhodeCode Enterprise
23 """
23 """
24
24
25 import logging
25 import logging
26 import time
26 import time
27
27 import re
28
28
29 from pylons import tmpl_context as c, request
29 from pylons import tmpl_context as c, request, url, config
30 from pylons.i18n.translation import _
30 from pylons.i18n.translation import _
31 from sqlalchemy.sql import func
31 from sqlalchemy.sql import func
32
32
33 from rhodecode.lib.auth import (
33 from rhodecode.lib.auth import (
34 LoginRequired, HasPermissionAllDecorator,
34 LoginRequired, HasPermissionAllDecorator, AuthUser,
35 HasRepoGroupPermissionAnyDecorator, XHRRequired)
35 HasRepoGroupPermissionAnyDecorator, XHRRequired)
36 from rhodecode.lib.base import BaseController, render
36 from rhodecode.lib.base import BaseController, render
37 from rhodecode.lib.index import searcher_from_config
37 from rhodecode.lib.ext_json import json
38 from rhodecode.lib.ext_json import json
38 from rhodecode.lib.utils import jsonify
39 from rhodecode.lib.utils import jsonify
39 from rhodecode.lib.utils2 import safe_unicode
40 from rhodecode.lib.utils2 import safe_unicode
40 from rhodecode.model.db import Repository, RepoGroup
41 from rhodecode.model.db import Repository, RepoGroup
41 from rhodecode.model.repo import RepoModel
42 from rhodecode.model.repo import RepoModel
42 from rhodecode.model.repo_group import RepoGroupModel
43 from rhodecode.model.repo_group import RepoGroupModel
43 from rhodecode.model.scm import RepoList, RepoGroupList
44 from rhodecode.model.scm import RepoList, RepoGroupList
44
45
45
46
46 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
47
48
48
49
49 class HomeController(BaseController):
50 class HomeController(BaseController):
50 def __before__(self):
51 def __before__(self):
51 super(HomeController, self).__before__()
52 super(HomeController, self).__before__()
52
53
53 def ping(self):
54 def ping(self):
54 """
55 """
55 Ping, doesn't require login, good for checking out the platform
56 Ping, doesn't require login, good for checking out the platform
56 """
57 """
57 instance_id = getattr(c, 'rhodecode_instanceid', '')
58 instance_id = getattr(c, 'rhodecode_instanceid', '')
58 return 'pong[%s] => %s' % (instance_id, self.ip_addr,)
59 return 'pong[%s] => %s' % (instance_id, self.ip_addr,)
59
60
60 @LoginRequired()
61 @LoginRequired()
61 @HasPermissionAllDecorator('hg.admin')
62 @HasPermissionAllDecorator('hg.admin')
62 def error_test(self):
63 def error_test(self):
63 """
64 """
64 Test exception handling and emails on errors
65 Test exception handling and emails on errors
65 """
66 """
66 class TestException(Exception):
67 class TestException(Exception):
67 pass
68 pass
68
69
69 msg = ('RhodeCode Enterprise %s test exception. Generation time: %s'
70 msg = ('RhodeCode Enterprise %s test exception. Generation time: %s'
70 % (c.rhodecode_name, time.time()))
71 % (c.rhodecode_name, time.time()))
71 raise TestException(msg)
72 raise TestException(msg)
72
73
73 def _get_groups_and_repos(self, repo_group_id=None):
74 def _get_groups_and_repos(self, repo_group_id=None):
74 # repo groups groups
75 # repo groups groups
75 repo_group_list = RepoGroup.get_all_repo_groups(group_id=repo_group_id)
76 repo_group_list = RepoGroup.get_all_repo_groups(group_id=repo_group_id)
76 _perms = ['group.read', 'group.write', 'group.admin']
77 _perms = ['group.read', 'group.write', 'group.admin']
77 repo_group_list_acl = RepoGroupList(repo_group_list, perm_set=_perms)
78 repo_group_list_acl = RepoGroupList(repo_group_list, perm_set=_perms)
78 repo_group_data = RepoGroupModel().get_repo_groups_as_dict(
79 repo_group_data = RepoGroupModel().get_repo_groups_as_dict(
79 repo_group_list=repo_group_list_acl, admin=False)
80 repo_group_list=repo_group_list_acl, admin=False)
80
81
81 # repositories
82 # repositories
82 repo_list = Repository.get_all_repos(group_id=repo_group_id)
83 repo_list = Repository.get_all_repos(group_id=repo_group_id)
83 _perms = ['repository.read', 'repository.write', 'repository.admin']
84 _perms = ['repository.read', 'repository.write', 'repository.admin']
84 repo_list_acl = RepoList(repo_list, perm_set=_perms)
85 repo_list_acl = RepoList(repo_list, perm_set=_perms)
85 repo_data = RepoModel().get_repos_as_dict(
86 repo_data = RepoModel().get_repos_as_dict(
86 repo_list=repo_list_acl, admin=False)
87 repo_list=repo_list_acl, admin=False)
87
88
88 return repo_data, repo_group_data
89 return repo_data, repo_group_data
89
90
90 @LoginRequired()
91 @LoginRequired()
91 def index(self):
92 def index(self):
92 c.repo_group = None
93 c.repo_group = None
93
94
94 repo_data, repo_group_data = self._get_groups_and_repos()
95 repo_data, repo_group_data = self._get_groups_and_repos()
95 # json used to render the grids
96 # json used to render the grids
96 c.repos_data = json.dumps(repo_data)
97 c.repos_data = json.dumps(repo_data)
97 c.repo_groups_data = json.dumps(repo_group_data)
98 c.repo_groups_data = json.dumps(repo_group_data)
98
99
99 return render('/index.html')
100 return render('/index.html')
100
101
101 @LoginRequired()
102 @LoginRequired()
102 @HasRepoGroupPermissionAnyDecorator('group.read', 'group.write',
103 @HasRepoGroupPermissionAnyDecorator('group.read', 'group.write',
103 'group.admin')
104 'group.admin')
104 def index_repo_group(self, group_name):
105 def index_repo_group(self, group_name):
105 """GET /repo_group_name: Show a specific item"""
106 """GET /repo_group_name: Show a specific item"""
106 c.repo_group = RepoGroupModel()._get_repo_group(group_name)
107 c.repo_group = RepoGroupModel()._get_repo_group(group_name)
107 repo_data, repo_group_data = self._get_groups_and_repos(
108 repo_data, repo_group_data = self._get_groups_and_repos(
108 c.repo_group.group_id)
109 c.repo_group.group_id)
109
110
110 # json used to render the grids
111 # json used to render the grids
111 c.repos_data = json.dumps(repo_data)
112 c.repos_data = json.dumps(repo_data)
112 c.repo_groups_data = json.dumps(repo_group_data)
113 c.repo_groups_data = json.dumps(repo_group_data)
113
114
114 return render('index_repo_group.html')
115 return render('index_repo_group.html')
115
116
116 def _get_repo_list(self, name_contains=None, repo_type=None, limit=20):
117 def _get_repo_list(self, name_contains=None, repo_type=None, limit=20):
117 query = Repository.query()\
118 query = Repository.query()\
118 .order_by(func.length(Repository.repo_name))\
119 .order_by(func.length(Repository.repo_name))\
119 .order_by(Repository.repo_name)
120 .order_by(Repository.repo_name)
120
121
121 if repo_type:
122 if repo_type:
122 query = query.filter(Repository.repo_type == repo_type)
123 query = query.filter(Repository.repo_type == repo_type)
123
124
124 if name_contains:
125 if name_contains:
125 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
126 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
126 query = query.filter(
127 query = query.filter(
127 Repository.repo_name.ilike(ilike_expression))
128 Repository.repo_name.ilike(ilike_expression))
128 query = query.limit(limit)
129 query = query.limit(limit)
129
130
130 all_repos = query.all()
131 all_repos = query.all()
131 repo_iter = self.scm_model.get_repos(all_repos)
132 repo_iter = self.scm_model.get_repos(all_repos)
132 return [
133 return [
133 {
134 {
134 'id': obj['name'],
135 'id': obj['name'],
135 'text': obj['name'],
136 'text': obj['name'],
136 'type': 'repo',
137 'type': 'repo',
137 'obj': obj['dbrepo']
138 'obj': obj['dbrepo'],
139 'url': url('summary_home', repo_name=obj['name'])
138 }
140 }
139 for obj in repo_iter]
141 for obj in repo_iter]
140
142
141 def _get_repo_group_list(self, name_contains=None, limit=20):
143 def _get_repo_group_list(self, name_contains=None, limit=20):
142 query = RepoGroup.query()\
144 query = RepoGroup.query()\
143 .order_by(func.length(RepoGroup.group_name))\
145 .order_by(func.length(RepoGroup.group_name))\
144 .order_by(RepoGroup.group_name)
146 .order_by(RepoGroup.group_name)
145
147
146 if name_contains:
148 if name_contains:
147 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
149 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
148 query = query.filter(
150 query = query.filter(
149 RepoGroup.group_name.ilike(ilike_expression))
151 RepoGroup.group_name.ilike(ilike_expression))
150 query = query.limit(limit)
152 query = query.limit(limit)
151
153
152 all_groups = query.all()
154 all_groups = query.all()
153 repo_groups_iter = self.scm_model.get_repo_groups(all_groups)
155 repo_groups_iter = self.scm_model.get_repo_groups(all_groups)
154 return [
156 return [
155 {
157 {
156 'id': obj.group_name,
158 'id': obj.group_name,
157 'text': obj.group_name,
159 'text': obj.group_name,
158 'type': 'group',
160 'type': 'group',
159 'obj': {}
161 'obj': {},
162 'url': url('repo_group_home', group_name=obj.group_name)
160 }
163 }
161 for obj in repo_groups_iter]
164 for obj in repo_groups_iter]
162
165
166 def _get_hash_commit_list(self, hash_starts_with=None, limit=20):
167 if not hash_starts_with or len(hash_starts_with) < 3:
168 return []
169
170 commit_hashes = re.compile('([0-9a-f]{2,40})').findall(hash_starts_with)
171
172 if len(commit_hashes) != 1:
173 return []
174
175 commit_hash_prefix = commit_hashes[0]
176
177 auth_user = AuthUser(
178 user_id=c.rhodecode_user.user_id, ip_addr=self.ip_addr)
179 searcher = searcher_from_config(config)
180 result = searcher.search(
181 'commit_id:%s*' % commit_hash_prefix, 'commit', auth_user)
182
183 return [
184 {
185 'id': entry['commit_id'],
186 'text': entry['commit_id'],
187 'type': 'commit',
188 'obj': {'repo': entry['repository']},
189 'url': url('changeset_home',
190 repo_name=entry['repository'], revision=entry['commit_id'])
191 }
192 for entry in result['results']]
193
163 @LoginRequired()
194 @LoginRequired()
164 @XHRRequired()
195 @XHRRequired()
165 @jsonify
196 @jsonify
166 def repo_switcher_data(self):
197 def goto_switcher_data(self):
167 query = request.GET.get('query')
198 query = request.GET.get('query')
168 log.debug('generating switcher repo/groups list, query %s', query)
199 log.debug('generating goto switcher list, query %s', query)
169
200
170 res = []
201 res = []
171 repo_groups = self._get_repo_group_list(query)
202 repo_groups = self._get_repo_group_list(query)
172 if repo_groups:
203 if repo_groups:
173 res.append({
204 res.append({
174 'text': _('Groups'),
205 'text': _('Groups'),
175 'children': repo_groups
206 'children': repo_groups
176 })
207 })
177
208
178 repos = self._get_repo_list(query)
209 repos = self._get_repo_list(query)
179 if repos:
210 if repos:
180 res.append({
211 res.append({
181 'text': _('Repositories'),
212 'text': _('Repositories'),
182 'children': repos
213 'children': repos
183 })
214 })
184
215
216 commits = self._get_hash_commit_list(query)
217 if commits:
218 unique_repos = {}
219 for commit in commits:
220 unique_repos.setdefault(commit['obj']['repo'], []
221 ).append(commit)
222
223 for repo in unique_repos:
224 res.append({
225 'text': _('Commits in %(repo)s') % {'repo': repo},
226 'children': unique_repos[repo]
227 })
228
185 data = {
229 data = {
186 'more': False,
230 'more': False,
187 'results': res
231 'results': res
188 }
232 }
189 return data
233 return data
190
234
191 @LoginRequired()
235 @LoginRequired()
192 @XHRRequired()
236 @XHRRequired()
193 @jsonify
237 @jsonify
194 def repo_list_data(self):
238 def repo_list_data(self):
195 query = request.GET.get('query')
239 query = request.GET.get('query')
196 repo_type = request.GET.get('repo_type')
240 repo_type = request.GET.get('repo_type')
197 log.debug('generating repo list, query:%s', query)
241 log.debug('generating repo list, query:%s', query)
198
242
199 res = []
243 res = []
200 repos = self._get_repo_list(query, repo_type=repo_type)
244 repos = self._get_repo_list(query, repo_type=repo_type)
201 if repos:
245 if repos:
202 res.append({
246 res.append({
203 'text': _('Repositories'),
247 'text': _('Repositories'),
204 'children': repos
248 'children': repos
205 })
249 })
250
206 data = {
251 data = {
207 'more': False,
252 'more': False,
208 'results': res
253 'results': res
209 }
254 }
210 return data
255 return data
211
256
212 @LoginRequired()
257 @LoginRequired()
213 @XHRRequired()
258 @XHRRequired()
214 @jsonify
259 @jsonify
215 def user_autocomplete_data(self):
260 def user_autocomplete_data(self):
216 query = request.GET.get('query')
261 query = request.GET.get('query')
217
262
218 repo_model = RepoModel()
263 repo_model = RepoModel()
219 _users = repo_model.get_users(name_contains=query)
264 _users = repo_model.get_users(name_contains=query)
220
265
221 if request.GET.get('user_groups'):
266 if request.GET.get('user_groups'):
222 # extend with user groups
267 # extend with user groups
223 _user_groups = repo_model.get_user_groups(name_contains=query)
268 _user_groups = repo_model.get_user_groups(name_contains=query)
224 _users = _users + _user_groups
269 _users = _users + _user_groups
225
270
226 return {'suggestions': _users}
271 return {'suggestions': _users}
227
272
228 @LoginRequired()
273 @LoginRequired()
229 @XHRRequired()
274 @XHRRequired()
230 @jsonify
275 @jsonify
231 def user_group_autocomplete_data(self):
276 def user_group_autocomplete_data(self):
232 return {'suggestions': []}
277 return {'suggestions': []}
@@ -1,56 +1,55 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2016 RhodeCode GmbH
3 # Copyright (C) 2012-2016 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 Index schema for RhodeCode
22 Index schema for RhodeCode
23 """
23 """
24
24
25 import importlib
25 import importlib
26 import logging
26 import logging
27
27
28 log = logging.getLogger(__name__)
28 log = logging.getLogger(__name__)
29
29
30 # leave defaults for backward compat
30 # leave defaults for backward compat
31 default_searcher = 'rhodecode.lib.index.whoosh'
31 default_searcher = 'rhodecode.lib.index.whoosh'
32 default_location = '%(here)s/data/index'
32 default_location = '%(here)s/data/index'
33
33
34
34
35 class BaseSearch(object):
35 class BaseSearch(object):
36 def __init__(self):
36 def __init__(self):
37 pass
37 pass
38
38
39 def cleanup(self):
39 def cleanup(self):
40 pass
40 pass
41
41
42 def search(self, query, document_type, search_user, repo_name=None):
42 def search(self, query, document_type, search_user, repo_name=None):
43 raise Exception('NotImplemented')
43 raise Exception('NotImplemented')
44
44
45
46 def searcher_from_config(config, prefix='search.'):
45 def searcher_from_config(config, prefix='search.'):
47 _config = {}
46 _config = {}
48 for key in config.keys():
47 for key in config.keys():
49 if key.startswith(prefix):
48 if key.startswith(prefix):
50 _config[key[len(prefix):]] = config[key]
49 _config[key[len(prefix):]] = config[key]
51
50
52 if 'location' not in _config:
51 if 'location' not in _config:
53 _config['location'] = default_location
52 _config['location'] = default_location
54 imported = importlib.import_module(_config.get('module', default_searcher))
53 imported = importlib.import_module(_config.get('module', default_searcher))
55 searcher = imported.Search(config=_config)
54 searcher = imported.Search(config=_config)
56 return searcher
55 return searcher
@@ -1,261 +1,274 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2016 RhodeCode GmbH
3 # Copyright (C) 2012-2016 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 Index schema for RhodeCode
22 Index schema for RhodeCode
23 """
23 """
24
24
25 from __future__ import absolute_import
25 from __future__ import absolute_import
26 import logging
26 import logging
27 import os
27 import os
28 import re
28
29
29 from pylons.i18n.translation import _
30 from pylons.i18n.translation import _
30
31
31 from whoosh import query as query_lib, sorting
32 from whoosh import query as query_lib, sorting
32 from whoosh.highlight import HtmlFormatter, ContextFragmenter
33 from whoosh.highlight import HtmlFormatter, ContextFragmenter
33 from whoosh.index import create_in, open_dir, exists_in, EmptyIndexError
34 from whoosh.index import create_in, open_dir, exists_in, EmptyIndexError
34 from whoosh.qparser import QueryParser, QueryParserError
35 from whoosh.qparser import QueryParser, QueryParserError
35
36
36 import rhodecode.lib.helpers as h
37 import rhodecode.lib.helpers as h
37 from rhodecode.lib.index import BaseSearch
38 from rhodecode.lib.index import BaseSearch
38
39
39 log = logging.getLogger(__name__)
40 log = logging.getLogger(__name__)
40
41
41
42
42 try:
43 try:
43 # we first try to import from rhodecode tools, fallback to copies if
44 # we first try to import from rhodecode tools, fallback to copies if
44 # we're unable to
45 # we're unable to
45 from rhodecode_tools.lib.fts_index.whoosh_schema import (
46 from rhodecode_tools.lib.fts_index.whoosh_schema import (
46 ANALYZER, FILE_INDEX_NAME, FILE_SCHEMA, COMMIT_INDEX_NAME,
47 ANALYZER, FILE_INDEX_NAME, FILE_SCHEMA, COMMIT_INDEX_NAME,
47 COMMIT_SCHEMA)
48 COMMIT_SCHEMA)
48 except ImportError:
49 except ImportError:
49 log.warning('rhodecode_tools schema not available, doing a fallback '
50 log.warning('rhodecode_tools schema not available, doing a fallback '
50 'import from `rhodecode.lib.index.whoosh_fallback_schema`')
51 'import from `rhodecode.lib.index.whoosh_fallback_schema`')
51 from rhodecode.lib.index.whoosh_fallback_schema import (
52 from rhodecode.lib.index.whoosh_fallback_schema import (
52 ANALYZER, FILE_INDEX_NAME, FILE_SCHEMA, COMMIT_INDEX_NAME,
53 ANALYZER, FILE_INDEX_NAME, FILE_SCHEMA, COMMIT_INDEX_NAME,
53 COMMIT_SCHEMA)
54 COMMIT_SCHEMA)
54
55
55
56
56 FORMATTER = HtmlFormatter('span', between='\n<span class="break">...</span>\n')
57 FORMATTER = HtmlFormatter('span', between='\n<span class="break">...</span>\n')
57 FRAGMENTER = ContextFragmenter(200)
58 FRAGMENTER = ContextFragmenter(200)
58
59
59 log = logging.getLogger(__name__)
60 log = logging.getLogger(__name__)
60
61
61
62
63
62 class Search(BaseSearch):
64 class Search(BaseSearch):
63
65
64 name = 'whoosh'
66 name = 'whoosh'
65
67
66 def __init__(self, config):
68 def __init__(self, config):
67 self.config = config
69 self.config = config
68 if not os.path.isdir(self.config['location']):
70 if not os.path.isdir(self.config['location']):
69 os.makedirs(self.config['location'])
71 os.makedirs(self.config['location'])
70
72
71 opener = create_in
73 opener = create_in
72 if exists_in(self.config['location'], indexname=FILE_INDEX_NAME):
74 if exists_in(self.config['location'], indexname=FILE_INDEX_NAME):
73 opener = open_dir
75 opener = open_dir
74 file_index = opener(self.config['location'], schema=FILE_SCHEMA,
76 file_index = opener(self.config['location'], schema=FILE_SCHEMA,
75 indexname=FILE_INDEX_NAME)
77 indexname=FILE_INDEX_NAME)
76
78
77 opener = create_in
79 opener = create_in
78 if exists_in(self.config['location'], indexname=COMMIT_INDEX_NAME):
80 if exists_in(self.config['location'], indexname=COMMIT_INDEX_NAME):
79 opener = open_dir
81 opener = open_dir
80 changeset_index = opener(self.config['location'], schema=COMMIT_SCHEMA,
82 changeset_index = opener(self.config['location'], schema=COMMIT_SCHEMA,
81 indexname=COMMIT_INDEX_NAME)
83 indexname=COMMIT_INDEX_NAME)
82
84
83 self.commit_schema = COMMIT_SCHEMA
85 self.commit_schema = COMMIT_SCHEMA
84 self.commit_index = changeset_index
86 self.commit_index = changeset_index
85 self.file_schema = FILE_SCHEMA
87 self.file_schema = FILE_SCHEMA
86 self.file_index = file_index
88 self.file_index = file_index
87 self.searcher = None
89 self.searcher = None
88
90
89 def cleanup(self):
91 def cleanup(self):
90 if self.searcher:
92 if self.searcher:
91 self.searcher.close()
93 self.searcher.close()
92
94
95 def _extend_query(self, query):
96 hashes = re.compile('([0-9a-f]{5,40})').findall(query)
97 if hashes:
98 hashes_or_query = ' OR '.join('commit_id:%s*' % h for h in hashes)
99 query = u'(%s) OR %s' % (query, hashes_or_query)
100 return query
101
93 def search(self, query, document_type, search_user, repo_name=None,
102 def search(self, query, document_type, search_user, repo_name=None,
94 requested_page=1, page_limit=10):
103 requested_page=1, page_limit=10):
104
105 original_query = query
106 query = self._extend_query(query)
107
95 log.debug(u'QUERY: %s on %s', query, document_type)
108 log.debug(u'QUERY: %s on %s', query, document_type)
96 result = {
109 result = {
97 'results': [],
110 'results': [],
98 'count': 0,
111 'count': 0,
99 'error': None,
112 'error': None,
100 'runtime': 0
113 'runtime': 0
101 }
114 }
102 search_type, index_name, schema_defn = self._prepare_for_search(
115 search_type, index_name, schema_defn = self._prepare_for_search(
103 document_type)
116 document_type)
104 self._init_searcher(index_name)
117 self._init_searcher(index_name)
105 try:
118 try:
106 qp = QueryParser(search_type, schema=schema_defn)
119 qp = QueryParser(search_type, schema=schema_defn)
107 allowed_repos_filter = self._get_repo_filter(
120 allowed_repos_filter = self._get_repo_filter(
108 search_user, repo_name)
121 search_user, repo_name)
109 try:
122 try:
110 query = qp.parse(unicode(query))
123 query = qp.parse(unicode(query))
111 log.debug('query: %s (%s)' % (query, repr(query)))
124 log.debug('query: %s (%s)' % (query, repr(query)))
112
125
113 sortedby = None
126 sortedby = None
114 if search_type == 'message':
127 if search_type == 'message':
115 sortedby = sorting.FieldFacet('commit_idx', reverse=True)
128 sortedby = sorting.FieldFacet('commit_idx', reverse=True)
116
129
117 whoosh_results = self.searcher.search(
130 whoosh_results = self.searcher.search(
118 query, filter=allowed_repos_filter, limit=None,
131 query, filter=allowed_repos_filter, limit=None,
119 sortedby=sortedby,)
132 sortedby=sortedby,)
120
133
121 # fixes for 32k limit that whoosh uses for highlight
134 # fixes for 32k limit that whoosh uses for highlight
122 whoosh_results.fragmenter.charlimit = None
135 whoosh_results.fragmenter.charlimit = None
123 res_ln = whoosh_results.scored_length()
136 res_ln = whoosh_results.scored_length()
124 result['runtime'] = whoosh_results.runtime
137 result['runtime'] = whoosh_results.runtime
125 result['count'] = res_ln
138 result['count'] = res_ln
126 result['results'] = WhooshResultWrapper(
139 result['results'] = WhooshResultWrapper(
127 search_type, res_ln, whoosh_results)
140 search_type, res_ln, whoosh_results)
128
141
129 except QueryParserError:
142 except QueryParserError:
130 result['error'] = _('Invalid search query. Try quoting it.')
143 result['error'] = _('Invalid search query. Try quoting it.')
131 except (EmptyIndexError, IOError, OSError):
144 except (EmptyIndexError, IOError, OSError):
132 msg = _('There is no index to search in. '
145 msg = _('There is no index to search in. '
133 'Please run whoosh indexer')
146 'Please run whoosh indexer')
134 log.exception(msg)
147 log.exception(msg)
135 result['error'] = msg
148 result['error'] = msg
136 except Exception:
149 except Exception:
137 msg = _('An error occurred during this search operation')
150 msg = _('An error occurred during this search operation')
138 log.exception(msg)
151 log.exception(msg)
139 result['error'] = msg
152 result['error'] = msg
140
153
141 return result
154 return result
142
155
143 def statistics(self):
156 def statistics(self):
144 stats = [
157 stats = [
145 {'key': _('Index Type'), 'value': 'Whoosh'},
158 {'key': _('Index Type'), 'value': 'Whoosh'},
146 {'key': _('File Index'), 'value': str(self.file_index)},
159 {'key': _('File Index'), 'value': str(self.file_index)},
147 {'key': _('Indexed documents'),
160 {'key': _('Indexed documents'),
148 'value': self.file_index.doc_count()},
161 'value': self.file_index.doc_count()},
149 {'key': _('Last update'),
162 {'key': _('Last update'),
150 'value': h.time_to_datetime(self.file_index.last_modified())},
163 'value': h.time_to_datetime(self.file_index.last_modified())},
151 {'key': _('Commit index'), 'value': str(self.commit_index)},
164 {'key': _('Commit index'), 'value': str(self.commit_index)},
152 {'key': _('Indexed documents'),
165 {'key': _('Indexed documents'),
153 'value': str(self.commit_index.doc_count())},
166 'value': str(self.commit_index.doc_count())},
154 {'key': _('Last update'),
167 {'key': _('Last update'),
155 'value': h.time_to_datetime(self.commit_index.last_modified())}
168 'value': h.time_to_datetime(self.commit_index.last_modified())}
156 ]
169 ]
157 return stats
170 return stats
158
171
159 def _get_repo_filter(self, auth_user, repo_name):
172 def _get_repo_filter(self, auth_user, repo_name):
160
173
161 allowed_to_search = [
174 allowed_to_search = [
162 repo for repo, perm in
175 repo for repo, perm in
163 auth_user.permissions['repositories'].items()
176 auth_user.permissions['repositories'].items()
164 if perm != 'repository.none']
177 if perm != 'repository.none']
165
178
166 if repo_name:
179 if repo_name:
167 repo_filter = [query_lib.Term('repository', repo_name)]
180 repo_filter = [query_lib.Term('repository', repo_name)]
168
181
169 elif 'hg.admin' in auth_user.permissions.get('global', []):
182 elif 'hg.admin' in auth_user.permissions.get('global', []):
170 return None
183 return None
171
184
172 else:
185 else:
173 repo_filter = [query_lib.Term('repository', _rn)
186 repo_filter = [query_lib.Term('repository', _rn)
174 for _rn in allowed_to_search]
187 for _rn in allowed_to_search]
175 # in case we're not allowed to search anywhere, it's a trick
188 # in case we're not allowed to search anywhere, it's a trick
176 # to tell whoosh we're filtering, on ALL results
189 # to tell whoosh we're filtering, on ALL results
177 repo_filter = repo_filter or [query_lib.Term('repository', '')]
190 repo_filter = repo_filter or [query_lib.Term('repository', '')]
178
191
179 return query_lib.Or(repo_filter)
192 return query_lib.Or(repo_filter)
180
193
181 def _prepare_for_search(self, cur_type):
194 def _prepare_for_search(self, cur_type):
182 search_type = {
195 search_type = {
183 'content': 'content',
196 'content': 'content',
184 'commit': 'message',
197 'commit': 'message',
185 'path': 'path',
198 'path': 'path',
186 'repository': 'repository'
199 'repository': 'repository'
187 }.get(cur_type, 'content')
200 }.get(cur_type, 'content')
188
201
189 index_name = {
202 index_name = {
190 'content': FILE_INDEX_NAME,
203 'content': FILE_INDEX_NAME,
191 'commit': COMMIT_INDEX_NAME,
204 'commit': COMMIT_INDEX_NAME,
192 'path': FILE_INDEX_NAME
205 'path': FILE_INDEX_NAME
193 }.get(cur_type, FILE_INDEX_NAME)
206 }.get(cur_type, FILE_INDEX_NAME)
194
207
195 schema_defn = {
208 schema_defn = {
196 'content': self.file_schema,
209 'content': self.file_schema,
197 'commit': self.commit_schema,
210 'commit': self.commit_schema,
198 'path': self.file_schema
211 'path': self.file_schema
199 }.get(cur_type, self.file_schema)
212 }.get(cur_type, self.file_schema)
200
213
201 log.debug('IDX: %s' % index_name)
214 log.debug('IDX: %s' % index_name)
202 log.debug('SCHEMA: %s' % schema_defn)
215 log.debug('SCHEMA: %s' % schema_defn)
203 return search_type, index_name, schema_defn
216 return search_type, index_name, schema_defn
204
217
205 def _init_searcher(self, index_name):
218 def _init_searcher(self, index_name):
206 idx = open_dir(self.config['location'], indexname=index_name)
219 idx = open_dir(self.config['location'], indexname=index_name)
207 self.searcher = idx.searcher()
220 self.searcher = idx.searcher()
208 return self.searcher
221 return self.searcher
209
222
210
223
211 class WhooshResultWrapper(object):
224 class WhooshResultWrapper(object):
212 def __init__(self, search_type, total_hits, results):
225 def __init__(self, search_type, total_hits, results):
213 self.search_type = search_type
226 self.search_type = search_type
214 self.results = results
227 self.results = results
215 self.total_hits = total_hits
228 self.total_hits = total_hits
216
229
217 def __str__(self):
230 def __str__(self):
218 return '<%s at %s>' % (self.__class__.__name__, len(self))
231 return '<%s at %s>' % (self.__class__.__name__, len(self))
219
232
220 def __repr__(self):
233 def __repr__(self):
221 return self.__str__()
234 return self.__str__()
222
235
223 def __len__(self):
236 def __len__(self):
224 return self.total_hits
237 return self.total_hits
225
238
226 def __iter__(self):
239 def __iter__(self):
227 """
240 """
228 Allows Iteration over results,and lazy generate content
241 Allows Iteration over results,and lazy generate content
229
242
230 *Requires* implementation of ``__getitem__`` method.
243 *Requires* implementation of ``__getitem__`` method.
231 """
244 """
232 for hit in self.results:
245 for hit in self.results:
233 yield self.get_full_content(hit)
246 yield self.get_full_content(hit)
234
247
235 def __getitem__(self, key):
248 def __getitem__(self, key):
236 """
249 """
237 Slicing of resultWrapper
250 Slicing of resultWrapper
238 """
251 """
239 i, j = key.start, key.stop
252 i, j = key.start, key.stop
240 for hit in self.results[i:j]:
253 for hit in self.results[i:j]:
241 yield self.get_full_content(hit)
254 yield self.get_full_content(hit)
242
255
243 def get_full_content(self, hit):
256 def get_full_content(self, hit):
244 # TODO: marcink: this feels like an overkill, there's a lot of data
257 # TODO: marcink: this feels like an overkill, there's a lot of data
245 # inside hit object, and we don't need all
258 # inside hit object, and we don't need all
246 res = dict(hit)
259 res = dict(hit)
247
260
248 f_path = '' # noqa
261 f_path = '' # noqa
249 if self.search_type in ['content', 'path']:
262 if self.search_type in ['content', 'path']:
250 f_path = res['path'].split(res['repository'])[-1]
263 f_path = res['path'].split(res['repository'])[-1]
251 f_path = f_path.lstrip(os.sep)
264 f_path = f_path.lstrip(os.sep)
252
265
253 if self.search_type == 'content':
266 if self.search_type == 'content':
254 res.update({'content_short_hl': hit.highlights('content'),
267 res.update({'content_short_hl': hit.highlights('content'),
255 'f_path': f_path})
268 'f_path': f_path})
256 elif self.search_type == 'path':
269 elif self.search_type == 'path':
257 res.update({'f_path': f_path})
270 res.update({'f_path': f_path})
258 elif self.search_type == 'message':
271 elif self.search_type == 'message':
259 res.update({'message_hl': hit.highlights('message')})
272 res.update({'message_hl': hit.highlights('message')})
260
273
261 return res
274 return res
@@ -1,652 +1,655 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="root.html"/>
2 <%inherit file="root.html"/>
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.url('/images/rhodecode-logo-white-216x60.png')}" alt="RhodeCode"/></a>
10 <a href="${h.url('home')}"><img src="${h.url('/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 ${self.body()}
19 ${self.body()}
20 </div>
20 </div>
21 </div>
21 </div>
22 ${self.menu_bar_subnav()}
22 ${self.menu_bar_subnav()}
23 <!-- END HEADER -->
23 <!-- END HEADER -->
24
24
25 <!-- CONTENT -->
25 <!-- CONTENT -->
26 <div id="content" class="wrapper">
26 <div id="content" class="wrapper">
27 ${self.flash_msg()}
27 ${self.flash_msg()}
28 <div class="main">
28 <div class="main">
29 ${next.main()}
29 ${next.main()}
30 </div>
30 </div>
31 </div>
31 </div>
32 <!-- END CONTENT -->
32 <!-- END CONTENT -->
33
33
34 </div>
34 </div>
35 <!-- FOOTER -->
35 <!-- FOOTER -->
36 <div id="footer">
36 <div id="footer">
37 <div id="footer-inner" class="title wrapper">
37 <div id="footer-inner" class="title wrapper">
38 <div>
38 <div>
39 <p class="footer-link-right">
39 <p class="footer-link-right">
40 % if c.visual.show_version:
40 % if c.visual.show_version:
41 RhodeCode Enterprise ${c.rhodecode_version} ${c.rhodecode_edition}
41 RhodeCode Enterprise ${c.rhodecode_version} ${c.rhodecode_edition}
42 % endif
42 % endif
43 &copy; 2010-${h.datetime.today().year}, <a href="${h.url('rhodecode_official')}" target="_blank">RhodeCode GmbH</a>. All rights reserved.
43 &copy; 2010-${h.datetime.today().year}, <a href="${h.url('rhodecode_official')}" target="_blank">RhodeCode GmbH</a>. All rights reserved.
44 % if c.visual.rhodecode_support_url:
44 % if c.visual.rhodecode_support_url:
45 <a href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a>
45 <a href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a>
46 % endif
46 % endif
47 </p>
47 </p>
48 <% sid = 'block' if request.GET.get('showrcid') else 'none' %>
48 <% sid = 'block' if request.GET.get('showrcid') else 'none' %>
49 <p class="server-instance" style="display:${sid}">
49 <p class="server-instance" style="display:${sid}">
50 ## display hidden instance ID if specially defined
50 ## display hidden instance ID if specially defined
51 % if c.rhodecode_instanceid:
51 % if c.rhodecode_instanceid:
52 ${_('RhodeCode instance id: %s') % c.rhodecode_instanceid}
52 ${_('RhodeCode instance id: %s') % c.rhodecode_instanceid}
53 % endif
53 % endif
54 </p>
54 </p>
55 </div>
55 </div>
56 </div>
56 </div>
57 </div>
57 </div>
58
58
59 <!-- END FOOTER -->
59 <!-- END FOOTER -->
60
60
61 ### MAKO DEFS ###
61 ### MAKO DEFS ###
62
62
63 <%def name="menu_bar_subnav()">
63 <%def name="menu_bar_subnav()">
64 </%def>
64 </%def>
65
65
66 <%def name="flash_msg()">
66 <%def name="flash_msg()">
67 <%include file="/base/flash_msg.html"/>
67 <%include file="/base/flash_msg.html"/>
68 </%def>
68 </%def>
69
69
70 <%def name="breadcrumbs(class_='breadcrumbs')">
70 <%def name="breadcrumbs(class_='breadcrumbs')">
71 <div class="${class_}">
71 <div class="${class_}">
72 ${self.breadcrumbs_links()}
72 ${self.breadcrumbs_links()}
73 </div>
73 </div>
74 </%def>
74 </%def>
75
75
76 <%def name="admin_menu()">
76 <%def name="admin_menu()">
77 <ul class="admin_menu submenu">
77 <ul class="admin_menu submenu">
78 <li><a href="${h.url('admin_home')}">${_('Admin journal')}</a></li>
78 <li><a href="${h.url('admin_home')}">${_('Admin journal')}</a></li>
79 <li><a href="${h.url('repos')}">${_('Repositories')}</a></li>
79 <li><a href="${h.url('repos')}">${_('Repositories')}</a></li>
80 <li><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
80 <li><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
81 <li><a href="${h.url('users')}">${_('Users')}</a></li>
81 <li><a href="${h.url('users')}">${_('Users')}</a></li>
82 <li><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
82 <li><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
83 <li><a href="${h.url('admin_permissions_application')}">${_('Permissions')}</a></li>
83 <li><a href="${h.url('admin_permissions_application')}">${_('Permissions')}</a></li>
84 <li><a href="${h.route_path('auth_home', traverse='')}">${_('Authentication')}</a></li>
84 <li><a href="${h.route_path('auth_home', traverse='')}">${_('Authentication')}</a></li>
85 <li><a href="${h.url('admin_defaults_repositories')}">${_('Defaults')}</a></li>
85 <li><a href="${h.url('admin_defaults_repositories')}">${_('Defaults')}</a></li>
86 <li class="last"><a href="${h.url('admin_settings')}">${_('Settings')}</a></li>
86 <li class="last"><a href="${h.url('admin_settings')}">${_('Settings')}</a></li>
87 </ul>
87 </ul>
88 </%def>
88 </%def>
89
89
90
90
91 <%def name="dt_info_panel(elements)">
91 <%def name="dt_info_panel(elements)">
92 <dl class="dl-horizontal">
92 <dl class="dl-horizontal">
93 %for dt, dd, title, show_items in elements:
93 %for dt, dd, title, show_items in elements:
94 <dt>${dt}:</dt>
94 <dt>${dt}:</dt>
95 <dd title="${title}">
95 <dd title="${title}">
96 %if callable(dd):
96 %if callable(dd):
97 ## allow lazy evaluation of elements
97 ## allow lazy evaluation of elements
98 ${dd()}
98 ${dd()}
99 %else:
99 %else:
100 ${dd}
100 ${dd}
101 %endif
101 %endif
102 %if show_items:
102 %if show_items:
103 <span class="btn-collapse" data-toggle="item-${h.md5(dt)[:6]}-details">${_('Show More')} </span>
103 <span class="btn-collapse" data-toggle="item-${h.md5(dt)[:6]}-details">${_('Show More')} </span>
104 %endif
104 %endif
105 </dd>
105 </dd>
106
106
107 %if show_items:
107 %if show_items:
108 <div class="collapsable-content" data-toggle="item-${h.md5(dt)[:6]}-details" style="display: none">
108 <div class="collapsable-content" data-toggle="item-${h.md5(dt)[:6]}-details" style="display: none">
109 %for item in show_items:
109 %for item in show_items:
110 <dt></dt>
110 <dt></dt>
111 <dd>${item}</dd>
111 <dd>${item}</dd>
112 %endfor
112 %endfor
113 </div>
113 </div>
114 %endif
114 %endif
115
115
116 %endfor
116 %endfor
117 </dl>
117 </dl>
118 </%def>
118 </%def>
119
119
120
120
121 <%def name="gravatar(email, size=16)">
121 <%def name="gravatar(email, size=16)">
122 <%
122 <%
123 if (size > 16):
123 if (size > 16):
124 gravatar_class = 'gravatar gravatar-large'
124 gravatar_class = 'gravatar gravatar-large'
125 else:
125 else:
126 gravatar_class = 'gravatar'
126 gravatar_class = 'gravatar'
127 %>
127 %>
128 <%doc>
128 <%doc>
129 TODO: johbo: For now we serve double size images to make it smooth
129 TODO: johbo: For now we serve double size images to make it smooth
130 for retina. This is how it worked until now. Should be replaced
130 for retina. This is how it worked until now. Should be replaced
131 with a better solution at some point.
131 with a better solution at some point.
132 </%doc>
132 </%doc>
133 <img class="${gravatar_class}" src="${h.gravatar_url(email, size * 2)}" height="${size}" width="${size}">
133 <img class="${gravatar_class}" src="${h.gravatar_url(email, size * 2)}" height="${size}" width="${size}">
134 </%def>
134 </%def>
135
135
136
136
137 <%def name="gravatar_with_user(contact, size=16)">
137 <%def name="gravatar_with_user(contact, size=16)">
138 <div class="rc-user tooltip" title="${contact}">
138 <div class="rc-user tooltip" title="${contact}">
139 ${self.gravatar(h.email_or_none(contact), size)}
139 ${self.gravatar(h.email_or_none(contact), size)}
140 <span class="user"> ${h.link_to_user(contact)}</span>
140 <span class="user"> ${h.link_to_user(contact)}</span>
141 </div>
141 </div>
142 </%def>
142 </%def>
143
143
144
144
145 ## admin menu used for people that have some admin resources
145 ## admin menu used for people that have some admin resources
146 <%def name="admin_menu_simple(repositories=None, repository_groups=None, user_groups=None)">
146 <%def name="admin_menu_simple(repositories=None, repository_groups=None, user_groups=None)">
147 <ul class="submenu">
147 <ul class="submenu">
148 %if repositories:
148 %if repositories:
149 <li><a href="${h.url('repos')}">${_('Repositories')}</a></li>
149 <li><a href="${h.url('repos')}">${_('Repositories')}</a></li>
150 %endif
150 %endif
151 %if repository_groups:
151 %if repository_groups:
152 <li><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
152 <li><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
153 %endif
153 %endif
154 %if user_groups:
154 %if user_groups:
155 <li><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
155 <li><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
156 %endif
156 %endif
157 </ul>
157 </ul>
158 </%def>
158 </%def>
159
159
160 <%def name="repo_page_title(repo_instance)">
160 <%def name="repo_page_title(repo_instance)">
161 <div class="title-content">
161 <div class="title-content">
162 <div class="title-main">
162 <div class="title-main">
163 ## SVN/HG/GIT icons
163 ## SVN/HG/GIT icons
164 %if h.is_hg(repo_instance):
164 %if h.is_hg(repo_instance):
165 <i class="icon-hg"></i>
165 <i class="icon-hg"></i>
166 %endif
166 %endif
167 %if h.is_git(repo_instance):
167 %if h.is_git(repo_instance):
168 <i class="icon-git"></i>
168 <i class="icon-git"></i>
169 %endif
169 %endif
170 %if h.is_svn(repo_instance):
170 %if h.is_svn(repo_instance):
171 <i class="icon-svn"></i>
171 <i class="icon-svn"></i>
172 %endif
172 %endif
173
173
174 ## public/private
174 ## public/private
175 %if repo_instance.private:
175 %if repo_instance.private:
176 <i class="icon-repo-private"></i>
176 <i class="icon-repo-private"></i>
177 %else:
177 %else:
178 <i class="icon-repo-public"></i>
178 <i class="icon-repo-public"></i>
179 %endif
179 %endif
180
180
181 ## repo name with group name
181 ## repo name with group name
182 ${h.breadcrumb_repo_link(c.rhodecode_db_repo)}
182 ${h.breadcrumb_repo_link(c.rhodecode_db_repo)}
183
183
184 </div>
184 </div>
185
185
186 ## FORKED
186 ## FORKED
187 %if repo_instance.fork:
187 %if repo_instance.fork:
188 <p>
188 <p>
189 <i class="icon-code-fork"></i> ${_('Fork of')}
189 <i class="icon-code-fork"></i> ${_('Fork of')}
190 <a href="${h.url('summary_home',repo_name=repo_instance.fork.repo_name)}">${repo_instance.fork.repo_name}</a>
190 <a href="${h.url('summary_home',repo_name=repo_instance.fork.repo_name)}">${repo_instance.fork.repo_name}</a>
191 </p>
191 </p>
192 %endif
192 %endif
193
193
194 ## IMPORTED FROM REMOTE
194 ## IMPORTED FROM REMOTE
195 %if repo_instance.clone_uri:
195 %if repo_instance.clone_uri:
196 <p>
196 <p>
197 <i class="icon-code-fork"></i> ${_('Clone from')}
197 <i class="icon-code-fork"></i> ${_('Clone from')}
198 <a href="${h.url(str(h.hide_credentials(repo_instance.clone_uri)))}">${h.hide_credentials(repo_instance.clone_uri)}</a>
198 <a href="${h.url(str(h.hide_credentials(repo_instance.clone_uri)))}">${h.hide_credentials(repo_instance.clone_uri)}</a>
199 </p>
199 </p>
200 %endif
200 %endif
201
201
202 ## LOCKING STATUS
202 ## LOCKING STATUS
203 %if repo_instance.locked[0]:
203 %if repo_instance.locked[0]:
204 <p class="locking_locked">
204 <p class="locking_locked">
205 <i class="icon-repo-lock"></i>
205 <i class="icon-repo-lock"></i>
206 ${_('Repository locked by %(user)s') % {'user': h.person_by_id(repo_instance.locked[0])}}
206 ${_('Repository locked by %(user)s') % {'user': h.person_by_id(repo_instance.locked[0])}}
207 </p>
207 </p>
208 %elif repo_instance.enable_locking:
208 %elif repo_instance.enable_locking:
209 <p class="locking_unlocked">
209 <p class="locking_unlocked">
210 <i class="icon-repo-unlock"></i>
210 <i class="icon-repo-unlock"></i>
211 ${_('Repository not locked. Pull repository to lock it.')}
211 ${_('Repository not locked. Pull repository to lock it.')}
212 </p>
212 </p>
213 %endif
213 %endif
214
214
215 </div>
215 </div>
216 </%def>
216 </%def>
217
217
218 <%def name="repo_menu(active=None)">
218 <%def name="repo_menu(active=None)">
219 <%
219 <%
220 def is_active(selected):
220 def is_active(selected):
221 if selected == active:
221 if selected == active:
222 return "active"
222 return "active"
223 %>
223 %>
224
224
225 <!--- CONTEXT BAR -->
225 <!--- CONTEXT BAR -->
226 <div id="context-bar">
226 <div id="context-bar">
227 <div class="wrapper">
227 <div class="wrapper">
228 <ul id="context-pages" class="horizontal-list navigation">
228 <ul id="context-pages" class="horizontal-list navigation">
229 <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('summary')}"><a class="menulink" href="${h.url('summary_home', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li>
230 <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('changelog')}"><a class="menulink" href="${h.url('changelog_home', repo_name=c.repo_name)}"><div class="menulabel">${_('Changelog')}</div></a></li>
231 <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('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>
232 <li class="${is_active('compare')}">
232 <li class="${is_active('compare')}">
233 <a class="menulink" href="${h.url('compare_home',repo_name=c.repo_name)}"><div class="menulabel">${_('Compare')}</div></a>
233 <a class="menulink" href="${h.url('compare_home',repo_name=c.repo_name)}"><div class="menulabel">${_('Compare')}</div></a>
234 </li>
234 </li>
235 ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()"
235 ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()"
236 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
236 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
237 <li class="${is_active('showpullrequest')}">
237 <li class="${is_active('showpullrequest')}">
238 <a class="menulink" href="${h.url('pullrequest_show_all',repo_name=c.repo_name)}" title="${_('Show Pull Requests for %s') % c.repo_name}">
238 <a class="menulink" href="${h.url('pullrequest_show_all',repo_name=c.repo_name)}" title="${_('Show Pull Requests for %s') % c.repo_name}">
239 %if c.repository_pull_requests:
239 %if c.repository_pull_requests:
240 <span class="pr_notifications">${c.repository_pull_requests}</span>
240 <span class="pr_notifications">${c.repository_pull_requests}</span>
241 %endif
241 %endif
242 <div class="menulabel">${_('Pull Requests')}</div>
242 <div class="menulabel">${_('Pull Requests')}</div>
243 </a>
243 </a>
244 </li>
244 </li>
245 %endif
245 %endif
246 <li class="${is_active('options')}">
246 <li class="${is_active('options')}">
247 <a class="menulink" href="#" class="dropdown"><div class="menulabel">${_('Options')} <div class="show_more"></div></div></a>
247 <a class="menulink" href="#" class="dropdown"><div class="menulabel">${_('Options')} <div class="show_more"></div></div></a>
248 <ul class="submenu">
248 <ul class="submenu">
249 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
249 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
250 <li><a href="${h.url('edit_repo',repo_name=c.repo_name)}">${_('Settings')}</a></li>
250 <li><a href="${h.url('edit_repo',repo_name=c.repo_name)}">${_('Settings')}</a></li>
251 %endif
251 %endif
252 %if c.rhodecode_db_repo.fork:
252 %if c.rhodecode_db_repo.fork:
253 <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 <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)}">
254 ${_('Compare fork')}</a></li>
254 ${_('Compare fork')}</a></li>
255 %endif
255 %endif
256
256
257 <li><a href="${h.url('search_repo_home',repo_name=c.repo_name)}">${_('Search')}</a></li>
257 <li><a href="${h.url('search_repo_home',repo_name=c.repo_name)}">${_('Search')}</a></li>
258
258
259 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking:
259 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking:
260 %if c.rhodecode_db_repo.locked[0]:
260 %if c.rhodecode_db_repo.locked[0]:
261 <li><a class="locking_del" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Unlock')}</a></li>
261 <li><a class="locking_del" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Unlock')}</a></li>
262 %else:
262 %else:
263 <li><a class="locking_add" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Lock')}</a></li>
263 <li><a class="locking_add" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Lock')}</a></li>
264 %endif
264 %endif
265 %endif
265 %endif
266 %if c.rhodecode_user.username != h.DEFAULT_USER:
266 %if c.rhodecode_user.username != h.DEFAULT_USER:
267 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
267 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
268 <li><a href="${h.url('repo_fork_home',repo_name=c.repo_name)}">${_('Fork')}</a></li>
268 <li><a href="${h.url('repo_fork_home',repo_name=c.repo_name)}">${_('Fork')}</a></li>
269 <li><a href="${h.url('pullrequest_home',repo_name=c.repo_name)}">${_('Create Pull Request')}</a></li>
269 <li><a href="${h.url('pullrequest_home',repo_name=c.repo_name)}">${_('Create Pull Request')}</a></li>
270 %endif
270 %endif
271 %endif
271 %endif
272 </ul>
272 </ul>
273 </li>
273 </li>
274 </ul>
274 </ul>
275 </div>
275 </div>
276 <div class="clear"></div>
276 <div class="clear"></div>
277 </div>
277 </div>
278 <!--- END CONTEXT BAR -->
278 <!--- END CONTEXT BAR -->
279
279
280 </%def>
280 </%def>
281
281
282 <%def name="usermenu()">
282 <%def name="usermenu()">
283 ## USER MENU
283 ## USER MENU
284 <li id="quick_login_li">
284 <li id="quick_login_li">
285 <a id="quick_login_link" class="menulink childs">
285 <a id="quick_login_link" class="menulink childs">
286 ${gravatar(c.rhodecode_user.email, 20)}
286 ${gravatar(c.rhodecode_user.email, 20)}
287 <span class="user">
287 <span class="user">
288 %if c.rhodecode_user.username != h.DEFAULT_USER:
288 %if c.rhodecode_user.username != h.DEFAULT_USER:
289 <span class="menu_link_user">${c.rhodecode_user.username}</span><div class="show_more"></div>
289 <span class="menu_link_user">${c.rhodecode_user.username}</span><div class="show_more"></div>
290 %else:
290 %else:
291 <span>${_('Sign in')}</span>
291 <span>${_('Sign in')}</span>
292 %endif
292 %endif
293 </span>
293 </span>
294 </a>
294 </a>
295
295
296 <div class="user-menu submenu">
296 <div class="user-menu submenu">
297 <div id="quick_login">
297 <div id="quick_login">
298 %if c.rhodecode_user.username == h.DEFAULT_USER:
298 %if c.rhodecode_user.username == h.DEFAULT_USER:
299 <h4>${_('Sign in to your account')}</h4>
299 <h4>${_('Sign in to your account')}</h4>
300 ${h.form(h.route_path('login', _query={'came_from': h.url.current()}), needs_csrf_token=False)}
300 ${h.form(h.route_path('login', _query={'came_from': h.url.current()}), needs_csrf_token=False)}
301 <div class="form form-vertical">
301 <div class="form form-vertical">
302 <div class="fields">
302 <div class="fields">
303 <div class="field">
303 <div class="field">
304 <div class="label">
304 <div class="label">
305 <label for="username">${_('Username')}:</label>
305 <label for="username">${_('Username')}:</label>
306 </div>
306 </div>
307 <div class="input">
307 <div class="input">
308 ${h.text('username',class_='focus',tabindex=1)}
308 ${h.text('username',class_='focus',tabindex=1)}
309 </div>
309 </div>
310
310
311 </div>
311 </div>
312 <div class="field">
312 <div class="field">
313 <div class="label">
313 <div class="label">
314 <label for="password">${_('Password')}:</label>
314 <label for="password">${_('Password')}:</label>
315 <span class="forgot_password">${h.link_to(_('(Forgot password?)'),h.route_path('reset_password'))}</span>
315 <span class="forgot_password">${h.link_to(_('(Forgot password?)'),h.route_path('reset_password'))}</span>
316 </div>
316 </div>
317 <div class="input">
317 <div class="input">
318 ${h.password('password',class_='focus',tabindex=2)}
318 ${h.password('password',class_='focus',tabindex=2)}
319 </div>
319 </div>
320 </div>
320 </div>
321 <div class="buttons">
321 <div class="buttons">
322 <div class="register">
322 <div class="register">
323 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
323 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
324 ${h.link_to(_("Don't have an account ?"),h.route_path('register'))}
324 ${h.link_to(_("Don't have an account ?"),h.route_path('register'))}
325 %endif
325 %endif
326 </div>
326 </div>
327 <div class="submit">
327 <div class="submit">
328 ${h.submit('sign_in',_('Sign In'),class_="btn btn-small",tabindex=3)}
328 ${h.submit('sign_in',_('Sign In'),class_="btn btn-small",tabindex=3)}
329 </div>
329 </div>
330 </div>
330 </div>
331 </div>
331 </div>
332 </div>
332 </div>
333 ${h.end_form()}
333 ${h.end_form()}
334 %else:
334 %else:
335 <div class="">
335 <div class="">
336 <div class="big_gravatar">${gravatar(c.rhodecode_user.email, 48)}</div>
336 <div class="big_gravatar">${gravatar(c.rhodecode_user.email, 48)}</div>
337 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
337 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
338 <div class="email">${c.rhodecode_user.email}</div>
338 <div class="email">${c.rhodecode_user.email}</div>
339 </div>
339 </div>
340 <div class="">
340 <div class="">
341 <ol class="links">
341 <ol class="links">
342 <li>${h.link_to(_(u'My account'),h.url('my_account'))}</li>
342 <li>${h.link_to(_(u'My account'),h.url('my_account'))}</li>
343 <li class="logout">
343 <li class="logout">
344 ${h.secure_form(h.route_path('logout'))}
344 ${h.secure_form(h.route_path('logout'))}
345 ${h.submit('log_out', _(u'Sign Out'),class_="btn btn-primary")}
345 ${h.submit('log_out', _(u'Sign Out'),class_="btn btn-primary")}
346 ${h.end_form()}
346 ${h.end_form()}
347 </li>
347 </li>
348 </ol>
348 </ol>
349 </div>
349 </div>
350 %endif
350 %endif
351 </div>
351 </div>
352 </div>
352 </div>
353 %if c.rhodecode_user.username != h.DEFAULT_USER:
353 %if c.rhodecode_user.username != h.DEFAULT_USER:
354 <div class="pill_container">
354 <div class="pill_container">
355 % if c.unread_notifications == 0:
355 % if c.unread_notifications == 0:
356 <a class="menu_link_notifications empty" href="${h.url('notifications')}">${c.unread_notifications}</a>
356 <a class="menu_link_notifications empty" href="${h.url('notifications')}">${c.unread_notifications}</a>
357 % else:
357 % else:
358 <a class="menu_link_notifications" href="${h.url('notifications')}">${c.unread_notifications}</a>
358 <a class="menu_link_notifications" href="${h.url('notifications')}">${c.unread_notifications}</a>
359 % endif
359 % endif
360 </div>
360 </div>
361 % endif
361 % endif
362 </li>
362 </li>
363 </%def>
363 </%def>
364
364
365 <%def name="menu_items(active=None)">
365 <%def name="menu_items(active=None)">
366 <%
366 <%
367 def is_active(selected):
367 def is_active(selected):
368 if selected == active:
368 if selected == active:
369 return "active"
369 return "active"
370 return ""
370 return ""
371 %>
371 %>
372 <ul id="quick" class="main_nav navigation horizontal-list">
372 <ul id="quick" class="main_nav navigation horizontal-list">
373 <!-- repo switcher -->
373 <!-- repo switcher -->
374 <li class="${is_active('repositories')} repo_switcher_li has_select2">
374 <li class="${is_active('repositories')} repo_switcher_li has_select2">
375 <input id="repo_switcher" name="repo_switcher" type="hidden">
375 <input id="repo_switcher" name="repo_switcher" type="hidden">
376 </li>
376 </li>
377
377
378 ## ROOT MENU
378 ## ROOT MENU
379 %if c.rhodecode_user.username != h.DEFAULT_USER:
379 %if c.rhodecode_user.username != h.DEFAULT_USER:
380 <li class="${is_active('journal')}">
380 <li class="${is_active('journal')}">
381 <a class="menulink" title="${_('Show activity journal')}" href="${h.url('journal')}">
381 <a class="menulink" title="${_('Show activity journal')}" href="${h.url('journal')}">
382 <div class="menulabel">${_('Journal')}</div>
382 <div class="menulabel">${_('Journal')}</div>
383 </a>
383 </a>
384 </li>
384 </li>
385 %else:
385 %else:
386 <li class="${is_active('journal')}">
386 <li class="${is_active('journal')}">
387 <a class="menulink" title="${_('Show Public activity journal')}" href="${h.url('public_journal')}">
387 <a class="menulink" title="${_('Show Public activity journal')}" href="${h.url('public_journal')}">
388 <div class="menulabel">${_('Public journal')}</div>
388 <div class="menulabel">${_('Public journal')}</div>
389 </a>
389 </a>
390 </li>
390 </li>
391 %endif
391 %endif
392 <li class="${is_active('gists')}">
392 <li class="${is_active('gists')}">
393 <a class="menulink childs" title="${_('Show Gists')}" href="${h.url('gists')}">
393 <a class="menulink childs" title="${_('Show Gists')}" href="${h.url('gists')}">
394 <div class="menulabel">${_('Gists')}</div>
394 <div class="menulabel">${_('Gists')}</div>
395 </a>
395 </a>
396 </li>
396 </li>
397 <li class="${is_active('search')}">
397 <li class="${is_active('search')}">
398 <a class="menulink" title="${_('Search in repositories you have access to')}" href="${h.url('search')}">
398 <a class="menulink" title="${_('Search in repositories you have access to')}" href="${h.url('search')}">
399 <div class="menulabel">${_('Search')}</div>
399 <div class="menulabel">${_('Search')}</div>
400 </a>
400 </a>
401 </li>
401 </li>
402 % if h.HasPermissionAll('hg.admin')('access admin main page'):
402 % if h.HasPermissionAll('hg.admin')('access admin main page'):
403 <li class="${is_active('admin')}">
403 <li class="${is_active('admin')}">
404 <a class="menulink childs" title="${_('Admin settings')}" href="#" onclick="return false;">
404 <a class="menulink childs" title="${_('Admin settings')}" href="#" onclick="return false;">
405 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
405 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
406 </a>
406 </a>
407 ${admin_menu()}
407 ${admin_menu()}
408 </li>
408 </li>
409 % elif c.rhodecode_user.repositories_admin or c.rhodecode_user.repository_groups_admin or c.rhodecode_user.user_groups_admin:
409 % elif c.rhodecode_user.repositories_admin or c.rhodecode_user.repository_groups_admin or c.rhodecode_user.user_groups_admin:
410 <li class="${is_active('admin')}">
410 <li class="${is_active('admin')}">
411 <a class="menulink childs" title="${_('Delegated Admin settings')}">
411 <a class="menulink childs" title="${_('Delegated Admin settings')}">
412 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
412 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
413 </a>
413 </a>
414 ${admin_menu_simple(c.rhodecode_user.repositories_admin,
414 ${admin_menu_simple(c.rhodecode_user.repositories_admin,
415 c.rhodecode_user.repository_groups_admin,
415 c.rhodecode_user.repository_groups_admin,
416 c.rhodecode_user.user_groups_admin or h.HasPermissionAny('hg.usergroup.create.true')())}
416 c.rhodecode_user.user_groups_admin or h.HasPermissionAny('hg.usergroup.create.true')())}
417 </li>
417 </li>
418 % endif
418 % endif
419 % if c.debug_style:
419 % if c.debug_style:
420 <li class="${is_active('debug_style')}">
420 <li class="${is_active('debug_style')}">
421 <a class="menulink" title="${_('Style')}" href="${h.url('debug_style_home')}">
421 <a class="menulink" title="${_('Style')}" href="${h.url('debug_style_home')}">
422 <div class="menulabel">${_('Style')}</div>
422 <div class="menulabel">${_('Style')}</div>
423 </a>
423 </a>
424 </li>
424 </li>
425 % endif
425 % endif
426 ## render extra user menu
426 ## render extra user menu
427 ${usermenu()}
427 ${usermenu()}
428 </ul>
428 </ul>
429
429
430 <script type="text/javascript">
430 <script type="text/javascript">
431 var visual_show_public_icon = "${c.visual.show_public_icon}" == "True";
431 var visual_show_public_icon = "${c.visual.show_public_icon}" == "True";
432
432
433 /*format the look of items in the list*/
433 /*format the look of items in the list*/
434 var format = function(state, escapeMarkup){
434 var format = function(state, escapeMarkup){
435 if (!state.id){
435 if (!state.id){
436 return state.text; // optgroup
436 return state.text; // optgroup
437 }
437 }
438 var obj_dict = state.obj;
438 var obj_dict = state.obj;
439 var tmpl = '';
439 var tmpl = '';
440
440
441 if(obj_dict && state.type == 'repo'){
441 if(obj_dict && state.type == 'repo'){
442 if(obj_dict['repo_type'] === 'hg'){
442 if(obj_dict['repo_type'] === 'hg'){
443 tmpl += '<i class="icon-hg"></i> ';
443 tmpl += '<i class="icon-hg"></i> ';
444 }
444 }
445 else if(obj_dict['repo_type'] === 'git'){
445 else if(obj_dict['repo_type'] === 'git'){
446 tmpl += '<i class="icon-git"></i> ';
446 tmpl += '<i class="icon-git"></i> ';
447 }
447 }
448 else if(obj_dict['repo_type'] === 'svn'){
448 else if(obj_dict['repo_type'] === 'svn'){
449 tmpl += '<i class="icon-svn"></i> ';
449 tmpl += '<i class="icon-svn"></i> ';
450 }
450 }
451 if(obj_dict['private']){
451 if(obj_dict['private']){
452 tmpl += '<i class="icon-lock" ></i> ';
452 tmpl += '<i class="icon-lock" ></i> ';
453 }
453 }
454 else if(visual_show_public_icon){
454 else if(visual_show_public_icon){
455 tmpl += '<i class="icon-unlock-alt"></i> ';
455 tmpl += '<i class="icon-unlock-alt"></i> ';
456 }
456 }
457 }
457 }
458 if(obj_dict && state.type == 'commit') {
459 tmpl += '<i class="icon-tag"></i>';
460 }
458 if(obj_dict && state.type == 'group'){
461 if(obj_dict && state.type == 'group'){
459 tmpl += '<i class="icon-folder-close"></i> ';
462 tmpl += '<i class="icon-folder-close"></i> ';
460 }
463 }
461 tmpl += escapeMarkup(state.text);
464 tmpl += escapeMarkup(state.text);
462 return tmpl;
465 return tmpl;
463 };
466 };
464
467
465 var formatResult = function(result, container, query, escapeMarkup) {
468 var formatResult = function(result, container, query, escapeMarkup) {
466 return format(result, escapeMarkup);
469 return format(result, escapeMarkup);
467 };
470 };
468
471
469 var formatSelection = function(data, container, escapeMarkup) {
472 var formatSelection = function(data, container, escapeMarkup) {
470 return format(data, escapeMarkup);
473 return format(data, escapeMarkup);
471 };
474 };
472
475
473 $("#repo_switcher").select2({
476 $("#repo_switcher").select2({
474 cachedDataSource: {},
477 cachedDataSource: {},
475 minimumInputLength: 2,
478 minimumInputLength: 2,
476 placeholder: '<div class="menulabel">${_('Go to')} <div class="show_more"></div></div>',
479 placeholder: '<div class="menulabel">${_('Go to')} <div class="show_more"></div></div>',
477 dropdownAutoWidth: true,
480 dropdownAutoWidth: true,
478 formatResult: formatResult,
481 formatResult: formatResult,
479 formatSelection: formatSelection,
482 formatSelection: formatSelection,
480 containerCssClass: "repo-switcher",
483 containerCssClass: "repo-switcher",
481 dropdownCssClass: "repo-switcher-dropdown",
484 dropdownCssClass: "repo-switcher-dropdown",
482 escapeMarkup: function(m){
485 escapeMarkup: function(m){
483 // don't escape our custom placeholder
486 // don't escape our custom placeholder
484 if(m.substr(0,23) == '<div class="menulabel">'){
487 if(m.substr(0,23) == '<div class="menulabel">'){
485 return m;
488 return m;
486 }
489 }
487
490
488 return Select2.util.escapeMarkup(m);
491 return Select2.util.escapeMarkup(m);
489 },
492 },
490 query: $.debounce(250, function(query){
493 query: $.debounce(250, function(query){
491 self = this;
494 self = this;
492 var cacheKey = query.term;
495 var cacheKey = query.term;
493 var cachedData = self.cachedDataSource[cacheKey];
496 var cachedData = self.cachedDataSource[cacheKey];
494
497
495 if (cachedData) {
498 if (cachedData) {
496 query.callback({results: cachedData.results});
499 query.callback({results: cachedData.results});
497 } else {
500 } else {
498 $.ajax({
501 $.ajax({
499 url: "${h.url('repo_switcher_data')}",
502 url: "${h.url('goto_switcher_data')}",
500 data: {'query': query.term},
503 data: {'query': query.term},
501 dataType: 'json',
504 dataType: 'json',
502 type: 'GET',
505 type: 'GET',
503 success: function(data) {
506 success: function(data) {
504 self.cachedDataSource[cacheKey] = data;
507 self.cachedDataSource[cacheKey] = data;
505 query.callback({results: data.results});
508 query.callback({results: data.results});
506 },
509 },
507 error: function(data, textStatus, errorThrown) {
510 error: function(data, textStatus, errorThrown) {
508 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
511 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
509 }
512 }
510 })
513 })
511 }
514 }
512 })
515 })
513 });
516 });
514
517
515 $("#repo_switcher").on('select2-selecting', function(e){
518 $("#repo_switcher").on('select2-selecting', function(e){
516 e.preventDefault();
519 e.preventDefault();
517 window.location = pyroutes.url('summary_home', {'repo_name': e.val});
520 window.location = e.choice.url;
518 });
521 });
519
522
520 ## Global mouse bindings ##
523 ## Global mouse bindings ##
521
524
522 // general help "?"
525 // general help "?"
523 Mousetrap.bind(['?'], function(e) {
526 Mousetrap.bind(['?'], function(e) {
524 $('#help_kb').modal({})
527 $('#help_kb').modal({})
525 });
528 });
526
529
527 // / open the quick filter
530 // / open the quick filter
528 Mousetrap.bind(['/'], function(e) {
531 Mousetrap.bind(['/'], function(e) {
529 $("#repo_switcher").select2("open");
532 $("#repo_switcher").select2("open");
530
533
531 // return false to prevent default browser behavior
534 // return false to prevent default browser behavior
532 // and stop event from bubbling
535 // and stop event from bubbling
533 return false;
536 return false;
534 });
537 });
535
538
536 // general nav g + action
539 // general nav g + action
537 Mousetrap.bind(['g h'], function(e) {
540 Mousetrap.bind(['g h'], function(e) {
538 window.location = pyroutes.url('home');
541 window.location = pyroutes.url('home');
539 });
542 });
540 Mousetrap.bind(['g g'], function(e) {
543 Mousetrap.bind(['g g'], function(e) {
541 window.location = pyroutes.url('gists', {'private':1});
544 window.location = pyroutes.url('gists', {'private':1});
542 });
545 });
543 Mousetrap.bind(['g G'], function(e) {
546 Mousetrap.bind(['g G'], function(e) {
544 window.location = pyroutes.url('gists', {'public':1});
547 window.location = pyroutes.url('gists', {'public':1});
545 });
548 });
546 Mousetrap.bind(['n g'], function(e) {
549 Mousetrap.bind(['n g'], function(e) {
547 window.location = pyroutes.url('new_gist');
550 window.location = pyroutes.url('new_gist');
548 });
551 });
549 Mousetrap.bind(['n r'], function(e) {
552 Mousetrap.bind(['n r'], function(e) {
550 window.location = pyroutes.url('new_repo');
553 window.location = pyroutes.url('new_repo');
551 });
554 });
552
555
553 % if hasattr(c, 'repo_name') and hasattr(c, 'rhodecode_db_repo'):
556 % if hasattr(c, 'repo_name') and hasattr(c, 'rhodecode_db_repo'):
554 // nav in repo context
557 // nav in repo context
555 Mousetrap.bind(['g s'], function(e) {
558 Mousetrap.bind(['g s'], function(e) {
556 window.location = pyroutes.url('summary_home', {'repo_name': REPO_NAME});
559 window.location = pyroutes.url('summary_home', {'repo_name': REPO_NAME});
557 });
560 });
558 Mousetrap.bind(['g c'], function(e) {
561 Mousetrap.bind(['g c'], function(e) {
559 window.location = pyroutes.url('changelog_home', {'repo_name': REPO_NAME});
562 window.location = pyroutes.url('changelog_home', {'repo_name': REPO_NAME});
560 });
563 });
561 Mousetrap.bind(['g F'], function(e) {
564 Mousetrap.bind(['g F'], function(e) {
562 window.location = pyroutes.url('files_home', {'repo_name': REPO_NAME, 'revision': '${c.rhodecode_db_repo.landing_rev[1]}', 'f_path': '', 'search': '1'});
565 window.location = pyroutes.url('files_home', {'repo_name': REPO_NAME, 'revision': '${c.rhodecode_db_repo.landing_rev[1]}', 'f_path': '', 'search': '1'});
563 });
566 });
564 Mousetrap.bind(['g f'], function(e) {
567 Mousetrap.bind(['g f'], function(e) {
565 window.location = pyroutes.url('files_home', {'repo_name': REPO_NAME, 'revision': '${c.rhodecode_db_repo.landing_rev[1]}', 'f_path': ''});
568 window.location = pyroutes.url('files_home', {'repo_name': REPO_NAME, 'revision': '${c.rhodecode_db_repo.landing_rev[1]}', 'f_path': ''});
566 });
569 });
567 Mousetrap.bind(['g p'], function(e) {
570 Mousetrap.bind(['g p'], function(e) {
568 window.location = pyroutes.url('pullrequest_show_all', {'repo_name': REPO_NAME});
571 window.location = pyroutes.url('pullrequest_show_all', {'repo_name': REPO_NAME});
569 });
572 });
570 Mousetrap.bind(['g o'], function(e) {
573 Mousetrap.bind(['g o'], function(e) {
571 window.location = pyroutes.url('edit_repo', {'repo_name': REPO_NAME});
574 window.location = pyroutes.url('edit_repo', {'repo_name': REPO_NAME});
572 });
575 });
573 Mousetrap.bind(['g O'], function(e) {
576 Mousetrap.bind(['g O'], function(e) {
574 window.location = pyroutes.url('edit_repo_perms', {'repo_name': REPO_NAME});
577 window.location = pyroutes.url('edit_repo_perms', {'repo_name': REPO_NAME});
575 });
578 });
576 % endif
579 % endif
577
580
578 </script>
581 </script>
579 <script src="${h.url('/js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script>
582 <script src="${h.url('/js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script>
580 </%def>
583 </%def>
581
584
582 <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
585 <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
583 <div class="modal-dialog">
586 <div class="modal-dialog">
584 <div class="modal-content">
587 <div class="modal-content">
585 <div class="modal-header">
588 <div class="modal-header">
586 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
589 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
587 <h4 class="modal-title" id="myModalLabel">${_('Keyboard shortcuts')}</h4>
590 <h4 class="modal-title" id="myModalLabel">${_('Keyboard shortcuts')}</h4>
588 </div>
591 </div>
589 <div class="modal-body">
592 <div class="modal-body">
590 <div class="block-left">
593 <div class="block-left">
591 <table class="keyboard-mappings">
594 <table class="keyboard-mappings">
592 <tbody>
595 <tbody>
593 <tr>
596 <tr>
594 <th></th>
597 <th></th>
595 <th>${_('Site-wide shortcuts')}</th>
598 <th>${_('Site-wide shortcuts')}</th>
596 </tr>
599 </tr>
597 <%
600 <%
598 elems = [
601 elems = [
599 ('/', 'Open quick search box'),
602 ('/', 'Open quick search box'),
600 ('g h', 'Goto home page'),
603 ('g h', 'Goto home page'),
601 ('g g', 'Goto my private gists page'),
604 ('g g', 'Goto my private gists page'),
602 ('g G', 'Goto my public gists page'),
605 ('g G', 'Goto my public gists page'),
603 ('n r', 'New repository page'),
606 ('n r', 'New repository page'),
604 ('n g', 'New gist page'),
607 ('n g', 'New gist page'),
605 ]
608 ]
606 %>
609 %>
607 %for key, desc in elems:
610 %for key, desc in elems:
608 <tr>
611 <tr>
609 <td class="keys">
612 <td class="keys">
610 <span class="key tag">${key}</span>
613 <span class="key tag">${key}</span>
611 </td>
614 </td>
612 <td>${desc}</td>
615 <td>${desc}</td>
613 </tr>
616 </tr>
614 %endfor
617 %endfor
615 </tbody>
618 </tbody>
616 </table>
619 </table>
617 </div>
620 </div>
618 <div class="block-left">
621 <div class="block-left">
619 <table class="keyboard-mappings">
622 <table class="keyboard-mappings">
620 <tbody>
623 <tbody>
621 <tr>
624 <tr>
622 <th></th>
625 <th></th>
623 <th>${_('Repositories')}</th>
626 <th>${_('Repositories')}</th>
624 </tr>
627 </tr>
625 <%
628 <%
626 elems = [
629 elems = [
627 ('g s', 'Goto summary page'),
630 ('g s', 'Goto summary page'),
628 ('g c', 'Goto changelog page'),
631 ('g c', 'Goto changelog page'),
629 ('g f', 'Goto files page'),
632 ('g f', 'Goto files page'),
630 ('g F', 'Goto files page with file search activated'),
633 ('g F', 'Goto files page with file search activated'),
631 ('g p', 'Goto pull requests page'),
634 ('g p', 'Goto pull requests page'),
632 ('g o', 'Goto repository settings'),
635 ('g o', 'Goto repository settings'),
633 ('g O', 'Goto repository permissions settings'),
636 ('g O', 'Goto repository permissions settings'),
634 ]
637 ]
635 %>
638 %>
636 %for key, desc in elems:
639 %for key, desc in elems:
637 <tr>
640 <tr>
638 <td class="keys">
641 <td class="keys">
639 <span class="key tag">${key}</span>
642 <span class="key tag">${key}</span>
640 </td>
643 </td>
641 <td>${desc}</td>
644 <td>${desc}</td>
642 </tr>
645 </tr>
643 %endfor
646 %endfor
644 </tbody>
647 </tbody>
645 </table>
648 </table>
646 </div>
649 </div>
647 </div>
650 </div>
648 <div class="modal-footer">
651 <div class="modal-footer">
649 </div>
652 </div>
650 </div><!-- /.modal-content -->
653 </div><!-- /.modal-content -->
651 </div><!-- /.modal-dialog -->
654 </div><!-- /.modal-dialog -->
652 </div><!-- /.modal -->
655 </div><!-- /.modal -->
@@ -1,353 +1,365 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 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 json
21 import json
22
22
23 from mock import patch
23 from mock import patch
24 import pytest
24 import pytest
25
25
26 import rhodecode
26 import rhodecode
27 from rhodecode.lib.utils import map_groups
27 from rhodecode.lib.utils import map_groups
28 from rhodecode.model.db import Repository, User, RepoGroup
28 from rhodecode.model.db import Repository, User, RepoGroup
29 from rhodecode.model.meta import Session
29 from rhodecode.model.meta import Session
30 from rhodecode.model.repo import RepoModel
30 from rhodecode.model.repo import RepoModel
31 from rhodecode.model.repo_group import RepoGroupModel
31 from rhodecode.model.repo_group import RepoGroupModel
32 from rhodecode.model.settings import SettingsModel
32 from rhodecode.model.settings import SettingsModel
33 from rhodecode.tests import TestController, url, TEST_USER_ADMIN_LOGIN
33 from rhodecode.tests import TestController, url, TEST_USER_ADMIN_LOGIN
34 from rhodecode.tests.fixture import Fixture
34 from rhodecode.tests.fixture import Fixture
35
35
36
36
37 fixture = Fixture()
37 fixture = Fixture()
38
38
39
39
40 class TestHomeController(TestController):
40 class TestHomeController(TestController):
41
41
42 def test_index(self):
42 def test_index(self):
43 self.log_user()
43 self.log_user()
44 response = self.app.get(url(controller='home', action='index'))
44 response = self.app.get(url(controller='home', action='index'))
45 # if global permission is set
45 # if global permission is set
46 response.mustcontain('Add Repository')
46 response.mustcontain('Add Repository')
47
47
48 # search for objects inside the JavaScript JSON
48 # search for objects inside the JavaScript JSON
49 for repo in Repository.getAll():
49 for repo in Repository.getAll():
50 response.mustcontain('"name_raw": "%s"' % repo.repo_name)
50 response.mustcontain('"name_raw": "%s"' % repo.repo_name)
51
51
52 def test_index_contains_backend_specific_details(self, backend):
52 def test_index_contains_backend_specific_details(self, backend):
53 self.log_user()
53 self.log_user()
54 response = self.app.get(url(controller='home', action='index'))
54 response = self.app.get(url(controller='home', action='index'))
55 tip = backend.repo.get_commit().raw_id
55 tip = backend.repo.get_commit().raw_id
56
56
57 # html in javascript variable:
57 # html in javascript variable:
58 response.mustcontain(r'<i class=\"icon-%s\"' % (backend.alias, ))
58 response.mustcontain(r'<i class=\"icon-%s\"' % (backend.alias, ))
59 response.mustcontain(r'href=\"/%s\"' % (backend.repo_name, ))
59 response.mustcontain(r'href=\"/%s\"' % (backend.repo_name, ))
60
60
61 response.mustcontain("""/%s/changeset/%s""" % (backend.repo_name, tip))
61 response.mustcontain("""/%s/changeset/%s""" % (backend.repo_name, tip))
62 response.mustcontain("""Added a symlink""")
62 response.mustcontain("""Added a symlink""")
63
63
64 def test_index_with_anonymous_access_disabled(self):
64 def test_index_with_anonymous_access_disabled(self):
65 with fixture.anon_access(False):
65 with fixture.anon_access(False):
66 response = self.app.get(url(controller='home', action='index'),
66 response = self.app.get(url(controller='home', action='index'),
67 status=302)
67 status=302)
68 assert 'login' in response.location
68 assert 'login' in response.location
69
69
70 def test_index_page_on_groups(self, autologin_user, repo_group):
70 def test_index_page_on_groups(self, autologin_user, repo_group):
71 response = self.app.get(url('repo_group_home', group_name='gr1'))
71 response = self.app.get(url('repo_group_home', group_name='gr1'))
72 response.mustcontain("gr1/repo_in_group")
72 response.mustcontain("gr1/repo_in_group")
73
73
74 def test_index_page_on_group_with_trailing_slash(
74 def test_index_page_on_group_with_trailing_slash(
75 self, autologin_user, repo_group):
75 self, autologin_user, repo_group):
76 response = self.app.get(url('repo_group_home', group_name='gr1') + '/')
76 response = self.app.get(url('repo_group_home', group_name='gr1') + '/')
77 response.mustcontain("gr1/repo_in_group")
77 response.mustcontain("gr1/repo_in_group")
78
78
79 @pytest.fixture(scope='class')
79 @pytest.fixture(scope='class')
80 def repo_group(self, request):
80 def repo_group(self, request):
81 gr = fixture.create_repo_group('gr1')
81 gr = fixture.create_repo_group('gr1')
82 fixture.create_repo(name='gr1/repo_in_group', repo_group=gr)
82 fixture.create_repo(name='gr1/repo_in_group', repo_group=gr)
83
83
84 @request.addfinalizer
84 @request.addfinalizer
85 def cleanup():
85 def cleanup():
86 RepoModel().delete('gr1/repo_in_group')
86 RepoModel().delete('gr1/repo_in_group')
87 RepoGroupModel().delete(repo_group='gr1', force_delete=True)
87 RepoGroupModel().delete(repo_group='gr1', force_delete=True)
88 Session().commit()
88 Session().commit()
89
89
90 def test_index_with_name_with_tags(self, autologin_user):
90 def test_index_with_name_with_tags(self, autologin_user):
91 user = User.get_by_username('test_admin')
91 user = User.get_by_username('test_admin')
92 user.name = '<img src="/image1" onload="alert(\'Hello, World!\');">'
92 user.name = '<img src="/image1" onload="alert(\'Hello, World!\');">'
93 user.lastname = (
93 user.lastname = (
94 '<img src="/image2" onload="alert(\'Hello, World!\');">')
94 '<img src="/image2" onload="alert(\'Hello, World!\');">')
95 Session().add(user)
95 Session().add(user)
96 Session().commit()
96 Session().commit()
97
97
98 response = self.app.get(url(controller='home', action='index'))
98 response = self.app.get(url(controller='home', action='index'))
99 response.mustcontain(
99 response.mustcontain(
100 '&lt;img src=&#34;/image1&#34; onload=&#34;'
100 '&lt;img src=&#34;/image1&#34; onload=&#34;'
101 'alert(&#39;Hello, World!&#39;);&#34;&gt;')
101 'alert(&#39;Hello, World!&#39;);&#34;&gt;')
102 response.mustcontain(
102 response.mustcontain(
103 '&lt;img src=&#34;/image2&#34; onload=&#34;'
103 '&lt;img src=&#34;/image2&#34; onload=&#34;'
104 'alert(&#39;Hello, World!&#39;);&#34;&gt;')
104 'alert(&#39;Hello, World!&#39;);&#34;&gt;')
105
105
106 @pytest.mark.parametrize("name, state", [
106 @pytest.mark.parametrize("name, state", [
107 ('Disabled', False),
107 ('Disabled', False),
108 ('Enabled', True),
108 ('Enabled', True),
109 ])
109 ])
110 def test_index_show_version(self, autologin_user, name, state):
110 def test_index_show_version(self, autologin_user, name, state):
111 version_string = 'RhodeCode Enterprise %s' % rhodecode.__version__
111 version_string = 'RhodeCode Enterprise %s' % rhodecode.__version__
112
112
113 show = SettingsModel().get_setting_by_name('show_version')
113 show = SettingsModel().get_setting_by_name('show_version')
114 show.app_settings_value = state
114 show.app_settings_value = state
115 Session().add(show)
115 Session().add(show)
116 Session().commit()
116 Session().commit()
117 response = self.app.get(url(controller='home', action='index'))
117 response = self.app.get(url(controller='home', action='index'))
118 if state is True:
118 if state is True:
119 response.mustcontain(version_string)
119 response.mustcontain(version_string)
120 if state is False:
120 if state is False:
121 response.mustcontain(no=[version_string])
121 response.mustcontain(no=[version_string])
122
122
123
123
124 class TestUserAutocompleteData(TestController):
124 class TestUserAutocompleteData(TestController):
125 def test_returns_list_of_users(self, user_util):
125 def test_returns_list_of_users(self, user_util):
126 self.log_user()
126 self.log_user()
127 user = user_util.create_user(is_active=True)
127 user = user_util.create_user(is_active=True)
128 user_name = user.username
128 user_name = user.username
129 response = self.app.get(
129 response = self.app.get(
130 url(controller='home', action='user_autocomplete_data'),
130 url(controller='home', action='user_autocomplete_data'),
131 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200)
131 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200)
132 result = json.loads(response.body)
132 result = json.loads(response.body)
133 values = [suggestion['value'] for suggestion in result['suggestions']]
133 values = [suggestion['value'] for suggestion in result['suggestions']]
134 assert user_name in values
134 assert user_name in values
135
135
136 def test_returns_groups_when_user_groups_sent(self, user_util):
136 def test_returns_groups_when_user_groups_sent(self, user_util):
137 self.log_user()
137 self.log_user()
138 group = user_util.create_user_group(user_groups_active=True)
138 group = user_util.create_user_group(user_groups_active=True)
139 group_name = group.users_group_name
139 group_name = group.users_group_name
140 response = self.app.get(
140 response = self.app.get(
141 url(controller='home', action='user_autocomplete_data',
141 url(controller='home', action='user_autocomplete_data',
142 user_groups='true'),
142 user_groups='true'),
143 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200)
143 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200)
144 result = json.loads(response.body)
144 result = json.loads(response.body)
145 values = [suggestion['value'] for suggestion in result['suggestions']]
145 values = [suggestion['value'] for suggestion in result['suggestions']]
146 assert group_name in values
146 assert group_name in values
147
147
148 def test_result_is_limited_when_query_is_sent(self):
148 def test_result_is_limited_when_query_is_sent(self):
149 self.log_user()
149 self.log_user()
150 fake_result = [
150 fake_result = [
151 {
151 {
152 'first_name': 'John',
152 'first_name': 'John',
153 'value_display': 'hello{} (John Smith)'.format(i),
153 'value_display': 'hello{} (John Smith)'.format(i),
154 'icon_link': '/images/user14.png',
154 'icon_link': '/images/user14.png',
155 'value': 'hello{}'.format(i),
155 'value': 'hello{}'.format(i),
156 'last_name': 'Smith',
156 'last_name': 'Smith',
157 'username': 'hello{}'.format(i),
157 'username': 'hello{}'.format(i),
158 'id': i,
158 'id': i,
159 'value_type': u'user'
159 'value_type': u'user'
160 }
160 }
161 for i in range(10)
161 for i in range(10)
162 ]
162 ]
163 users_patcher = patch.object(
163 users_patcher = patch.object(
164 RepoModel, 'get_users', return_value=fake_result)
164 RepoModel, 'get_users', return_value=fake_result)
165 groups_patcher = patch.object(
165 groups_patcher = patch.object(
166 RepoModel, 'get_user_groups', return_value=fake_result)
166 RepoModel, 'get_user_groups', return_value=fake_result)
167
167
168 query = 'hello'
168 query = 'hello'
169 with users_patcher as users_mock, groups_patcher as groups_mock:
169 with users_patcher as users_mock, groups_patcher as groups_mock:
170 response = self.app.get(
170 response = self.app.get(
171 url(controller='home', action='user_autocomplete_data',
171 url(controller='home', action='user_autocomplete_data',
172 user_groups='true', query=query),
172 user_groups='true', query=query),
173 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200)
173 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200)
174
174
175 result = json.loads(response.body)
175 result = json.loads(response.body)
176 users_mock.assert_called_once_with(name_contains=query)
176 users_mock.assert_called_once_with(name_contains=query)
177 groups_mock.assert_called_once_with(name_contains=query)
177 groups_mock.assert_called_once_with(name_contains=query)
178 assert len(result['suggestions']) == 20
178 assert len(result['suggestions']) == 20
179
179
180
180
181 def assert_and_get_content(result):
181 def assert_and_get_content(result):
182 repos = []
182 repos = []
183 groups = []
183 groups = []
184 commits = []
184 for data in result:
185 for data in result:
185 for data_item in data['children']:
186 for data_item in data['children']:
186 assert data_item['id']
187 assert data_item['id']
187 assert data_item['text']
188 assert data_item['text']
189 assert data_item['url']
188 if data_item['type'] == 'repo':
190 if data_item['type'] == 'repo':
189 repos.append(data_item)
191 repos.append(data_item)
190 else:
192 elif data_item['type'] == 'group':
191 groups.append(data_item)
193 groups.append(data_item)
194 elif data_item['type'] == 'commit':
195 commits.append(data_item)
196 else:
197 raise Exception('invalid type %s' % data_item['type'])
192
198
193 return repos, groups
199 return repos, groups, commits
194
200
195
201
196 class TestRepoSwitcherData(TestController):
202 class TestGotoSwitcherData(TestController):
197 required_repos_with_groups = [
203 required_repos_with_groups = [
198 'abc',
204 'abc',
199 'abc-fork',
205 'abc-fork',
200 'forks/abcd',
206 'forks/abcd',
201 'abcd',
207 'abcd',
202 'abcde',
208 'abcde',
203 'a/abc',
209 'a/abc',
204 'aa/abc',
210 'aa/abc',
205 'aaa/abc',
211 'aaa/abc',
206 'aaaa/abc',
212 'aaaa/abc',
207 'repos_abc/aaa/abc',
213 'repos_abc/aaa/abc',
208 'abc_repos/abc',
214 'abc_repos/abc',
209 'abc_repos/abcd',
215 'abc_repos/abcd',
210 'xxx/xyz',
216 'xxx/xyz',
211 'forked-abc/a/abc'
217 'forked-abc/a/abc'
212 ]
218 ]
213
219
214 @pytest.fixture(autouse=True, scope='class')
220 @pytest.fixture(autouse=True, scope='class')
215 def prepare(self, request, pylonsapp):
221 def prepare(self, request, pylonsapp):
216 for repo_and_group in self.required_repos_with_groups:
222 for repo_and_group in self.required_repos_with_groups:
217 # create structure of groups and return the last group
223 # create structure of groups and return the last group
218
224
219 repo_group = map_groups(repo_and_group)
225 repo_group = map_groups(repo_and_group)
220
226
221 RepoModel()._create_repo(
227 RepoModel()._create_repo(
222 repo_and_group, 'hg', 'test-ac', TEST_USER_ADMIN_LOGIN,
228 repo_and_group, 'hg', 'test-ac', TEST_USER_ADMIN_LOGIN,
223 repo_group=getattr(repo_group, 'group_id', None))
229 repo_group=getattr(repo_group, 'group_id', None))
224
230
225 Session().commit()
231 Session().commit()
226
232
227 request.addfinalizer(self.cleanup)
233 request.addfinalizer(self.cleanup)
228
234
229 def cleanup(self):
235 def cleanup(self):
230 # first delete all repos
236 # first delete all repos
231 for repo_and_groups in self.required_repos_with_groups:
237 for repo_and_groups in self.required_repos_with_groups:
232 repo = Repository.get_by_repo_name(repo_and_groups)
238 repo = Repository.get_by_repo_name(repo_and_groups)
233 if repo:
239 if repo:
234 RepoModel().delete(repo)
240 RepoModel().delete(repo)
235 Session().commit()
241 Session().commit()
236
242
237 # then delete all empty groups
243 # then delete all empty groups
238 for repo_and_groups in self.required_repos_with_groups:
244 for repo_and_groups in self.required_repos_with_groups:
239 if '/' in repo_and_groups:
245 if '/' in repo_and_groups:
240 r_group = repo_and_groups.rsplit('/', 1)[0]
246 r_group = repo_and_groups.rsplit('/', 1)[0]
241 repo_group = RepoGroup.get_by_group_name(r_group)
247 repo_group = RepoGroup.get_by_group_name(r_group)
242 if not repo_group:
248 if not repo_group:
243 continue
249 continue
244 parents = repo_group.parents
250 parents = repo_group.parents
245 RepoGroupModel().delete(repo_group, force_delete=True)
251 RepoGroupModel().delete(repo_group, force_delete=True)
246 Session().commit()
252 Session().commit()
247
253
248 for el in reversed(parents):
254 for el in reversed(parents):
249 RepoGroupModel().delete(el, force_delete=True)
255 RepoGroupModel().delete(el, force_delete=True)
250 Session().commit()
256 Session().commit()
251
257
252 def test_returns_list_of_repos_and_groups(self):
258 def test_returns_list_of_repos_and_groups(self):
253 self.log_user()
259 self.log_user()
254
260
255 response = self.app.get(
261 response = self.app.get(
256 url(controller='home', action='repo_switcher_data'),
262 url(controller='home', action='goto_switcher_data'),
257 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200)
263 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200)
258 result = json.loads(response.body)['results']
264 result = json.loads(response.body)['results']
259
265
260 repos, groups = assert_and_get_content(result)
266 repos, groups, commits = assert_and_get_content(result)
261
267
262 assert len(repos) == len(Repository.get_all())
268 assert len(repos) == len(Repository.get_all())
263 assert len(groups) == len(RepoGroup.get_all())
269 assert len(groups) == len(RepoGroup.get_all())
270 assert len(commits) == 0
264
271
265 def test_returns_list_of_repos_and_groups_filtered(self):
272 def test_returns_list_of_repos_and_groups_filtered(self):
266 self.log_user()
273 self.log_user()
267
274
268 response = self.app.get(
275 response = self.app.get(
269 url(controller='home', action='repo_switcher_data'),
276 url(controller='home', action='goto_switcher_data'),
270 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', },
277 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', },
271 params={'query': 'abc'}, status=200)
278 params={'query': 'abc'}, status=200)
272 result = json.loads(response.body)['results']
279 result = json.loads(response.body)['results']
273
280
274 repos, groups = assert_and_get_content(result)
281 repos, groups, commits = assert_and_get_content(result)
275
282
276 assert len(repos) == 13
283 assert len(repos) == 13
277 assert len(groups) == 5
284 assert len(groups) == 5
285 assert len(commits) == 0
278
286
279 def test_returns_list_of_properly_sorted_and_filtered(self):
287 def test_returns_list_of_properly_sorted_and_filtered(self):
280 self.log_user()
288 self.log_user()
281
289
282 response = self.app.get(
290 response = self.app.get(
283 url(controller='home', action='repo_switcher_data'),
291 url(controller='home', action='goto_switcher_data'),
284 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', },
292 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', },
285 params={'query': 'abc'}, status=200)
293 params={'query': 'abc'}, status=200)
286 result = json.loads(response.body)['results']
294 result = json.loads(response.body)['results']
287
295
288 repos, groups = assert_and_get_content(result)
296 repos, groups, commits = assert_and_get_content(result)
289
297
290 test_repos = [x['text'] for x in repos[:4]]
298 test_repos = [x['text'] for x in repos[:4]]
291 assert ['abc', 'abcd', 'a/abc', 'abcde'] == test_repos
299 assert ['abc', 'abcd', 'a/abc', 'abcde'] == test_repos
292
300
293 test_groups = [x['text'] for x in groups[:4]]
301 test_groups = [x['text'] for x in groups[:4]]
294 assert ['abc_repos', 'repos_abc',
302 assert ['abc_repos', 'repos_abc',
295 'forked-abc', 'forked-abc/a'] == test_groups
303 'forked-abc', 'forked-abc/a'] == test_groups
296
304
297
305
298 class TestRepoListData(TestController):
306 class TestRepoListData(TestController):
299 def test_returns_list_of_repos_and_groups(self, user_util):
307 def test_returns_list_of_repos_and_groups(self, user_util):
300 self.log_user()
308 self.log_user()
301
309
302 response = self.app.get(
310 response = self.app.get(
303 url(controller='home', action='repo_switcher_data'),
311 url(controller='home', action='repo_list_data'),
304 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200)
312 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', }, status=200)
305 result = json.loads(response.body)['results']
313 result = json.loads(response.body)['results']
306
314
307 repos, groups = assert_and_get_content(result)
315 repos, groups, commits = assert_and_get_content(result)
308
316
309 assert len(repos) == len(Repository.get_all())
317 assert len(repos) == len(Repository.get_all())
310 assert len(groups) == 0
318 assert len(groups) == 0
319 assert len(commits) == 0
311
320
312 def test_returns_list_of_repos_and_groups_filtered(self):
321 def test_returns_list_of_repos_and_groups_filtered(self):
313 self.log_user()
322 self.log_user()
314
323
315 response = self.app.get(
324 response = self.app.get(
316 url(controller='home', action='repo_switcher_data'),
325 url(controller='home', action='repo_list_data'),
317 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', },
326 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', },
318 params={'query': 'vcs_test_git'}, status=200)
327 params={'query': 'vcs_test_git'}, status=200)
319 result = json.loads(response.body)['results']
328 result = json.loads(response.body)['results']
320
329
321 repos, groups = assert_and_get_content(result)
330 repos, groups, commits = assert_and_get_content(result)
322
331
323 assert len(repos) == len(Repository.query().filter(
332 assert len(repos) == len(Repository.query().filter(
324 Repository.repo_name.ilike('%vcs_test_git%')).all())
333 Repository.repo_name.ilike('%vcs_test_git%')).all())
325 assert len(groups) == 0
334 assert len(groups) == 0
335 assert len(commits) == 0
326
336
327 def test_returns_list_of_repos_and_groups_filtered_with_type(self):
337 def test_returns_list_of_repos_and_groups_filtered_with_type(self):
328 self.log_user()
338 self.log_user()
329
339
330 response = self.app.get(
340 response = self.app.get(
331 url(controller='home', action='repo_switcher_data'),
341 url(controller='home', action='repo_list_data'),
332 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', },
342 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', },
333 params={'query': 'vcs_test_git', 'repo_type': 'git'}, status=200)
343 params={'query': 'vcs_test_git', 'repo_type': 'git'}, status=200)
334 result = json.loads(response.body)['results']
344 result = json.loads(response.body)['results']
335
345
336 repos, groups = assert_and_get_content(result)
346 repos, groups, commits = assert_and_get_content(result)
337
347
338 assert len(repos) == len(Repository.query().filter(
348 assert len(repos) == len(Repository.query().filter(
339 Repository.repo_name.ilike('%vcs_test_git%')).all())
349 Repository.repo_name.ilike('%vcs_test_git%')).all())
340 assert len(groups) == 0
350 assert len(groups) == 0
351 assert len(commits) == 0
341
352
342 def test_returns_list_of_repos_non_ascii_query(self):
353 def test_returns_list_of_repos_non_ascii_query(self):
343 self.log_user()
354 self.log_user()
344 response = self.app.get(
355 response = self.app.get(
345 url(controller='home', action='repo_switcher_data'),
356 url(controller='home', action='repo_list_data'),
346 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', },
357 headers={'X-REQUESTED-WITH': 'XMLHttpRequest', },
347 params={'query': 'Δ‡_vcs_test_Δ…', 'repo_type': 'git'}, status=200)
358 params={'query': 'Δ‡_vcs_test_Δ…', 'repo_type': 'git'}, status=200)
348 result = json.loads(response.body)['results']
359 result = json.loads(response.body)['results']
349
360
350 repos, groups = assert_and_get_content(result)
361 repos, groups, commits = assert_and_get_content(result)
351
362
352 assert len(repos) == 0
363 assert len(repos) == 0
353 assert len(groups) == 0
364 assert len(groups) == 0
365 assert len(commits) == 0
@@ -1,198 +1,202 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 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 os
21 import os
22
22
23 import mock
23 import mock
24 import pytest
24 import pytest
25 from whoosh import query
25 from whoosh import query
26
26
27 from rhodecode.tests import (
27 from rhodecode.tests import (
28 TestController, url, SkipTest, HG_REPO,
28 TestController, url, SkipTest, HG_REPO,
29 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
29 TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
30 from rhodecode.tests.utils import AssertResponse
30 from rhodecode.tests.utils import AssertResponse
31
31
32
32
33 class TestSearchController(TestController):
33 class TestSearchController(TestController):
34
34
35 def test_index(self):
35 def test_index(self):
36 self.log_user()
36 self.log_user()
37 response = self.app.get(url(controller='search', action='index'))
37 response = self.app.get(url(controller='search', action='index'))
38 assert_response = AssertResponse(response)
38 assert_response = AssertResponse(response)
39 assert_response.one_element_exists('input#q')
39 assert_response.one_element_exists('input#q')
40
40
41 def test_search_files_empty_search(self):
41 def test_search_files_empty_search(self):
42 if os.path.isdir(self.index_location):
42 if os.path.isdir(self.index_location):
43 raise SkipTest('skipped due to existing index')
43 raise SkipTest('skipped due to existing index')
44 else:
44 else:
45 self.log_user()
45 self.log_user()
46 response = self.app.get(url(controller='search', action='index'),
46 response = self.app.get(url(controller='search', action='index'),
47 {'q': HG_REPO})
47 {'q': HG_REPO})
48 response.mustcontain('There is no index to search in. '
48 response.mustcontain('There is no index to search in. '
49 'Please run whoosh indexer')
49 'Please run whoosh indexer')
50
50
51 def test_search_validation(self):
51 def test_search_validation(self):
52 self.log_user()
52 self.log_user()
53 response = self.app.get(
53 response = self.app.get(
54 url(controller='search', action='index'), {'q': query,
54 url(controller='search', action='index'), {'q': query,
55 'type': 'content',
55 'type': 'content',
56 'page_limit': 1000})
56 'page_limit': 1000})
57
57
58 response.mustcontain(
58 response.mustcontain(
59 'page_limit - 1000 is greater than maximum value 500')
59 'page_limit - 1000 is greater than maximum value 500')
60
60
61 @pytest.mark.parametrize("query, expected_hits, expected_paths", [
61 @pytest.mark.parametrize("query, expected_hits, expected_paths", [
62 ('todo', 23, [
62 ('todo', 23, [
63 'vcs/backends/hg/inmemory.py',
63 'vcs/backends/hg/inmemory.py',
64 'vcs/tests/test_git.py']),
64 'vcs/tests/test_git.py']),
65 ('extension:rst installation', 6, [
65 ('extension:rst installation', 6, [
66 'docs/index.rst',
66 'docs/index.rst',
67 'docs/installation.rst']),
67 'docs/installation.rst']),
68 ('def repo', 87, [
68 ('def repo', 87, [
69 'vcs/tests/test_git.py',
69 'vcs/tests/test_git.py',
70 'vcs/tests/test_changesets.py']),
70 'vcs/tests/test_changesets.py']),
71 ('repository:%s def test' % HG_REPO, 18, [
71 ('repository:%s def test' % HG_REPO, 18, [
72 'vcs/tests/test_git.py',
72 'vcs/tests/test_git.py',
73 'vcs/tests/test_changesets.py']),
73 'vcs/tests/test_changesets.py']),
74 ('"def main"', 9, [
74 ('"def main"', 9, [
75 'vcs/__init__.py',
75 'vcs/__init__.py',
76 'vcs/tests/__init__.py',
76 'vcs/tests/__init__.py',
77 'vcs/utils/progressbar.py']),
77 'vcs/utils/progressbar.py']),
78 ('owner:test_admin', 358, [
78 ('owner:test_admin', 358, [
79 'vcs/tests/base.py',
79 'vcs/tests/base.py',
80 'MANIFEST.in',
80 'MANIFEST.in',
81 'vcs/utils/termcolors.py',
81 'vcs/utils/termcolors.py',
82 'docs/theme/ADC/static/documentation.png']),
82 'docs/theme/ADC/static/documentation.png']),
83 ('owner:test_admin def main', 72, [
83 ('owner:test_admin def main', 72, [
84 'vcs/__init__.py',
84 'vcs/__init__.py',
85 'vcs/tests/test_utils_filesize.py',
85 'vcs/tests/test_utils_filesize.py',
86 'vcs/tests/test_cli.py']),
86 'vcs/tests/test_cli.py']),
87 ('owner:michaΕ‚ test', 0, []),
87 ('owner:michaΕ‚ test', 0, []),
88 ])
88 ])
89 def test_search_files(self, query, expected_hits, expected_paths):
89 def test_search_files(self, query, expected_hits, expected_paths):
90 self.log_user()
90 self.log_user()
91 response = self.app.get(
91 response = self.app.get(
92 url(controller='search', action='index'), {'q': query,
92 url(controller='search', action='index'), {'q': query,
93 'type': 'content',
93 'type': 'content',
94 'page_limit': 500})
94 'page_limit': 500})
95
95
96 response.mustcontain('%s results' % expected_hits)
96 response.mustcontain('%s results' % expected_hits)
97 for path in expected_paths:
97 for path in expected_paths:
98 response.mustcontain(path)
98 response.mustcontain(path)
99
99
100 @pytest.mark.parametrize("query, expected_hits, expected_commits", [
100 @pytest.mark.parametrize("query, expected_hits, expected_commits", [
101 ('bother to ask where to fetch repo during tests', 3, [
101 ('bother to ask where to fetch repo during tests', 3, [
102 ('hg', 'a00c1b6f5d7a6ae678fd553a8b81d92367f7ecf1'),
102 ('hg', 'a00c1b6f5d7a6ae678fd553a8b81d92367f7ecf1'),
103 ('git', 'c6eb379775c578a95dad8ddab53f963b80894850'),
103 ('git', 'c6eb379775c578a95dad8ddab53f963b80894850'),
104 ('svn', '98')]),
104 ('svn', '98')]),
105 ('michaΕ‚', 0, []),
105 ('michaΕ‚', 0, []),
106 ('changed:tests/utils.py', 36, [
106 ('changed:tests/utils.py', 36, [
107 ('hg', 'a00c1b6f5d7a6ae678fd553a8b81d92367f7ecf1')]),
107 ('hg', 'a00c1b6f5d7a6ae678fd553a8b81d92367f7ecf1')]),
108 ('changed:vcs/utils/archivers.py', 11, [
108 ('changed:vcs/utils/archivers.py', 11, [
109 ('hg', '25213a5fbb048dff8ba65d21e466a835536e5b70'),
109 ('hg', '25213a5fbb048dff8ba65d21e466a835536e5b70'),
110 ('hg', '47aedd538bf616eedcb0e7d630ea476df0e159c7'),
110 ('hg', '47aedd538bf616eedcb0e7d630ea476df0e159c7'),
111 ('hg', 'f5d23247fad4856a1dabd5838afade1e0eed24fb'),
111 ('hg', 'f5d23247fad4856a1dabd5838afade1e0eed24fb'),
112 ('hg', '04ad456aefd6461aea24f90b63954b6b1ce07b3e'),
112 ('hg', '04ad456aefd6461aea24f90b63954b6b1ce07b3e'),
113 ('git', 'c994f0de03b2a0aa848a04fc2c0d7e737dba31fc'),
113 ('git', 'c994f0de03b2a0aa848a04fc2c0d7e737dba31fc'),
114 ('git', 'd1f898326327e20524fe22417c22d71064fe54a1'),
114 ('git', 'd1f898326327e20524fe22417c22d71064fe54a1'),
115 ('git', 'fe568b4081755c12abf6ba673ba777fc02a415f3'),
115 ('git', 'fe568b4081755c12abf6ba673ba777fc02a415f3'),
116 ('git', 'bafe786f0d8c2ff7da5c1dcfcfa577de0b5e92f1')]),
116 ('git', 'bafe786f0d8c2ff7da5c1dcfcfa577de0b5e92f1')]),
117 ('added:README.rst', 3, [
117 ('added:README.rst', 3, [
118 ('hg', '3803844fdbd3b711175fc3da9bdacfcd6d29a6fb'),
118 ('hg', '3803844fdbd3b711175fc3da9bdacfcd6d29a6fb'),
119 ('git', 'ff7ca51e58c505fec0dd2491de52c622bb7a806b'),
119 ('git', 'ff7ca51e58c505fec0dd2491de52c622bb7a806b'),
120 ('svn', '8')]),
120 ('svn', '8')]),
121 ('changed:lazy.py', 15, [
121 ('changed:lazy.py', 15, [
122 ('hg', 'eaa291c5e6ae6126a203059de9854ccf7b5baa12'),
122 ('hg', 'eaa291c5e6ae6126a203059de9854ccf7b5baa12'),
123 ('git', '17438a11f72b93f56d0e08e7d1fa79a378578a82'),
123 ('git', '17438a11f72b93f56d0e08e7d1fa79a378578a82'),
124 ('svn', '82'),
124 ('svn', '82'),
125 ('svn', '262'),
125 ('svn', '262'),
126 ('hg', 'f5d23247fad4856a1dabd5838afade1e0eed24fb'),
126 ('hg', 'f5d23247fad4856a1dabd5838afade1e0eed24fb'),
127 ('git', '33fa3223355104431402a888fa77a4e9956feb3e')
127 ('git', '33fa3223355104431402a888fa77a4e9956feb3e')
128 ]),
128 ]),
129 ('author:marcin@python-blog.com '
129 ('author:marcin@python-blog.com '
130 'commit_id:b986218ba1c9b0d6a259fac9b050b1724ed8e545', 1, [
130 'commit_id:b986218ba1c9b0d6a259fac9b050b1724ed8e545', 1, [
131 ('hg', 'b986218ba1c9b0d6a259fac9b050b1724ed8e545')]),
131 ('hg', 'b986218ba1c9b0d6a259fac9b050b1724ed8e545')]),
132 ('b986218ba1c9b0d6a259fac9b050b1724ed8e545', 1, [
133 ('hg', 'b986218ba1c9b0d6a259fac9b050b1724ed8e545')]),
134 ('b986218b', 1, [
135 ('hg', 'b986218ba1c9b0d6a259fac9b050b1724ed8e545')]),
132 ])
136 ])
133 def test_search_commit_messages(
137 def test_search_commit_messages(
134 self, query, expected_hits, expected_commits, enabled_backends):
138 self, query, expected_hits, expected_commits, enabled_backends):
135 self.log_user()
139 self.log_user()
136 response = self.app.get(
140 response = self.app.get(
137 url(controller='search', action='index'), {'q': query,
141 url(controller='search', action='index'), {'q': query,
138 'type': 'commit',
142 'type': 'commit',
139 'page_limit': 500})
143 'page_limit': 500})
140
144
141 response.mustcontain('%s results' % expected_hits)
145 response.mustcontain('%s results' % expected_hits)
142 for backend, commit_id in expected_commits:
146 for backend, commit_id in expected_commits:
143 if backend in enabled_backends:
147 if backend in enabled_backends:
144 response.mustcontain(commit_id)
148 response.mustcontain(commit_id)
145
149
146 @pytest.mark.parametrize("query, expected_hits, expected_paths", [
150 @pytest.mark.parametrize("query, expected_hits, expected_paths", [
147 ('readme.rst', 3, []),
151 ('readme.rst', 3, []),
148 ('test*', 75, []),
152 ('test*', 75, []),
149 ('*model*', 1, []),
153 ('*model*', 1, []),
150 ('extension:rst', 48, []),
154 ('extension:rst', 48, []),
151 ('extension:rst api', 24, []),
155 ('extension:rst api', 24, []),
152 ])
156 ])
153 def test_search_file_paths(self, query, expected_hits, expected_paths):
157 def test_search_file_paths(self, query, expected_hits, expected_paths):
154 self.log_user()
158 self.log_user()
155 response = self.app.get(
159 response = self.app.get(
156 url(controller='search', action='index'), {'q': query,
160 url(controller='search', action='index'), {'q': query,
157 'type': 'path',
161 'type': 'path',
158 'page_limit': 500})
162 'page_limit': 500})
159
163
160 response.mustcontain('%s results' % expected_hits)
164 response.mustcontain('%s results' % expected_hits)
161 for path in expected_paths:
165 for path in expected_paths:
162 response.mustcontain(path)
166 response.mustcontain(path)
163
167
164 def test_search_commit_message_specific_repo(self, backend):
168 def test_search_commit_message_specific_repo(self, backend):
165 self.log_user()
169 self.log_user()
166 response = self.app.get(
170 response = self.app.get(
167 url(controller='search', action='index',
171 url(controller='search', action='index',
168 repo_name=backend.repo_name),
172 repo_name=backend.repo_name),
169 {'q': 'bother to ask where to fetch repo during tests',
173 {'q': 'bother to ask where to fetch repo during tests',
170 'type': 'commit'})
174 'type': 'commit'})
171
175
172 response.mustcontain('1 results')
176 response.mustcontain('1 results')
173
177
174 def test_filters_are_not_applied_for_admin_user(self):
178 def test_filters_are_not_applied_for_admin_user(self):
175 self.log_user()
179 self.log_user()
176 with mock.patch('whoosh.searching.Searcher.search') as search_mock:
180 with mock.patch('whoosh.searching.Searcher.search') as search_mock:
177 self.app.get(
181 self.app.get(
178 url(controller='search', action='index'),
182 url(controller='search', action='index'),
179 {'q': 'test query', 'type': 'commit'})
183 {'q': 'test query', 'type': 'commit'})
180 assert search_mock.call_count == 1
184 assert search_mock.call_count == 1
181 _, kwargs = search_mock.call_args
185 _, kwargs = search_mock.call_args
182 assert kwargs['filter'] is None
186 assert kwargs['filter'] is None
183
187
184 def test_filters_are_applied_for_normal_user(self, enabled_backends):
188 def test_filters_are_applied_for_normal_user(self, enabled_backends):
185 self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
189 self.log_user(TEST_USER_REGULAR_LOGIN, TEST_USER_REGULAR_PASS)
186 with mock.patch('whoosh.searching.Searcher.search') as search_mock:
190 with mock.patch('whoosh.searching.Searcher.search') as search_mock:
187 self.app.get(
191 self.app.get(
188 url(controller='search', action='index'),
192 url(controller='search', action='index'),
189 {'q': 'test query', 'type': 'commit'})
193 {'q': 'test query', 'type': 'commit'})
190 assert search_mock.call_count == 1
194 assert search_mock.call_count == 1
191 _, kwargs = search_mock.call_args
195 _, kwargs = search_mock.call_args
192 assert isinstance(kwargs['filter'], query.Or)
196 assert isinstance(kwargs['filter'], query.Or)
193 expected_repositories = [
197 expected_repositories = [
194 'vcs_test_{}'.format(b) for b in enabled_backends]
198 'vcs_test_{}'.format(b) for b in enabled_backends]
195 queried_repositories = [
199 queried_repositories = [
196 name for type_, name in kwargs['filter'].all_terms()]
200 name for type_, name in kwargs['filter'].all_terms()]
197 for repository in expected_repositories:
201 for repository in expected_repositories:
198 assert repository in queried_repositories
202 assert repository in queried_repositories
General Comments 0
You need to be logged in to leave comments. Login now