##// END OF EJS Templates
better detection of deleting groups with subgroups inside....
marcink -
r3458:0ad025ee beta
parent child Browse files
Show More
@@ -1,648 +1,661 b''
1 1 """
2 2 Routes configuration
3 3
4 4 The more specific and detailed routes should be defined first so they
5 5 may take precedent over the more generic routes. For more information
6 6 refer to the routes manual at http://routes.groovie.org/docs/
7 7 """
8 8 from __future__ import with_statement
9 9 from routes import Mapper
10 10
11 11 # prefix for non repository related links needs to be prefixed with `/`
12 12 ADMIN_PREFIX = '/_admin'
13 13
14 14
15 15 def make_map(config):
16 16 """Create, configure and return the routes Mapper"""
17 17 rmap = Mapper(directory=config['pylons.paths']['controllers'],
18 18 always_scan=config['debug'])
19 19 rmap.minimization = False
20 20 rmap.explicit = False
21 21
22 22 from rhodecode.lib.utils import is_valid_repo
23 23 from rhodecode.lib.utils import is_valid_repos_group
24 24
25 25 def check_repo(environ, match_dict):
26 26 """
27 27 check for valid repository for proper 404 handling
28 28
29 29 :param environ:
30 30 :param match_dict:
31 31 """
32 32 from rhodecode.model.db import Repository
33 33 repo_name = match_dict.get('repo_name')
34 34
35 35 if match_dict.get('f_path'):
36 36 #fix for multiple initial slashes that causes errors
37 37 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
38 38
39 39 try:
40 40 by_id = repo_name.split('_')
41 41 if len(by_id) == 2 and by_id[1].isdigit() and by_id[0] == '':
42 42 repo_name = Repository.get(by_id[1]).repo_name
43 43 match_dict['repo_name'] = repo_name
44 44 except:
45 45 pass
46 46
47 47 return is_valid_repo(repo_name, config['base_path'])
48 48
49 49 def check_group(environ, match_dict):
50 50 """
51 51 check for valid repository group for proper 404 handling
52 52
53 53 :param environ:
54 54 :param match_dict:
55 55 """
56 56 repos_group_name = match_dict.get('group_name')
57 57 return is_valid_repos_group(repos_group_name, config['base_path'])
58 58
59 def check_group_skip_path(environ, match_dict):
60 """
61 check for valid repository group for proper 404 handling, but skips
62 verification of existing path
63
64 :param environ:
65 :param match_dict:
66 """
67 repos_group_name = match_dict.get('group_name')
68 return is_valid_repos_group(repos_group_name, config['base_path'],
69 skip_path_check=True)
70
59 71 def check_int(environ, match_dict):
60 72 return match_dict.get('id').isdigit()
61 73
62 74 # The ErrorController route (handles 404/500 error pages); it should
63 75 # likely stay at the top, ensuring it can always be resolved
64 76 rmap.connect('/error/{action}', controller='error')
65 77 rmap.connect('/error/{action}/{id}', controller='error')
66 78
67 79 #==========================================================================
68 80 # CUSTOM ROUTES HERE
69 81 #==========================================================================
70 82
71 83 #MAIN PAGE
72 84 rmap.connect('home', '/', controller='home', action='index')
73 85 rmap.connect('repo_switcher', '/repos', controller='home',
74 86 action='repo_switcher')
75 87 rmap.connect('branch_tag_switcher', '/branches-tags/{repo_name:.*?}',
76 88 controller='home', action='branch_tag_switcher')
77 89 rmap.connect('bugtracker',
78 90 "http://bitbucket.org/marcinkuzminski/rhodecode/issues",
79 91 _static=True)
80 92 rmap.connect('rst_help',
81 93 "http://docutils.sourceforge.net/docs/user/rst/quickref.html",
82 94 _static=True)
83 95 rmap.connect('rhodecode_official', "http://rhodecode.org", _static=True)
84 96
85 97 #ADMIN REPOSITORY REST ROUTES
86 98 with rmap.submapper(path_prefix=ADMIN_PREFIX,
87 99 controller='admin/repos') as m:
88 100 m.connect("repos", "/repos",
89 101 action="create", conditions=dict(method=["POST"]))
90 102 m.connect("repos", "/repos",
91 103 action="index", conditions=dict(method=["GET"]))
92 104 m.connect("formatted_repos", "/repos.{format}",
93 105 action="index",
94 106 conditions=dict(method=["GET"]))
95 107 m.connect("new_repo", "/repos/new",
96 108 action="new", conditions=dict(method=["GET"]))
97 109 m.connect("formatted_new_repo", "/repos/new.{format}",
98 110 action="new", conditions=dict(method=["GET"]))
99 111 m.connect("/repos/{repo_name:.*?}",
100 112 action="update", conditions=dict(method=["PUT"],
101 113 function=check_repo))
102 114 m.connect("/repos/{repo_name:.*?}",
103 115 action="delete", conditions=dict(method=["DELETE"],
104 116 function=check_repo))
105 117 # no longer used:
106 118 m.connect("edit_repo_admin", "/repos/{repo_name:.*?}/edit",
107 119 action="edit", conditions=dict(method=["GET"],
108 120 function=check_repo))
109 121 m.connect("formatted_edit_repo", "/repos/{repo_name:.*?}.{format}/edit",
110 122 action="edit", conditions=dict(method=["GET"],
111 123 function=check_repo))
112 124 m.connect("repo", "/repos/{repo_name:.*?}",
113 125 action="show", conditions=dict(method=["GET"],
114 126 function=check_repo))
115 127 m.connect("formatted_repo", "/repos/{repo_name:.*?}.{format}",
116 128 action="show", conditions=dict(method=["GET"],
117 129 function=check_repo))
118 130 #ajax delete repo perm user
119 131 m.connect('delete_repo_user', "/repos_delete_user/{repo_name:.*?}",
120 132 action="delete_perm_user",
121 133 conditions=dict(method=["DELETE"], function=check_repo))
122 134
123 135 #ajax delete repo perm users_group
124 136 m.connect('delete_repo_users_group',
125 137 "/repos_delete_users_group/{repo_name:.*?}",
126 138 action="delete_perm_users_group",
127 139 conditions=dict(method=["DELETE"], function=check_repo))
128 140
129 141 #settings actions
130 142 m.connect('repo_stats', "/repos_stats/{repo_name:.*?}",
131 143 action="repo_stats", conditions=dict(method=["DELETE"],
132 144 function=check_repo))
133 145 m.connect('repo_cache', "/repos_cache/{repo_name:.*?}",
134 146 action="repo_cache", conditions=dict(method=["DELETE"],
135 147 function=check_repo))
136 148 m.connect('repo_public_journal', "/repos_public_journal/{repo_name:.*?}",
137 149 action="repo_public_journal", conditions=dict(method=["PUT"],
138 150 function=check_repo))
139 151 m.connect('repo_pull', "/repo_pull/{repo_name:.*?}",
140 152 action="repo_pull", conditions=dict(method=["PUT"],
141 153 function=check_repo))
142 154 m.connect('repo_as_fork', "/repo_as_fork/{repo_name:.*?}",
143 155 action="repo_as_fork", conditions=dict(method=["PUT"],
144 156 function=check_repo))
145 157 m.connect('repo_locking', "/repo_locking/{repo_name:.*?}",
146 158 action="repo_locking", conditions=dict(method=["PUT"],
147 159 function=check_repo))
148 160 #repo fields
149 161 m.connect('create_repo_fields', "/repo_fields/{repo_name:.*?}/new",
150 162 action="create_repo_field", conditions=dict(method=["PUT"],
151 163 function=check_repo))
152 164
153 165 m.connect('delete_repo_fields', "/repo_fields/{repo_name:.*?}/{field_id}",
154 166 action="delete_repo_field", conditions=dict(method=["DELETE"],
155 167 function=check_repo))
156 168
157 169 with rmap.submapper(path_prefix=ADMIN_PREFIX,
158 170 controller='admin/repos_groups') as m:
159 171 m.connect("repos_groups", "/repos_groups",
160 172 action="create", conditions=dict(method=["POST"]))
161 173 m.connect("repos_groups", "/repos_groups",
162 174 action="index", conditions=dict(method=["GET"]))
163 175 m.connect("formatted_repos_groups", "/repos_groups.{format}",
164 176 action="index", conditions=dict(method=["GET"]))
165 177 m.connect("new_repos_group", "/repos_groups/new",
166 178 action="new", conditions=dict(method=["GET"]))
167 179 m.connect("formatted_new_repos_group", "/repos_groups/new.{format}",
168 180 action="new", conditions=dict(method=["GET"]))
169 181 m.connect("update_repos_group", "/repos_groups/{group_name:.*?}",
170 182 action="update", conditions=dict(method=["PUT"],
171 183 function=check_group))
172 184 m.connect("delete_repos_group", "/repos_groups/{group_name:.*?}",
173 185 action="delete", conditions=dict(method=["DELETE"],
174 function=check_group))
186 function=check_group_skip_path))
175 187 m.connect("edit_repos_group", "/repos_groups/{group_name:.*?}/edit",
176 action="edit", conditions=dict(method=["GET"],))
188 action="edit", conditions=dict(method=["GET"],
189 function=check_group))
177 190 m.connect("formatted_edit_repos_group",
178 191 "/repos_groups/{group_name:.*?}.{format}/edit",
179 192 action="edit", conditions=dict(method=["GET"],
180 193 function=check_group))
181 194 m.connect("repos_group", "/repos_groups/{group_name:.*?}",
182 195 action="show", conditions=dict(method=["GET"],
183 196 function=check_group))
184 197 m.connect("formatted_repos_group", "/repos_groups/{group_name:.*?}.{format}",
185 198 action="show", conditions=dict(method=["GET"],
186 199 function=check_group))
187 200 # ajax delete repos group perm user
188 201 m.connect('delete_repos_group_user_perm',
189 202 "/delete_repos_group_user_perm/{group_name:.*?}",
190 203 action="delete_repos_group_user_perm",
191 204 conditions=dict(method=["DELETE"], function=check_group))
192 205
193 206 # ajax delete repos group perm users_group
194 207 m.connect('delete_repos_group_users_group_perm',
195 208 "/delete_repos_group_users_group_perm/{group_name:.*?}",
196 209 action="delete_repos_group_users_group_perm",
197 210 conditions=dict(method=["DELETE"], function=check_group))
198 211
199 212 #ADMIN USER REST ROUTES
200 213 with rmap.submapper(path_prefix=ADMIN_PREFIX,
201 214 controller='admin/users') as m:
202 215 m.connect("users", "/users",
203 216 action="create", conditions=dict(method=["POST"]))
204 217 m.connect("users", "/users",
205 218 action="index", conditions=dict(method=["GET"]))
206 219 m.connect("formatted_users", "/users.{format}",
207 220 action="index", conditions=dict(method=["GET"]))
208 221 m.connect("new_user", "/users/new",
209 222 action="new", conditions=dict(method=["GET"]))
210 223 m.connect("formatted_new_user", "/users/new.{format}",
211 224 action="new", conditions=dict(method=["GET"]))
212 225 m.connect("update_user", "/users/{id}",
213 226 action="update", conditions=dict(method=["PUT"]))
214 227 m.connect("delete_user", "/users/{id}",
215 228 action="delete", conditions=dict(method=["DELETE"]))
216 229 m.connect("edit_user", "/users/{id}/edit",
217 230 action="edit", conditions=dict(method=["GET"]))
218 231 m.connect("formatted_edit_user",
219 232 "/users/{id}.{format}/edit",
220 233 action="edit", conditions=dict(method=["GET"]))
221 234 m.connect("user", "/users/{id}",
222 235 action="show", conditions=dict(method=["GET"]))
223 236 m.connect("formatted_user", "/users/{id}.{format}",
224 237 action="show", conditions=dict(method=["GET"]))
225 238
226 239 #EXTRAS USER ROUTES
227 240 m.connect("user_perm", "/users_perm/{id}",
228 241 action="update_perm", conditions=dict(method=["PUT"]))
229 242 m.connect("user_emails", "/users_emails/{id}",
230 243 action="add_email", conditions=dict(method=["PUT"]))
231 244 m.connect("user_emails_delete", "/users_emails/{id}",
232 245 action="delete_email", conditions=dict(method=["DELETE"]))
233 246 m.connect("user_ips", "/users_ips/{id}",
234 247 action="add_ip", conditions=dict(method=["PUT"]))
235 248 m.connect("user_ips_delete", "/users_ips/{id}",
236 249 action="delete_ip", conditions=dict(method=["DELETE"]))
237 250
238 251 #ADMIN USER GROUPS REST ROUTES
239 252 with rmap.submapper(path_prefix=ADMIN_PREFIX,
240 253 controller='admin/users_groups') as m:
241 254 m.connect("users_groups", "/users_groups",
242 255 action="create", conditions=dict(method=["POST"]))
243 256 m.connect("users_groups", "/users_groups",
244 257 action="index", conditions=dict(method=["GET"]))
245 258 m.connect("formatted_users_groups", "/users_groups.{format}",
246 259 action="index", conditions=dict(method=["GET"]))
247 260 m.connect("new_users_group", "/users_groups/new",
248 261 action="new", conditions=dict(method=["GET"]))
249 262 m.connect("formatted_new_users_group", "/users_groups/new.{format}",
250 263 action="new", conditions=dict(method=["GET"]))
251 264 m.connect("update_users_group", "/users_groups/{id}",
252 265 action="update", conditions=dict(method=["PUT"]))
253 266 m.connect("delete_users_group", "/users_groups/{id}",
254 267 action="delete", conditions=dict(method=["DELETE"]))
255 268 m.connect("edit_users_group", "/users_groups/{id}/edit",
256 269 action="edit", conditions=dict(method=["GET"]))
257 270 m.connect("formatted_edit_users_group",
258 271 "/users_groups/{id}.{format}/edit",
259 272 action="edit", conditions=dict(method=["GET"]))
260 273 m.connect("users_group", "/users_groups/{id}",
261 274 action="show", conditions=dict(method=["GET"]))
262 275 m.connect("formatted_users_group", "/users_groups/{id}.{format}",
263 276 action="show", conditions=dict(method=["GET"]))
264 277
265 278 #EXTRAS USER ROUTES
266 279 m.connect("users_group_perm", "/users_groups_perm/{id}",
267 280 action="update_perm", conditions=dict(method=["PUT"]))
268 281
269 282 #ADMIN GROUP REST ROUTES
270 283 rmap.resource('group', 'groups',
271 284 controller='admin/groups', path_prefix=ADMIN_PREFIX)
272 285
273 286 #ADMIN PERMISSIONS REST ROUTES
274 287 rmap.resource('permission', 'permissions',
275 288 controller='admin/permissions', path_prefix=ADMIN_PREFIX)
276 289
277 290 #ADMIN DEFAULTS REST ROUTES
278 291 rmap.resource('default', 'defaults',
279 292 controller='admin/defaults', path_prefix=ADMIN_PREFIX)
280 293
281 294 ##ADMIN LDAP SETTINGS
282 295 rmap.connect('ldap_settings', '%s/ldap' % ADMIN_PREFIX,
283 296 controller='admin/ldap_settings', action='ldap_settings',
284 297 conditions=dict(method=["POST"]))
285 298
286 299 rmap.connect('ldap_home', '%s/ldap' % ADMIN_PREFIX,
287 300 controller='admin/ldap_settings')
288 301
289 302 #ADMIN SETTINGS REST ROUTES
290 303 with rmap.submapper(path_prefix=ADMIN_PREFIX,
291 304 controller='admin/settings') as m:
292 305 m.connect("admin_settings", "/settings",
293 306 action="create", conditions=dict(method=["POST"]))
294 307 m.connect("admin_settings", "/settings",
295 308 action="index", conditions=dict(method=["GET"]))
296 309 m.connect("formatted_admin_settings", "/settings.{format}",
297 310 action="index", conditions=dict(method=["GET"]))
298 311 m.connect("admin_new_setting", "/settings/new",
299 312 action="new", conditions=dict(method=["GET"]))
300 313 m.connect("formatted_admin_new_setting", "/settings/new.{format}",
301 314 action="new", conditions=dict(method=["GET"]))
302 315 m.connect("/settings/{setting_id}",
303 316 action="update", conditions=dict(method=["PUT"]))
304 317 m.connect("/settings/{setting_id}",
305 318 action="delete", conditions=dict(method=["DELETE"]))
306 319 m.connect("admin_edit_setting", "/settings/{setting_id}/edit",
307 320 action="edit", conditions=dict(method=["GET"]))
308 321 m.connect("formatted_admin_edit_setting",
309 322 "/settings/{setting_id}.{format}/edit",
310 323 action="edit", conditions=dict(method=["GET"]))
311 324 m.connect("admin_setting", "/settings/{setting_id}",
312 325 action="show", conditions=dict(method=["GET"]))
313 326 m.connect("formatted_admin_setting", "/settings/{setting_id}.{format}",
314 327 action="show", conditions=dict(method=["GET"]))
315 328 m.connect("admin_settings_my_account", "/my_account",
316 329 action="my_account", conditions=dict(method=["GET"]))
317 330 m.connect("admin_settings_my_account_update", "/my_account_update",
318 331 action="my_account_update", conditions=dict(method=["PUT"]))
319 332 m.connect("admin_settings_create_repository", "/create_repository",
320 333 action="create_repository", conditions=dict(method=["GET"]))
321 334 m.connect("admin_settings_my_repos", "/my_account/repos",
322 335 action="my_account_my_repos", conditions=dict(method=["GET"]))
323 336 m.connect("admin_settings_my_pullrequests", "/my_account/pull_requests",
324 337 action="my_account_my_pullrequests", conditions=dict(method=["GET"]))
325 338
326 339 #NOTIFICATION REST ROUTES
327 340 with rmap.submapper(path_prefix=ADMIN_PREFIX,
328 341 controller='admin/notifications') as m:
329 342 m.connect("notifications", "/notifications",
330 343 action="create", conditions=dict(method=["POST"]))
331 344 m.connect("notifications", "/notifications",
332 345 action="index", conditions=dict(method=["GET"]))
333 346 m.connect("notifications_mark_all_read", "/notifications/mark_all_read",
334 347 action="mark_all_read", conditions=dict(method=["GET"]))
335 348 m.connect("formatted_notifications", "/notifications.{format}",
336 349 action="index", conditions=dict(method=["GET"]))
337 350 m.connect("new_notification", "/notifications/new",
338 351 action="new", conditions=dict(method=["GET"]))
339 352 m.connect("formatted_new_notification", "/notifications/new.{format}",
340 353 action="new", conditions=dict(method=["GET"]))
341 354 m.connect("/notification/{notification_id}",
342 355 action="update", conditions=dict(method=["PUT"]))
343 356 m.connect("/notification/{notification_id}",
344 357 action="delete", conditions=dict(method=["DELETE"]))
345 358 m.connect("edit_notification", "/notification/{notification_id}/edit",
346 359 action="edit", conditions=dict(method=["GET"]))
347 360 m.connect("formatted_edit_notification",
348 361 "/notification/{notification_id}.{format}/edit",
349 362 action="edit", conditions=dict(method=["GET"]))
350 363 m.connect("notification", "/notification/{notification_id}",
351 364 action="show", conditions=dict(method=["GET"]))
352 365 m.connect("formatted_notification", "/notifications/{notification_id}.{format}",
353 366 action="show", conditions=dict(method=["GET"]))
354 367
355 368 #ADMIN MAIN PAGES
356 369 with rmap.submapper(path_prefix=ADMIN_PREFIX,
357 370 controller='admin/admin') as m:
358 371 m.connect('admin_home', '', action='index')
359 372 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
360 373 action='add_repo')
361 374
362 375 #==========================================================================
363 376 # API V2
364 377 #==========================================================================
365 378 with rmap.submapper(path_prefix=ADMIN_PREFIX,
366 379 controller='api/api') as m:
367 380 m.connect('api', '/api')
368 381
369 382 #USER JOURNAL
370 383 rmap.connect('journal', '%s/journal' % ADMIN_PREFIX,
371 384 controller='journal', action='index')
372 385 rmap.connect('journal_rss', '%s/journal/rss' % ADMIN_PREFIX,
373 386 controller='journal', action='journal_rss')
374 387 rmap.connect('journal_atom', '%s/journal/atom' % ADMIN_PREFIX,
375 388 controller='journal', action='journal_atom')
376 389
377 390 rmap.connect('public_journal', '%s/public_journal' % ADMIN_PREFIX,
378 391 controller='journal', action="public_journal")
379 392
380 393 rmap.connect('public_journal_rss', '%s/public_journal/rss' % ADMIN_PREFIX,
381 394 controller='journal', action="public_journal_rss")
382 395
383 396 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % ADMIN_PREFIX,
384 397 controller='journal', action="public_journal_rss")
385 398
386 399 rmap.connect('public_journal_atom',
387 400 '%s/public_journal/atom' % ADMIN_PREFIX, controller='journal',
388 401 action="public_journal_atom")
389 402
390 403 rmap.connect('public_journal_atom_old',
391 404 '%s/public_journal_atom' % ADMIN_PREFIX, controller='journal',
392 405 action="public_journal_atom")
393 406
394 407 rmap.connect('toggle_following', '%s/toggle_following' % ADMIN_PREFIX,
395 408 controller='journal', action='toggle_following',
396 409 conditions=dict(method=["POST"]))
397 410
398 411 #SEARCH
399 412 rmap.connect('search', '%s/search' % ADMIN_PREFIX, controller='search',)
400 413 rmap.connect('search_repo_admin', '%s/search/{repo_name:.*}' % ADMIN_PREFIX,
401 414 controller='search',
402 415 conditions=dict(function=check_repo))
403 416 rmap.connect('search_repo', '/{repo_name:.*?}/search',
404 417 controller='search',
405 418 conditions=dict(function=check_repo),
406 419 )
407 420
408 421 #LOGIN/LOGOUT/REGISTER/SIGN IN
409 422 rmap.connect('login_home', '%s/login' % ADMIN_PREFIX, controller='login')
410 423 rmap.connect('logout_home', '%s/logout' % ADMIN_PREFIX, controller='login',
411 424 action='logout')
412 425
413 426 rmap.connect('register', '%s/register' % ADMIN_PREFIX, controller='login',
414 427 action='register')
415 428
416 429 rmap.connect('reset_password', '%s/password_reset' % ADMIN_PREFIX,
417 430 controller='login', action='password_reset')
418 431
419 432 rmap.connect('reset_password_confirmation',
420 433 '%s/password_reset_confirmation' % ADMIN_PREFIX,
421 434 controller='login', action='password_reset_confirmation')
422 435
423 436 #FEEDS
424 437 rmap.connect('rss_feed_home', '/{repo_name:.*?}/feed/rss',
425 438 controller='feed', action='rss',
426 439 conditions=dict(function=check_repo))
427 440
428 441 rmap.connect('atom_feed_home', '/{repo_name:.*?}/feed/atom',
429 442 controller='feed', action='atom',
430 443 conditions=dict(function=check_repo))
431 444
432 445 #==========================================================================
433 446 # REPOSITORY ROUTES
434 447 #==========================================================================
435 448 rmap.connect('summary_home', '/{repo_name:.*?}',
436 449 controller='summary',
437 450 conditions=dict(function=check_repo))
438 451
439 452 rmap.connect('repo_size', '/{repo_name:.*?}/repo_size',
440 453 controller='summary', action='repo_size',
441 454 conditions=dict(function=check_repo))
442 455
443 456 rmap.connect('repos_group_home', '/{group_name:.*}',
444 457 controller='admin/repos_groups', action="show_by_name",
445 458 conditions=dict(function=check_group))
446 459
447 460 rmap.connect('changeset_home', '/{repo_name:.*?}/changeset/{revision}',
448 461 controller='changeset', revision='tip',
449 462 conditions=dict(function=check_repo))
450 463
451 464 rmap.connect("edit_repo", "/{repo_name:.*?}/edit",
452 465 controller='admin/repos', action="edit",
453 466 conditions=dict(method=["GET"], function=check_repo)
454 467 )
455 468
456 469 #still working url for backward compat.
457 470 rmap.connect('raw_changeset_home_depraced',
458 471 '/{repo_name:.*?}/raw-changeset/{revision}',
459 472 controller='changeset', action='changeset_raw',
460 473 revision='tip', conditions=dict(function=check_repo))
461 474
462 475 ## new URLs
463 476 rmap.connect('changeset_raw_home',
464 477 '/{repo_name:.*?}/changeset-diff/{revision}',
465 478 controller='changeset', action='changeset_raw',
466 479 revision='tip', conditions=dict(function=check_repo))
467 480
468 481 rmap.connect('changeset_patch_home',
469 482 '/{repo_name:.*?}/changeset-patch/{revision}',
470 483 controller='changeset', action='changeset_patch',
471 484 revision='tip', conditions=dict(function=check_repo))
472 485
473 486 rmap.connect('changeset_download_home',
474 487 '/{repo_name:.*?}/changeset-download/{revision}',
475 488 controller='changeset', action='changeset_download',
476 489 revision='tip', conditions=dict(function=check_repo))
477 490
478 491 rmap.connect('changeset_comment',
479 492 '/{repo_name:.*?}/changeset/{revision}/comment',
480 493 controller='changeset', revision='tip', action='comment',
481 494 conditions=dict(function=check_repo))
482 495
483 496 rmap.connect('changeset_comment_delete',
484 497 '/{repo_name:.*?}/changeset/comment/{comment_id}/delete',
485 498 controller='changeset', action='delete_comment',
486 499 conditions=dict(function=check_repo, method=["DELETE"]))
487 500
488 501 rmap.connect('changeset_info', '/changeset_info/{repo_name:.*?}/{revision}',
489 502 controller='changeset', action='changeset_info')
490 503
491 504 rmap.connect('compare_url',
492 505 '/{repo_name:.*?}/compare/{org_ref_type}@{org_ref:.*?}...{other_ref_type}@{other_ref:.*?}',
493 506 controller='compare', action='index',
494 507 conditions=dict(function=check_repo),
495 508 requirements=dict(
496 509 org_ref_type='(branch|book|tag|rev|__other_ref_type__)',
497 510 other_ref_type='(branch|book|tag|rev|__org_ref_type__)')
498 511 )
499 512
500 513 rmap.connect('pullrequest_home',
501 514 '/{repo_name:.*?}/pull-request/new', controller='pullrequests',
502 515 action='index', conditions=dict(function=check_repo,
503 516 method=["GET"]))
504 517
505 518 rmap.connect('pullrequest',
506 519 '/{repo_name:.*?}/pull-request/new', controller='pullrequests',
507 520 action='create', conditions=dict(function=check_repo,
508 521 method=["POST"]))
509 522
510 523 rmap.connect('pullrequest_show',
511 524 '/{repo_name:.*?}/pull-request/{pull_request_id}',
512 525 controller='pullrequests',
513 526 action='show', conditions=dict(function=check_repo,
514 527 method=["GET"]))
515 528 rmap.connect('pullrequest_update',
516 529 '/{repo_name:.*?}/pull-request/{pull_request_id}',
517 530 controller='pullrequests',
518 531 action='update', conditions=dict(function=check_repo,
519 532 method=["PUT"]))
520 533 rmap.connect('pullrequest_delete',
521 534 '/{repo_name:.*?}/pull-request/{pull_request_id}',
522 535 controller='pullrequests',
523 536 action='delete', conditions=dict(function=check_repo,
524 537 method=["DELETE"]))
525 538
526 539 rmap.connect('pullrequest_show_all',
527 540 '/{repo_name:.*?}/pull-request',
528 541 controller='pullrequests',
529 542 action='show_all', conditions=dict(function=check_repo,
530 543 method=["GET"]))
531 544
532 545 rmap.connect('pullrequest_comment',
533 546 '/{repo_name:.*?}/pull-request-comment/{pull_request_id}',
534 547 controller='pullrequests',
535 548 action='comment', conditions=dict(function=check_repo,
536 549 method=["POST"]))
537 550
538 551 rmap.connect('pullrequest_comment_delete',
539 552 '/{repo_name:.*?}/pull-request-comment/{comment_id}/delete',
540 553 controller='pullrequests', action='delete_comment',
541 554 conditions=dict(function=check_repo, method=["DELETE"]))
542 555
543 556 rmap.connect('summary_home_summary', '/{repo_name:.*?}/summary',
544 557 controller='summary', conditions=dict(function=check_repo))
545 558
546 559 rmap.connect('shortlog_home', '/{repo_name:.*?}/shortlog',
547 560 controller='shortlog', conditions=dict(function=check_repo))
548 561
549 562 rmap.connect('shortlog_file_home', '/{repo_name:.*?}/shortlog/{revision}/{f_path:.*}',
550 563 controller='shortlog', f_path=None,
551 564 conditions=dict(function=check_repo))
552 565
553 566 rmap.connect('branches_home', '/{repo_name:.*?}/branches',
554 567 controller='branches', conditions=dict(function=check_repo))
555 568
556 569 rmap.connect('tags_home', '/{repo_name:.*?}/tags',
557 570 controller='tags', conditions=dict(function=check_repo))
558 571
559 572 rmap.connect('bookmarks_home', '/{repo_name:.*?}/bookmarks',
560 573 controller='bookmarks', conditions=dict(function=check_repo))
561 574
562 575 rmap.connect('changelog_home', '/{repo_name:.*?}/changelog',
563 576 controller='changelog', conditions=dict(function=check_repo))
564 577
565 578 rmap.connect('changelog_details', '/{repo_name:.*?}/changelog_details/{cs}',
566 579 controller='changelog', action='changelog_details',
567 580 conditions=dict(function=check_repo))
568 581
569 582 rmap.connect('files_home', '/{repo_name:.*?}/files/{revision}/{f_path:.*}',
570 583 controller='files', revision='tip', f_path='',
571 584 conditions=dict(function=check_repo))
572 585
573 586 rmap.connect('files_history_home',
574 587 '/{repo_name:.*?}/history/{revision}/{f_path:.*}',
575 588 controller='files', action='history', revision='tip', f_path='',
576 589 conditions=dict(function=check_repo))
577 590
578 591 rmap.connect('files_diff_home', '/{repo_name:.*?}/diff/{f_path:.*}',
579 592 controller='files', action='diff', revision='tip', f_path='',
580 593 conditions=dict(function=check_repo))
581 594
582 595 rmap.connect('files_rawfile_home',
583 596 '/{repo_name:.*?}/rawfile/{revision}/{f_path:.*}',
584 597 controller='files', action='rawfile', revision='tip',
585 598 f_path='', conditions=dict(function=check_repo))
586 599
587 600 rmap.connect('files_raw_home',
588 601 '/{repo_name:.*?}/raw/{revision}/{f_path:.*}',
589 602 controller='files', action='raw', revision='tip', f_path='',
590 603 conditions=dict(function=check_repo))
591 604
592 605 rmap.connect('files_annotate_home',
593 606 '/{repo_name:.*?}/annotate/{revision}/{f_path:.*}',
594 607 controller='files', action='index', revision='tip',
595 608 f_path='', annotate=True, conditions=dict(function=check_repo))
596 609
597 610 rmap.connect('files_edit_home',
598 611 '/{repo_name:.*?}/edit/{revision}/{f_path:.*}',
599 612 controller='files', action='edit', revision='tip',
600 613 f_path='', conditions=dict(function=check_repo))
601 614
602 615 rmap.connect('files_add_home',
603 616 '/{repo_name:.*?}/add/{revision}/{f_path:.*}',
604 617 controller='files', action='add', revision='tip',
605 618 f_path='', conditions=dict(function=check_repo))
606 619
607 620 rmap.connect('files_archive_home', '/{repo_name:.*?}/archive/{fname}',
608 621 controller='files', action='archivefile',
609 622 conditions=dict(function=check_repo))
610 623
611 624 rmap.connect('files_nodelist_home',
612 625 '/{repo_name:.*?}/nodelist/{revision}/{f_path:.*}',
613 626 controller='files', action='nodelist',
614 627 conditions=dict(function=check_repo))
615 628
616 629 rmap.connect('repo_settings_delete', '/{repo_name:.*?}/settings',
617 630 controller='settings', action="delete",
618 631 conditions=dict(method=["DELETE"], function=check_repo))
619 632
620 633 rmap.connect('repo_settings_update', '/{repo_name:.*?}/settings',
621 634 controller='settings', action="update",
622 635 conditions=dict(method=["PUT"], function=check_repo))
623 636
624 637 rmap.connect('repo_settings_home', '/{repo_name:.*?}/settings',
625 638 controller='settings', action='index',
626 639 conditions=dict(function=check_repo))
627 640
628 641 rmap.connect('toggle_locking', "/{repo_name:.*?}/locking_toggle",
629 642 controller='settings', action="toggle_locking",
630 643 conditions=dict(method=["GET"], function=check_repo))
631 644
632 645 rmap.connect('repo_fork_create_home', '/{repo_name:.*?}/fork',
633 646 controller='forks', action='fork_create',
634 647 conditions=dict(function=check_repo, method=["POST"]))
635 648
636 649 rmap.connect('repo_fork_home', '/{repo_name:.*?}/fork',
637 650 controller='forks', action='fork',
638 651 conditions=dict(function=check_repo))
639 652
640 653 rmap.connect('repo_forks_home', '/{repo_name:.*?}/forks',
641 654 controller='forks', action='forks',
642 655 conditions=dict(function=check_repo))
643 656
644 657 rmap.connect('repo_followers_home', '/{repo_name:.*?}/followers',
645 658 controller='followers', action='followers',
646 659 conditions=dict(function=check_repo))
647 660
648 661 return rmap
@@ -1,398 +1,392 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.admin.repos_groups
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Repository groups controller for RhodeCode
7 7
8 8 :created_on: Mar 23, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27 import traceback
28 28 import formencode
29 29
30 30 from formencode import htmlfill
31 31
32 32 from pylons import request, tmpl_context as c, url
33 33 from pylons.controllers.util import abort, redirect
34 34 from pylons.i18n.translation import _
35 35
36 36 from sqlalchemy.exc import IntegrityError
37 37
38 38 import rhodecode
39 39 from rhodecode.lib import helpers as h
40 40 from rhodecode.lib.ext_json import json
41 41 from rhodecode.lib.auth import LoginRequired, HasPermissionAnyDecorator,\
42 42 HasReposGroupPermissionAnyDecorator, HasReposGroupPermissionAll,\
43 43 HasPermissionAll
44 44 from rhodecode.lib.base import BaseController, render
45 45 from rhodecode.model.db import RepoGroup, Repository
46 46 from rhodecode.model.repos_group import ReposGroupModel
47 47 from rhodecode.model.forms import ReposGroupForm
48 48 from rhodecode.model.meta import Session
49 49 from rhodecode.model.repo import RepoModel
50 50 from webob.exc import HTTPInternalServerError, HTTPNotFound
51 51 from rhodecode.lib.utils2 import str2bool, safe_int
52 52 from sqlalchemy.sql.expression import func
53 53 from rhodecode.model.scm import GroupList
54 54
55 55 log = logging.getLogger(__name__)
56 56
57 57
58 58 class ReposGroupsController(BaseController):
59 59 """REST Controller styled on the Atom Publishing Protocol"""
60 60 # To properly map this controller, ensure your config/routing.py
61 61 # file has a resource setup:
62 62 # map.resource('repos_group', 'repos_groups')
63 63
64 64 @LoginRequired()
65 65 def __before__(self):
66 66 super(ReposGroupsController, self).__before__()
67 67
68 68 def __load_defaults(self, allow_empty_group=False, exclude_group_ids=[]):
69 69 if HasPermissionAll('hg.admin')('group edit'):
70 70 #we're global admin, we're ok and we can create TOP level groups
71 71 allow_empty_group = True
72 72
73 73 #override the choices for this form, we need to filter choices
74 74 #and display only those we have ADMIN right
75 75 groups_with_admin_rights = GroupList(RepoGroup.query().all(),
76 76 perm_set=['group.admin'])
77 77 c.repo_groups = RepoGroup.groups_choices(groups=groups_with_admin_rights,
78 78 show_empty_group=allow_empty_group)
79 79 # exclude filtered ids
80 80 c.repo_groups = filter(lambda x: x[0] not in exclude_group_ids,
81 81 c.repo_groups)
82 82 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
83 83 repo_model = RepoModel()
84 84 c.users_array = repo_model.get_users_js()
85 85 c.users_groups_array = repo_model.get_users_groups_js()
86 86
87 87 def __load_data(self, group_id):
88 88 """
89 89 Load defaults settings for edit, and update
90 90
91 91 :param group_id:
92 92 """
93 93 repo_group = RepoGroup.get_or_404(group_id)
94 94 data = repo_group.get_dict()
95 95 data['group_name'] = repo_group.name
96 96
97 97 # fill repository users
98 98 for p in repo_group.repo_group_to_perm:
99 99 data.update({'u_perm_%s' % p.user.username:
100 100 p.permission.permission_name})
101 101
102 102 # fill repository groups
103 103 for p in repo_group.users_group_to_perm:
104 104 data.update({'g_perm_%s' % p.users_group.users_group_name:
105 105 p.permission.permission_name})
106 106
107 107 return data
108 108
109 109 def _revoke_perms_on_yourself(self, form_result):
110 110 _up = filter(lambda u: c.rhodecode_user.username == u[0],
111 111 form_result['perms_updates'])
112 112 _new = filter(lambda u: c.rhodecode_user.username == u[0],
113 113 form_result['perms_new'])
114 114 if _new and _new[0][1] != 'group.admin' or _up and _up[0][1] != 'group.admin':
115 115 return True
116 116 return False
117 117
118 118 def index(self, format='html'):
119 119 """GET /repos_groups: All items in the collection"""
120 120 # url('repos_groups')
121 121 group_iter = GroupList(RepoGroup.query().all(), perm_set=['group.admin'])
122 122 sk = lambda g: g.parents[0].group_name if g.parents else g.group_name
123 123 c.groups = sorted(group_iter, key=sk)
124 124 return render('admin/repos_groups/repos_groups_show.html')
125 125
126 126 def create(self):
127 127 """POST /repos_groups: Create a new item"""
128 128 # url('repos_groups')
129 129
130 130 self.__load_defaults()
131 131
132 132 # permissions for can create group based on parent_id are checked
133 133 # here in the Form
134 134 repos_group_form = ReposGroupForm(available_groups=
135 135 map(lambda k: unicode(k[0]), c.repo_groups))()
136 136 try:
137 137 form_result = repos_group_form.to_python(dict(request.POST))
138 138 ReposGroupModel().create(
139 139 group_name=form_result['group_name'],
140 140 group_description=form_result['group_description'],
141 141 parent=form_result['group_parent_id'],
142 142 owner=self.rhodecode_user.user_id
143 143 )
144 144 Session().commit()
145 145 h.flash(_('created repos group %s') \
146 146 % form_result['group_name'], category='success')
147 147 #TODO: in futureaction_logger(, '', '', '', self.sa)
148 148 except formencode.Invalid, errors:
149 149 return htmlfill.render(
150 150 render('admin/repos_groups/repos_groups_add.html'),
151 151 defaults=errors.value,
152 152 errors=errors.error_dict or {},
153 153 prefix_error=False,
154 154 encoding="UTF-8")
155 155 except Exception:
156 156 log.error(traceback.format_exc())
157 157 h.flash(_('error occurred during creation of repos group %s') \
158 158 % request.POST.get('group_name'), category='error')
159 159 parent_group_id = form_result['group_parent_id']
160 160 #TODO: maybe we should get back to the main view, not the admin one
161 161 return redirect(url('repos_groups', parent_group=parent_group_id))
162 162
163 163 def new(self, format='html'):
164 164 """GET /repos_groups/new: Form to create a new item"""
165 165 # url('new_repos_group')
166 166 if HasPermissionAll('hg.admin')('group create'):
167 167 #we're global admin, we're ok and we can create TOP level groups
168 168 pass
169 169 else:
170 170 # we pass in parent group into creation form, thus we know
171 171 # what would be the group, we can check perms here !
172 172 group_id = safe_int(request.GET.get('parent_group'))
173 173 group = RepoGroup.get(group_id) if group_id else None
174 174 group_name = group.group_name if group else None
175 175 if HasReposGroupPermissionAll('group.admin')(group_name, 'group create'):
176 176 pass
177 177 else:
178 178 return abort(403)
179 179
180 180 self.__load_defaults()
181 181 return render('admin/repos_groups/repos_groups_add.html')
182 182
183 183 @HasReposGroupPermissionAnyDecorator('group.admin')
184 184 def update(self, group_name):
185 185 """PUT /repos_groups/group_name: Update an existing item"""
186 186 # Forms posted to this method should contain a hidden field:
187 187 # <input type="hidden" name="_method" value="PUT" />
188 188 # Or using helpers:
189 189 # h.form(url('repos_group', group_name=GROUP_NAME),
190 190 # method='put')
191 191 # url('repos_group', group_name=GROUP_NAME)
192 192
193 193 c.repos_group = ReposGroupModel()._get_repos_group(group_name)
194 194 if HasPermissionAll('hg.admin')('group edit'):
195 195 #we're global admin, we're ok and we can create TOP level groups
196 196 allow_empty_group = True
197 197 elif not c.repos_group.parent_group:
198 198 allow_empty_group = True
199 199 else:
200 200 allow_empty_group = False
201 201 self.__load_defaults(allow_empty_group=allow_empty_group,
202 202 exclude_group_ids=[c.repos_group.group_id])
203 203
204 204 repos_group_form = ReposGroupForm(
205 205 edit=True,
206 206 old_data=c.repos_group.get_dict(),
207 207 available_groups=c.repo_groups_choices,
208 208 can_create_in_root=allow_empty_group,
209 209 )()
210 210 try:
211 211 form_result = repos_group_form.to_python(dict(request.POST))
212 212 if not c.rhodecode_user.is_admin:
213 213 if self._revoke_perms_on_yourself(form_result):
214 214 msg = _('Cannot revoke permission for yourself as admin')
215 215 h.flash(msg, category='warning')
216 216 raise Exception('revoke admin permission on self')
217 217
218 218 new_gr = ReposGroupModel().update(group_name, form_result)
219 219 Session().commit()
220 220 h.flash(_('updated repos group %s') \
221 221 % form_result['group_name'], category='success')
222 222 # we now have new name !
223 223 group_name = new_gr.group_name
224 224 #TODO: in future action_logger(, '', '', '', self.sa)
225 225 except formencode.Invalid, errors:
226 226
227 227 return htmlfill.render(
228 228 render('admin/repos_groups/repos_groups_edit.html'),
229 229 defaults=errors.value,
230 230 errors=errors.error_dict or {},
231 231 prefix_error=False,
232 232 encoding="UTF-8")
233 233 except Exception:
234 234 log.error(traceback.format_exc())
235 235 h.flash(_('error occurred during update of repos group %s') \
236 236 % request.POST.get('group_name'), category='error')
237 237
238 238 return redirect(url('edit_repos_group', group_name=group_name))
239 239
240 240 @HasReposGroupPermissionAnyDecorator('group.admin')
241 241 def delete(self, group_name):
242 242 """DELETE /repos_groups/group_name: Delete an existing item"""
243 243 # Forms posted to this method should contain a hidden field:
244 244 # <input type="hidden" name="_method" value="DELETE" />
245 245 # Or using helpers:
246 246 # h.form(url('repos_group', group_name=GROUP_NAME),
247 247 # method='delete')
248 248 # url('repos_group', group_name=GROUP_NAME)
249 249
250 250 gr = c.repos_group = ReposGroupModel()._get_repos_group(group_name)
251 251 repos = gr.repositories.all()
252 252 if repos:
253 253 h.flash(_('This group contains %s repositores and cannot be '
254 'deleted') % len(repos),
255 category='error')
254 'deleted') % len(repos), category='warning')
255 return redirect(url('repos_groups'))
256
257 children = gr.children.all()
258 if children:
259 h.flash(_('This group contains %s subgroups and cannot be deleted'
260 % (len(children))), category='warning')
256 261 return redirect(url('repos_groups'))
257 262
258 263 try:
259 264 ReposGroupModel().delete(group_name)
260 265 Session().commit()
261 h.flash(_('removed repos group %s') % gr.group_name,
266 h.flash(_('removed repos group %s') % group_name,
262 267 category='success')
263 268 #TODO: in future action_logger(, '', '', '', self.sa)
264 except IntegrityError, e:
265 if str(e.message).find('groups_group_parent_id_fkey') != -1:
266 log.error(traceback.format_exc())
267 h.flash(_('Cannot delete this group it still contains '
268 'subgroups'),
269 category='warning')
270 else:
271 log.error(traceback.format_exc())
272 h.flash(_('error occurred during deletion of repos '
273 'group %s') % gr.group_name, category='error')
274
275 269 except Exception:
276 270 log.error(traceback.format_exc())
277 271 h.flash(_('error occurred during deletion of repos '
278 'group %s') % gr.group_name, category='error')
272 'group %s') % group_name, category='error')
279 273
280 274 return redirect(url('repos_groups'))
281 275
282 276 @HasReposGroupPermissionAnyDecorator('group.admin')
283 277 def delete_repos_group_user_perm(self, group_name):
284 278 """
285 279 DELETE an existing repository group permission user
286 280
287 281 :param group_name:
288 282 """
289 283 try:
290 284 if not c.rhodecode_user.is_admin:
291 285 if c.rhodecode_user.user_id == safe_int(request.POST['user_id']):
292 286 msg = _('Cannot revoke permission for yourself as admin')
293 287 h.flash(msg, category='warning')
294 288 raise Exception('revoke admin permission on self')
295 289 recursive = str2bool(request.POST.get('recursive', False))
296 290 ReposGroupModel().delete_permission(
297 291 repos_group=group_name, obj=request.POST['user_id'],
298 292 obj_type='user', recursive=recursive
299 293 )
300 294 Session().commit()
301 295 except Exception:
302 296 log.error(traceback.format_exc())
303 297 h.flash(_('An error occurred during deletion of group user'),
304 298 category='error')
305 299 raise HTTPInternalServerError()
306 300
307 301 @HasReposGroupPermissionAnyDecorator('group.admin')
308 302 def delete_repos_group_users_group_perm(self, group_name):
309 303 """
310 304 DELETE an existing repository group permission user group
311 305
312 306 :param group_name:
313 307 """
314 308
315 309 try:
316 310 recursive = str2bool(request.POST.get('recursive', False))
317 311 ReposGroupModel().delete_permission(
318 312 repos_group=group_name, obj=request.POST['users_group_id'],
319 313 obj_type='users_group', recursive=recursive
320 314 )
321 315 Session().commit()
322 316 except Exception:
323 317 log.error(traceback.format_exc())
324 318 h.flash(_('An error occurred during deletion of group'
325 319 ' user groups'),
326 320 category='error')
327 321 raise HTTPInternalServerError()
328 322
329 323 def show_by_name(self, group_name):
330 324 """
331 325 This is a proxy that does a lookup group_name -> id, and shows
332 326 the group by id view instead
333 327 """
334 328 group_name = group_name.rstrip('/')
335 329 id_ = RepoGroup.get_by_group_name(group_name)
336 330 if id_:
337 331 return self.show(id_.group_id)
338 332 raise HTTPNotFound
339 333
340 334 @HasReposGroupPermissionAnyDecorator('group.read', 'group.write',
341 335 'group.admin')
342 336 def show(self, group_name, format='html'):
343 337 """GET /repos_groups/group_name: Show a specific item"""
344 338 # url('repos_group', group_name=GROUP_NAME)
345 339
346 340 c.group = c.repos_group = ReposGroupModel()._get_repos_group(group_name)
347 341 c.group_repos = c.group.repositories.all()
348 342
349 343 #overwrite our cached list with current filter
350 344 gr_filter = c.group_repos
351 345 c.repo_cnt = 0
352 346
353 347 groups = RepoGroup.query().order_by(RepoGroup.group_name)\
354 348 .filter(RepoGroup.group_parent_id == c.group.group_id).all()
355 349 c.groups = self.scm_model.get_repos_groups(groups)
356 350
357 351 if c.visual.lightweight_dashboard is False:
358 352 c.repos_list = self.scm_model.get_repos(all_repos=gr_filter)
359 353 ## lightweight version of dashboard
360 354 else:
361 355 c.repos_list = Repository.query()\
362 356 .filter(Repository.group_id == c.group.group_id)\
363 357 .order_by(func.lower(Repository.repo_name))\
364 358 .all()
365 359
366 360 repos_data = RepoModel().get_repos_as_dict(repos_list=c.repos_list,
367 361 admin=False)
368 362 #json used to render the grid
369 363 c.data = json.dumps(repos_data)
370 364
371 365 return render('admin/repos_groups/repos_groups.html')
372 366
373 367 @HasReposGroupPermissionAnyDecorator('group.admin')
374 368 def edit(self, group_name, format='html'):
375 369 """GET /repos_groups/group_name/edit: Form to edit an existing item"""
376 370 # url('edit_repos_group', group_name=GROUP_NAME)
377 371
378 372 c.repos_group = ReposGroupModel()._get_repos_group(group_name)
379 373 #we can only allow moving empty group if it's already a top-level
380 374 #group, ie has no parents, or we're admin
381 375 if HasPermissionAll('hg.admin')('group edit'):
382 376 #we're global admin, we're ok and we can create TOP level groups
383 377 allow_empty_group = True
384 378 elif not c.repos_group.parent_group:
385 379 allow_empty_group = True
386 380 else:
387 381 allow_empty_group = False
388 382
389 383 self.__load_defaults(allow_empty_group=allow_empty_group,
390 384 exclude_group_ids=[c.repos_group.group_id])
391 385 defaults = self.__load_data(c.repos_group.group_id)
392 386
393 387 return htmlfill.render(
394 388 render('admin/repos_groups/repos_groups_edit.html'),
395 389 defaults=defaults,
396 390 encoding="UTF-8",
397 391 force_defaults=False
398 392 )
@@ -1,801 +1,800 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.utils
4 4 ~~~~~~~~~~~~~~~~~~~
5 5
6 6 Utilities library for RhodeCode
7 7
8 8 :created_on: Apr 18, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import os
27 27 import re
28 28 import logging
29 29 import datetime
30 30 import traceback
31 31 import paste
32 32 import beaker
33 33 import tarfile
34 34 import shutil
35 35 import decorator
36 36 import warnings
37 37 from os.path import abspath
38 38 from os.path import dirname as dn, join as jn
39 39
40 40 from paste.script.command import Command, BadCommand
41 41
42 42 from mercurial import ui, config
43 43
44 44 from webhelpers.text import collapse, remove_formatting, strip_tags
45 45
46 46 from rhodecode.lib.vcs import get_backend
47 47 from rhodecode.lib.vcs.backends.base import BaseChangeset
48 48 from rhodecode.lib.vcs.utils.lazy import LazyProperty
49 49 from rhodecode.lib.vcs.utils.helpers import get_scm
50 50 from rhodecode.lib.vcs.exceptions import VCSError
51 51
52 52 from rhodecode.lib.caching_query import FromCache
53 53
54 54 from rhodecode.model import meta
55 55 from rhodecode.model.db import Repository, User, RhodeCodeUi, \
56 56 UserLog, RepoGroup, RhodeCodeSetting, CacheInvalidation
57 57 from rhodecode.model.meta import Session
58 58 from rhodecode.model.repos_group import ReposGroupModel
59 59 from rhodecode.lib.utils2 import safe_str, safe_unicode
60 60 from rhodecode.lib.vcs.utils.fakemod import create_module
61 61
62 62 log = logging.getLogger(__name__)
63 63
64 64 REMOVED_REPO_PAT = re.compile(r'rm__\d{8}_\d{6}_\d{6}__.*')
65 65
66 66
67 67 def recursive_replace(str_, replace=' '):
68 68 """
69 69 Recursive replace of given sign to just one instance
70 70
71 71 :param str_: given string
72 72 :param replace: char to find and replace multiple instances
73 73
74 74 Examples::
75 75 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
76 76 'Mighty-Mighty-Bo-sstones'
77 77 """
78 78
79 79 if str_.find(replace * 2) == -1:
80 80 return str_
81 81 else:
82 82 str_ = str_.replace(replace * 2, replace)
83 83 return recursive_replace(str_, replace)
84 84
85 85
86 86 def repo_name_slug(value):
87 87 """
88 88 Return slug of name of repository
89 89 This function is called on each creation/modification
90 90 of repository to prevent bad names in repo
91 91 """
92 92
93 93 slug = remove_formatting(value)
94 94 slug = strip_tags(slug)
95 95
96 96 for c in """`?=[]\;'"<>,/~!@#$%^&*()+{}|: """:
97 97 slug = slug.replace(c, '-')
98 98 slug = recursive_replace(slug, '-')
99 99 slug = collapse(slug, '-')
100 100 return slug
101 101
102 102
103 103 def get_repo_slug(request):
104 104 _repo = request.environ['pylons.routes_dict'].get('repo_name')
105 105 if _repo:
106 106 _repo = _repo.rstrip('/')
107 107 return _repo
108 108
109 109
110 110 def get_repos_group_slug(request):
111 111 _group = request.environ['pylons.routes_dict'].get('group_name')
112 112 if _group:
113 113 _group = _group.rstrip('/')
114 114 return _group
115 115
116 116
117 117 def action_logger(user, action, repo, ipaddr='', sa=None, commit=False):
118 118 """
119 119 Action logger for various actions made by users
120 120
121 121 :param user: user that made this action, can be a unique username string or
122 122 object containing user_id attribute
123 123 :param action: action to log, should be on of predefined unique actions for
124 124 easy translations
125 125 :param repo: string name of repository or object containing repo_id,
126 126 that action was made on
127 127 :param ipaddr: optional ip address from what the action was made
128 128 :param sa: optional sqlalchemy session
129 129
130 130 """
131 131
132 132 if not sa:
133 133 sa = meta.Session()
134 134
135 135 try:
136 136 if hasattr(user, 'user_id'):
137 137 user_obj = User.get(user.user_id)
138 138 elif isinstance(user, basestring):
139 139 user_obj = User.get_by_username(user)
140 140 else:
141 141 raise Exception('You have to provide a user object or a username')
142 142
143 143 if hasattr(repo, 'repo_id'):
144 144 repo_obj = Repository.get(repo.repo_id)
145 145 repo_name = repo_obj.repo_name
146 146 elif isinstance(repo, basestring):
147 147 repo_name = repo.lstrip('/')
148 148 repo_obj = Repository.get_by_repo_name(repo_name)
149 149 else:
150 150 repo_obj = None
151 151 repo_name = ''
152 152
153 153 user_log = UserLog()
154 154 user_log.user_id = user_obj.user_id
155 155 user_log.username = user_obj.username
156 156 user_log.action = safe_unicode(action)
157 157
158 158 user_log.repository = repo_obj
159 159 user_log.repository_name = repo_name
160 160
161 161 user_log.action_date = datetime.datetime.now()
162 162 user_log.user_ip = ipaddr
163 163 sa.add(user_log)
164 164
165 165 log.info('Logging action %s on %s by %s' %
166 166 (action, safe_unicode(repo), user_obj))
167 167 if commit:
168 168 sa.commit()
169 169 except:
170 170 log.error(traceback.format_exc())
171 171 raise
172 172
173 173
174 174 def get_repos(path, recursive=False, skip_removed_repos=True):
175 175 """
176 176 Scans given path for repos and return (name,(type,path)) tuple
177 177
178 178 :param path: path to scan for repositories
179 179 :param recursive: recursive search and return names with subdirs in front
180 180 """
181 181
182 182 # remove ending slash for better results
183 183 path = path.rstrip(os.sep)
184 184 log.debug('now scanning in %s location recursive:%s...' % (path, recursive))
185 185
186 186 def _get_repos(p):
187 187 if not os.access(p, os.W_OK):
188 188 return
189 189 for dirpath in os.listdir(p):
190 190 if os.path.isfile(os.path.join(p, dirpath)):
191 191 continue
192 192 cur_path = os.path.join(p, dirpath)
193 193
194 194 # skip removed repos
195 195 if skip_removed_repos and REMOVED_REPO_PAT.match(dirpath):
196 196 continue
197 197
198 198 #skip .<somethin> dirs
199 199 if dirpath.startswith('.'):
200 200 continue
201 201
202 202 try:
203 203 scm_info = get_scm(cur_path)
204 204 yield scm_info[1].split(path, 1)[-1].lstrip(os.sep), scm_info
205 205 except VCSError:
206 206 if not recursive:
207 207 continue
208 208 #check if this dir containts other repos for recursive scan
209 209 rec_path = os.path.join(p, dirpath)
210 210 if os.path.isdir(rec_path):
211 211 for inner_scm in _get_repos(rec_path):
212 212 yield inner_scm
213 213
214 214 return _get_repos(path)
215 215
216 216 #alias for backward compat
217 217 get_filesystem_repos = get_repos
218 218
219 219
220 220 def is_valid_repo(repo_name, base_path, scm=None):
221 221 """
222 222 Returns True if given path is a valid repository False otherwise.
223 223 If scm param is given also compare if given scm is the same as expected
224 224 from scm parameter
225 225
226 226 :param repo_name:
227 227 :param base_path:
228 228 :param scm:
229 229
230 230 :return True: if given path is a valid repository
231 231 """
232 232 full_path = os.path.join(safe_str(base_path), safe_str(repo_name))
233 233
234 234 try:
235 235 scm_ = get_scm(full_path)
236 236 if scm:
237 237 return scm_[0] == scm
238 238 return True
239 239 except VCSError:
240 240 return False
241 241
242 242
243 def is_valid_repos_group(repos_group_name, base_path):
243 def is_valid_repos_group(repos_group_name, base_path, skip_path_check=False):
244 244 """
245 245 Returns True if given path is a repos group False otherwise
246 246
247 247 :param repo_name:
248 248 :param base_path:
249 249 """
250 250 full_path = os.path.join(safe_str(base_path), safe_str(repos_group_name))
251 251
252 252 # check if it's not a repo
253 253 if is_valid_repo(repos_group_name, base_path):
254 254 return False
255 255
256 256 try:
257 257 # we need to check bare git repos at higher level
258 258 # since we might match branches/hooks/info/objects or possible
259 259 # other things inside bare git repo
260 260 get_scm(os.path.dirname(full_path))
261 261 return False
262 262 except VCSError:
263 263 pass
264 264
265 265 # check if it's a valid path
266 if os.path.isdir(full_path):
266 if skip_path_check or os.path.isdir(full_path):
267 267 return True
268 268
269 269 return False
270 270
271 271
272 272 def ask_ok(prompt, retries=4, complaint='Yes or no please!'):
273 273 while True:
274 274 ok = raw_input(prompt)
275 275 if ok in ('y', 'ye', 'yes'):
276 276 return True
277 277 if ok in ('n', 'no', 'nop', 'nope'):
278 278 return False
279 279 retries = retries - 1
280 280 if retries < 0:
281 281 raise IOError
282 282 print complaint
283 283
284 284 #propagated from mercurial documentation
285 285 ui_sections = ['alias', 'auth',
286 286 'decode/encode', 'defaults',
287 287 'diff', 'email',
288 288 'extensions', 'format',
289 289 'merge-patterns', 'merge-tools',
290 290 'hooks', 'http_proxy',
291 291 'smtp', 'patch',
292 292 'paths', 'profiling',
293 293 'server', 'trusted',
294 294 'ui', 'web', ]
295 295
296 296
297 297 def make_ui(read_from='file', path=None, checkpaths=True, clear_session=True):
298 298 """
299 299 A function that will read python rc files or database
300 300 and make an mercurial ui object from read options
301 301
302 302 :param path: path to mercurial config file
303 303 :param checkpaths: check the path
304 304 :param read_from: read from 'file' or 'db'
305 305 """
306 306
307 307 baseui = ui.ui()
308 308
309 309 # clean the baseui object
310 310 baseui._ocfg = config.config()
311 311 baseui._ucfg = config.config()
312 312 baseui._tcfg = config.config()
313 313
314 314 if read_from == 'file':
315 315 if not os.path.isfile(path):
316 316 log.debug('hgrc file is not present at %s, skipping...' % path)
317 317 return False
318 318 log.debug('reading hgrc from %s' % path)
319 319 cfg = config.config()
320 320 cfg.read(path)
321 321 for section in ui_sections:
322 322 for k, v in cfg.items(section):
323 323 log.debug('settings ui from file: [%s] %s=%s' % (section, k, v))
324 324 baseui.setconfig(safe_str(section), safe_str(k), safe_str(v))
325 325
326 326 elif read_from == 'db':
327 327 sa = meta.Session()
328 328 ret = sa.query(RhodeCodeUi)\
329 329 .options(FromCache("sql_cache_short", "get_hg_ui_settings"))\
330 330 .all()
331 331
332 332 hg_ui = ret
333 333 for ui_ in hg_ui:
334 334 if ui_.ui_active:
335 335 log.debug('settings ui from db: [%s] %s=%s', ui_.ui_section,
336 336 ui_.ui_key, ui_.ui_value)
337 337 baseui.setconfig(safe_str(ui_.ui_section), safe_str(ui_.ui_key),
338 338 safe_str(ui_.ui_value))
339 339 if ui_.ui_key == 'push_ssl':
340 340 # force set push_ssl requirement to False, rhodecode
341 341 # handles that
342 342 baseui.setconfig(safe_str(ui_.ui_section), safe_str(ui_.ui_key),
343 343 False)
344 344 if clear_session:
345 345 meta.Session.remove()
346 346 return baseui
347 347
348 348
349 349 def set_rhodecode_config(config):
350 350 """
351 351 Updates pylons config with new settings from database
352 352
353 353 :param config:
354 354 """
355 355 hgsettings = RhodeCodeSetting.get_app_settings()
356 356
357 357 for k, v in hgsettings.items():
358 358 config[k] = v
359 359
360 360
361 361 def invalidate_cache(cache_key, *args):
362 362 """
363 363 Puts cache invalidation task into db for
364 364 further global cache invalidation
365 365 """
366 366
367 367 from rhodecode.model.scm import ScmModel
368 368
369 369 if cache_key.startswith('get_repo_cached_'):
370 370 name = cache_key.split('get_repo_cached_')[-1]
371 371 ScmModel().mark_for_invalidation(name)
372 372
373 373
374 374 def map_groups(path):
375 375 """
376 376 Given a full path to a repository, create all nested groups that this
377 377 repo is inside. This function creates parent-child relationships between
378 378 groups and creates default perms for all new groups.
379 379
380 380 :param paths: full path to repository
381 381 """
382 382 sa = meta.Session()
383 383 groups = path.split(Repository.url_sep())
384 384 parent = None
385 385 group = None
386 386
387 387 # last element is repo in nested groups structure
388 388 groups = groups[:-1]
389 389 rgm = ReposGroupModel(sa)
390 390 for lvl, group_name in enumerate(groups):
391 391 group_name = '/'.join(groups[:lvl] + [group_name])
392 392 group = RepoGroup.get_by_group_name(group_name)
393 393 desc = '%s group' % group_name
394 394
395 395 # skip folders that are now removed repos
396 396 if REMOVED_REPO_PAT.match(group_name):
397 397 break
398 398
399 399 if group is None:
400 400 log.debug('creating group level: %s group_name: %s' % (lvl,
401 401 group_name))
402 402 group = RepoGroup(group_name, parent)
403 403 group.group_description = desc
404 404 sa.add(group)
405 405 rgm._create_default_perms(group)
406 406 sa.flush()
407 407 parent = group
408 408 return group
409 409
410 410
411 411 def repo2db_mapper(initial_repo_list, remove_obsolete=False,
412 412 install_git_hook=False):
413 413 """
414 414 maps all repos given in initial_repo_list, non existing repositories
415 415 are created, if remove_obsolete is True it also check for db entries
416 416 that are not in initial_repo_list and removes them.
417 417
418 418 :param initial_repo_list: list of repositories found by scanning methods
419 419 :param remove_obsolete: check for obsolete entries in database
420 420 :param install_git_hook: if this is True, also check and install githook
421 421 for a repo if missing
422 422 """
423 423 from rhodecode.model.repo import RepoModel
424 424 from rhodecode.model.scm import ScmModel
425 425 sa = meta.Session()
426 426 rm = RepoModel()
427 427 user = sa.query(User).filter(User.admin == True).first()
428 428 if user is None:
429 429 raise Exception('Missing administrative account!')
430 430 added = []
431 431
432 432 # # clear cache keys
433 433 # log.debug("Clearing cache keys now...")
434 434 # CacheInvalidation.clear_cache()
435 435 # sa.commit()
436 436
437 437 ##creation defaults
438 438 defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
439 439 enable_statistics = defs.get('repo_enable_statistics')
440 440 enable_locking = defs.get('repo_enable_locking')
441 441 enable_downloads = defs.get('repo_enable_downloads')
442 442 private = defs.get('repo_private')
443 443
444 444 for name, repo in initial_repo_list.items():
445 445 group = map_groups(name)
446 446 db_repo = rm.get_by_repo_name(name)
447 447 # found repo that is on filesystem not in RhodeCode database
448 448 if not db_repo:
449 449 log.info('repository %s not found, creating now' % name)
450 450 added.append(name)
451 451 desc = (repo.description
452 452 if repo.description != 'unknown'
453 453 else '%s repository' % name)
454 454
455 455 new_repo = rm.create_repo(
456 456 repo_name=name,
457 457 repo_type=repo.alias,
458 458 description=desc,
459 459 repos_group=getattr(group, 'group_id', None),
460 460 owner=user,
461 461 just_db=True,
462 462 enable_locking=enable_locking,
463 463 enable_downloads=enable_downloads,
464 464 enable_statistics=enable_statistics,
465 465 private=private
466 466 )
467 467 # we added that repo just now, and make sure it has githook
468 468 # installed
469 469 if new_repo.repo_type == 'git':
470 470 ScmModel().install_git_hook(new_repo.scm_instance)
471 471 new_repo.update_changeset_cache()
472 472 elif install_git_hook:
473 473 if db_repo.repo_type == 'git':
474 474 ScmModel().install_git_hook(db_repo.scm_instance)
475 475 # during starting install all cache keys for all repositories in the
476 476 # system, this will register all repos and multiple instances
477 477 key, _prefix, _org_key = CacheInvalidation._get_key(name)
478 478 CacheInvalidation.invalidate(name)
479 479 log.debug("Creating a cache key for %s, instance_id %s"
480 480 % (name, _prefix or 'unknown'))
481 481
482 482 sa.commit()
483 483 removed = []
484 484 if remove_obsolete:
485 485 # remove from database those repositories that are not in the filesystem
486 486 for repo in sa.query(Repository).all():
487 487 if repo.repo_name not in initial_repo_list.keys():
488 488 log.debug("Removing non-existing repository found in db `%s`" %
489 489 repo.repo_name)
490 490 try:
491 491 sa.delete(repo)
492 492 sa.commit()
493 493 removed.append(repo.repo_name)
494 494 except:
495 495 #don't hold further removals on error
496 496 log.error(traceback.format_exc())
497 497 sa.rollback()
498
499 498 return added, removed
500 499
501 500
502 501 # set cache regions for beaker so celery can utilise it
503 502 def add_cache(settings):
504 503 cache_settings = {'regions': None}
505 504 for key in settings.keys():
506 505 for prefix in ['beaker.cache.', 'cache.']:
507 506 if key.startswith(prefix):
508 507 name = key.split(prefix)[1].strip()
509 508 cache_settings[name] = settings[key].strip()
510 509 if cache_settings['regions']:
511 510 for region in cache_settings['regions'].split(','):
512 511 region = region.strip()
513 512 region_settings = {}
514 513 for key, value in cache_settings.items():
515 514 if key.startswith(region):
516 515 region_settings[key.split('.')[1]] = value
517 516 region_settings['expire'] = int(region_settings.get('expire',
518 517 60))
519 518 region_settings.setdefault('lock_dir',
520 519 cache_settings.get('lock_dir'))
521 520 region_settings.setdefault('data_dir',
522 521 cache_settings.get('data_dir'))
523 522
524 523 if 'type' not in region_settings:
525 524 region_settings['type'] = cache_settings.get('type',
526 525 'memory')
527 526 beaker.cache.cache_regions[region] = region_settings
528 527
529 528
530 529 def load_rcextensions(root_path):
531 530 import rhodecode
532 531 from rhodecode.config import conf
533 532
534 533 path = os.path.join(root_path, 'rcextensions', '__init__.py')
535 534 if os.path.isfile(path):
536 535 rcext = create_module('rc', path)
537 536 EXT = rhodecode.EXTENSIONS = rcext
538 537 log.debug('Found rcextensions now loading %s...' % rcext)
539 538
540 539 # Additional mappings that are not present in the pygments lexers
541 540 conf.LANGUAGES_EXTENSIONS_MAP.update(getattr(EXT, 'EXTRA_MAPPINGS', {}))
542 541
543 542 #OVERRIDE OUR EXTENSIONS FROM RC-EXTENSIONS (if present)
544 543
545 544 if getattr(EXT, 'INDEX_EXTENSIONS', []) != []:
546 545 log.debug('settings custom INDEX_EXTENSIONS')
547 546 conf.INDEX_EXTENSIONS = getattr(EXT, 'INDEX_EXTENSIONS', [])
548 547
549 548 #ADDITIONAL MAPPINGS
550 549 log.debug('adding extra into INDEX_EXTENSIONS')
551 550 conf.INDEX_EXTENSIONS.extend(getattr(EXT, 'EXTRA_INDEX_EXTENSIONS', []))
552 551
553 552 # auto check if the module is not missing any data, set to default if is
554 553 # this will help autoupdate new feature of rcext module
555 554 from rhodecode.config import rcextensions
556 555 for k in dir(rcextensions):
557 556 if not k.startswith('_') and not hasattr(EXT, k):
558 557 setattr(EXT, k, getattr(rcextensions, k))
559 558
560 559
561 560 def get_custom_lexer(extension):
562 561 """
563 562 returns a custom lexer if it's defined in rcextensions module, or None
564 563 if there's no custom lexer defined
565 564 """
566 565 import rhodecode
567 566 from pygments import lexers
568 567 #check if we didn't define this extension as other lexer
569 568 if rhodecode.EXTENSIONS and extension in rhodecode.EXTENSIONS.EXTRA_LEXERS:
570 569 _lexer_name = rhodecode.EXTENSIONS.EXTRA_LEXERS[extension]
571 570 return lexers.get_lexer_by_name(_lexer_name)
572 571
573 572
574 573 #==============================================================================
575 574 # TEST FUNCTIONS AND CREATORS
576 575 #==============================================================================
577 576 def create_test_index(repo_location, config, full_index):
578 577 """
579 578 Makes default test index
580 579
581 580 :param config: test config
582 581 :param full_index:
583 582 """
584 583
585 584 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
586 585 from rhodecode.lib.pidlock import DaemonLock, LockHeld
587 586
588 587 repo_location = repo_location
589 588
590 589 index_location = os.path.join(config['app_conf']['index_dir'])
591 590 if not os.path.exists(index_location):
592 591 os.makedirs(index_location)
593 592
594 593 try:
595 594 l = DaemonLock(file_=jn(dn(index_location), 'make_index.lock'))
596 595 WhooshIndexingDaemon(index_location=index_location,
597 596 repo_location=repo_location)\
598 597 .run(full_index=full_index)
599 598 l.release()
600 599 except LockHeld:
601 600 pass
602 601
603 602
604 603 def create_test_env(repos_test_path, config):
605 604 """
606 605 Makes a fresh database and
607 606 install test repository into tmp dir
608 607 """
609 608 from rhodecode.lib.db_manage import DbManage
610 609 from rhodecode.tests import HG_REPO, GIT_REPO, TESTS_TMP_PATH
611 610
612 611 # PART ONE create db
613 612 dbconf = config['sqlalchemy.db1.url']
614 613 log.debug('making test db %s' % dbconf)
615 614
616 615 # create test dir if it doesn't exist
617 616 if not os.path.isdir(repos_test_path):
618 617 log.debug('Creating testdir %s' % repos_test_path)
619 618 os.makedirs(repos_test_path)
620 619
621 620 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
622 621 tests=True)
623 622 dbmanage.create_tables(override=True)
624 623 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
625 624 dbmanage.create_default_user()
626 625 dbmanage.admin_prompt()
627 626 dbmanage.create_permissions()
628 627 dbmanage.populate_default_permissions()
629 628 Session().commit()
630 629 # PART TWO make test repo
631 630 log.debug('making test vcs repositories')
632 631
633 632 idx_path = config['app_conf']['index_dir']
634 633 data_path = config['app_conf']['cache_dir']
635 634
636 635 #clean index and data
637 636 if idx_path and os.path.exists(idx_path):
638 637 log.debug('remove %s' % idx_path)
639 638 shutil.rmtree(idx_path)
640 639
641 640 if data_path and os.path.exists(data_path):
642 641 log.debug('remove %s' % data_path)
643 642 shutil.rmtree(data_path)
644 643
645 644 #CREATE DEFAULT TEST REPOS
646 645 cur_dir = dn(dn(abspath(__file__)))
647 646 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
648 647 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
649 648 tar.close()
650 649
651 650 cur_dir = dn(dn(abspath(__file__)))
652 651 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_git.tar.gz"))
653 652 tar.extractall(jn(TESTS_TMP_PATH, GIT_REPO))
654 653 tar.close()
655 654
656 655 #LOAD VCS test stuff
657 656 from rhodecode.tests.vcs import setup_package
658 657 setup_package()
659 658
660 659
661 660 #==============================================================================
662 661 # PASTER COMMANDS
663 662 #==============================================================================
664 663 class BasePasterCommand(Command):
665 664 """
666 665 Abstract Base Class for paster commands.
667 666
668 667 The celery commands are somewhat aggressive about loading
669 668 celery.conf, and since our module sets the `CELERY_LOADER`
670 669 environment variable to our loader, we have to bootstrap a bit and
671 670 make sure we've had a chance to load the pylons config off of the
672 671 command line, otherwise everything fails.
673 672 """
674 673 min_args = 1
675 674 min_args_error = "Please provide a paster config file as an argument."
676 675 takes_config_file = 1
677 676 requires_config_file = True
678 677
679 678 def notify_msg(self, msg, log=False):
680 679 """Make a notification to user, additionally if logger is passed
681 680 it logs this action using given logger
682 681
683 682 :param msg: message that will be printed to user
684 683 :param log: logging instance, to use to additionally log this message
685 684
686 685 """
687 686 if log and isinstance(log, logging):
688 687 log(msg)
689 688
690 689 def run(self, args):
691 690 """
692 691 Overrides Command.run
693 692
694 693 Checks for a config file argument and loads it.
695 694 """
696 695 if len(args) < self.min_args:
697 696 raise BadCommand(
698 697 self.min_args_error % {'min_args': self.min_args,
699 698 'actual_args': len(args)})
700 699
701 700 # Decrement because we're going to lob off the first argument.
702 701 # @@ This is hacky
703 702 self.min_args -= 1
704 703 self.bootstrap_config(args[0])
705 704 self.update_parser()
706 705 return super(BasePasterCommand, self).run(args[1:])
707 706
708 707 def update_parser(self):
709 708 """
710 709 Abstract method. Allows for the class's parser to be updated
711 710 before the superclass's `run` method is called. Necessary to
712 711 allow options/arguments to be passed through to the underlying
713 712 celery command.
714 713 """
715 714 raise NotImplementedError("Abstract Method.")
716 715
717 716 def bootstrap_config(self, conf):
718 717 """
719 718 Loads the pylons configuration.
720 719 """
721 720 from pylons import config as pylonsconfig
722 721
723 722 self.path_to_ini_file = os.path.realpath(conf)
724 723 conf = paste.deploy.appconfig('config:' + self.path_to_ini_file)
725 724 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
726 725
727 726 def _init_session(self):
728 727 """
729 728 Inits SqlAlchemy Session
730 729 """
731 730 logging.config.fileConfig(self.path_to_ini_file)
732 731 from pylons import config
733 732 from rhodecode.model import init_model
734 733 from rhodecode.lib.utils2 import engine_from_config
735 734
736 735 #get to remove repos !!
737 736 add_cache(config)
738 737 engine = engine_from_config(config, 'sqlalchemy.db1.')
739 738 init_model(engine)
740 739
741 740
742 741 def check_git_version():
743 742 """
744 743 Checks what version of git is installed in system, and issues a warning
745 744 if it's too old for RhodeCode to properly work.
746 745 """
747 746 from rhodecode import BACKENDS
748 747 from rhodecode.lib.vcs.backends.git.repository import GitRepository
749 748 from distutils.version import StrictVersion
750 749
751 750 stdout, stderr = GitRepository._run_git_command('--version', _bare=True,
752 751 _safe=True)
753 752
754 753 ver = (stdout.split(' ')[-1] or '').strip() or '0.0.0'
755 754 if len(ver.split('.')) > 3:
756 755 #StrictVersion needs to be only 3 element type
757 756 ver = '.'.join(ver.split('.')[:3])
758 757 try:
759 758 _ver = StrictVersion(ver)
760 759 except:
761 760 _ver = StrictVersion('0.0.0')
762 761 stderr = traceback.format_exc()
763 762
764 763 req_ver = '1.7.4'
765 764 to_old_git = False
766 765 if _ver < StrictVersion(req_ver):
767 766 to_old_git = True
768 767
769 768 if 'git' in BACKENDS:
770 769 log.debug('GIT version detected: %s' % stdout)
771 770 if stderr:
772 771 log.warning('Unable to detect git version org error was:%r' % stderr)
773 772 elif to_old_git:
774 773 log.warning('RhodeCode detected git version %s, which is too old '
775 774 'for the system to function properly. Make sure '
776 775 'its version is at least %s' % (ver, req_ver))
777 776 return _ver
778 777
779 778
780 779 @decorator.decorator
781 780 def jsonify(func, *args, **kwargs):
782 781 """Action decorator that formats output for JSON
783 782
784 783 Given a function that will return content, this decorator will turn
785 784 the result into JSON, with a content-type of 'application/json' and
786 785 output it.
787 786
788 787 """
789 788 from pylons.decorators.util import get_pylons
790 789 from rhodecode.lib.ext_json import json
791 790 pylons = get_pylons(args)
792 791 pylons.response.headers['Content-Type'] = 'application/json; charset=utf-8'
793 792 data = func(*args, **kwargs)
794 793 if isinstance(data, (list, tuple)):
795 794 msg = "JSON responses with Array envelopes are susceptible to " \
796 795 "cross-site data leak attacks, see " \
797 796 "http://wiki.pylonshq.com/display/pylonsfaq/Warnings"
798 797 warnings.warn(msg, Warning, 2)
799 798 log.warning(msg)
800 799 log.debug("Returning JSON wrapped action output")
801 800 return json.dumps(data, encoding='utf-8')
General Comments 0
You need to be logged in to leave comments. Login now