##// END OF EJS Templates
unified annotation view with file source view
marcink -
r2177:ee07357d beta
parent child Browse files
Show More
@@ -1,507 +1,507 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 try:
36 36 by_id = repo_name.split('_')
37 37 if len(by_id) == 2 and by_id[1].isdigit():
38 38 repo_name = Repository.get(by_id[1]).repo_name
39 39 match_dict['repo_name'] = repo_name
40 40 except:
41 41 pass
42 42
43 43 return is_valid_repo(repo_name, config['base_path'])
44 44
45 45 def check_group(environ, match_dict):
46 46 """
47 47 check for valid repositories group for proper 404 handling
48 48
49 49 :param environ:
50 50 :param match_dict:
51 51 """
52 52 repos_group_name = match_dict.get('group_name')
53 53
54 54 return is_valid_repos_group(repos_group_name, config['base_path'])
55 55
56 56 def check_int(environ, match_dict):
57 57 return match_dict.get('id').isdigit()
58 58
59 59 # The ErrorController route (handles 404/500 error pages); it should
60 60 # likely stay at the top, ensuring it can always be resolved
61 61 rmap.connect('/error/{action}', controller='error')
62 62 rmap.connect('/error/{action}/{id}', controller='error')
63 63
64 64 #==========================================================================
65 65 # CUSTOM ROUTES HERE
66 66 #==========================================================================
67 67
68 68 #MAIN PAGE
69 69 rmap.connect('home', '/', controller='home', action='index')
70 70 rmap.connect('repo_switcher', '/repos', controller='home',
71 71 action='repo_switcher')
72 72 rmap.connect('branch_tag_switcher', '/branches-tags/{repo_name:.*}',
73 73 controller='home', action='branch_tag_switcher')
74 74 rmap.connect('bugtracker',
75 75 "http://bitbucket.org/marcinkuzminski/rhodecode/issues",
76 76 _static=True)
77 77 rmap.connect('rst_help',
78 78 "http://docutils.sourceforge.net/docs/user/rst/quickref.html",
79 79 _static=True)
80 80 rmap.connect('rhodecode_official', "http://rhodecode.org", _static=True)
81 81
82 82 #ADMIN REPOSITORY REST ROUTES
83 83 with rmap.submapper(path_prefix=ADMIN_PREFIX,
84 84 controller='admin/repos') as m:
85 85 m.connect("repos", "/repos",
86 86 action="create", conditions=dict(method=["POST"]))
87 87 m.connect("repos", "/repos",
88 88 action="index", conditions=dict(method=["GET"]))
89 89 m.connect("formatted_repos", "/repos.{format}",
90 90 action="index",
91 91 conditions=dict(method=["GET"]))
92 92 m.connect("new_repo", "/repos/new",
93 93 action="new", conditions=dict(method=["GET"]))
94 94 m.connect("formatted_new_repo", "/repos/new.{format}",
95 95 action="new", conditions=dict(method=["GET"]))
96 96 m.connect("/repos/{repo_name:.*}",
97 97 action="update", conditions=dict(method=["PUT"],
98 98 function=check_repo))
99 99 m.connect("/repos/{repo_name:.*}",
100 100 action="delete", conditions=dict(method=["DELETE"],
101 101 function=check_repo))
102 102 m.connect("edit_repo", "/repos/{repo_name:.*}/edit",
103 103 action="edit", conditions=dict(method=["GET"],
104 104 function=check_repo))
105 105 m.connect("formatted_edit_repo", "/repos/{repo_name:.*}.{format}/edit",
106 106 action="edit", conditions=dict(method=["GET"],
107 107 function=check_repo))
108 108 m.connect("repo", "/repos/{repo_name:.*}",
109 109 action="show", conditions=dict(method=["GET"],
110 110 function=check_repo))
111 111 m.connect("formatted_repo", "/repos/{repo_name:.*}.{format}",
112 112 action="show", conditions=dict(method=["GET"],
113 113 function=check_repo))
114 114 #ajax delete repo perm user
115 115 m.connect('delete_repo_user', "/repos_delete_user/{repo_name:.*}",
116 116 action="delete_perm_user",
117 117 conditions=dict(method=["DELETE"], function=check_repo))
118 118
119 119 #ajax delete repo perm users_group
120 120 m.connect('delete_repo_users_group',
121 121 "/repos_delete_users_group/{repo_name:.*}",
122 122 action="delete_perm_users_group",
123 123 conditions=dict(method=["DELETE"], function=check_repo))
124 124
125 125 #settings actions
126 126 m.connect('repo_stats', "/repos_stats/{repo_name:.*}",
127 127 action="repo_stats", conditions=dict(method=["DELETE"],
128 128 function=check_repo))
129 129 m.connect('repo_cache', "/repos_cache/{repo_name:.*}",
130 130 action="repo_cache", conditions=dict(method=["DELETE"],
131 131 function=check_repo))
132 132 m.connect('repo_public_journal', "/repos_public_journal/{repo_name:.*}",
133 133 action="repo_public_journal", conditions=dict(method=["PUT"],
134 134 function=check_repo))
135 135 m.connect('repo_pull', "/repo_pull/{repo_name:.*}",
136 136 action="repo_pull", conditions=dict(method=["PUT"],
137 137 function=check_repo))
138 138 m.connect('repo_as_fork', "/repo_as_fork/{repo_name:.*}",
139 139 action="repo_as_fork", conditions=dict(method=["PUT"],
140 140 function=check_repo))
141 141
142 142 with rmap.submapper(path_prefix=ADMIN_PREFIX,
143 143 controller='admin/repos_groups') as m:
144 144 m.connect("repos_groups", "/repos_groups",
145 145 action="create", conditions=dict(method=["POST"]))
146 146 m.connect("repos_groups", "/repos_groups",
147 147 action="index", conditions=dict(method=["GET"]))
148 148 m.connect("formatted_repos_groups", "/repos_groups.{format}",
149 149 action="index", conditions=dict(method=["GET"]))
150 150 m.connect("new_repos_group", "/repos_groups/new",
151 151 action="new", conditions=dict(method=["GET"]))
152 152 m.connect("formatted_new_repos_group", "/repos_groups/new.{format}",
153 153 action="new", conditions=dict(method=["GET"]))
154 154 m.connect("update_repos_group", "/repos_groups/{id}",
155 155 action="update", conditions=dict(method=["PUT"],
156 156 function=check_int))
157 157 m.connect("delete_repos_group", "/repos_groups/{id}",
158 158 action="delete", conditions=dict(method=["DELETE"],
159 159 function=check_int))
160 160 m.connect("edit_repos_group", "/repos_groups/{id}/edit",
161 161 action="edit", conditions=dict(method=["GET"],
162 162 function=check_int))
163 163 m.connect("formatted_edit_repos_group",
164 164 "/repos_groups/{id}.{format}/edit",
165 165 action="edit", conditions=dict(method=["GET"],
166 166 function=check_int))
167 167 m.connect("repos_group", "/repos_groups/{id}",
168 168 action="show", conditions=dict(method=["GET"],
169 169 function=check_int))
170 170 m.connect("formatted_repos_group", "/repos_groups/{id}.{format}",
171 171 action="show", conditions=dict(method=["GET"],
172 172 function=check_int))
173 173 # ajax delete repos group perm user
174 174 m.connect('delete_repos_group_user_perm',
175 175 "/delete_repos_group_user_perm/{group_name:.*}",
176 176 action="delete_repos_group_user_perm",
177 177 conditions=dict(method=["DELETE"], function=check_group))
178 178
179 179 # ajax delete repos group perm users_group
180 180 m.connect('delete_repos_group_users_group_perm',
181 181 "/delete_repos_group_users_group_perm/{group_name:.*}",
182 182 action="delete_repos_group_users_group_perm",
183 183 conditions=dict(method=["DELETE"], function=check_group))
184 184
185 185 #ADMIN USER REST ROUTES
186 186 with rmap.submapper(path_prefix=ADMIN_PREFIX,
187 187 controller='admin/users') as m:
188 188 m.connect("users", "/users",
189 189 action="create", conditions=dict(method=["POST"]))
190 190 m.connect("users", "/users",
191 191 action="index", conditions=dict(method=["GET"]))
192 192 m.connect("formatted_users", "/users.{format}",
193 193 action="index", conditions=dict(method=["GET"]))
194 194 m.connect("new_user", "/users/new",
195 195 action="new", conditions=dict(method=["GET"]))
196 196 m.connect("formatted_new_user", "/users/new.{format}",
197 197 action="new", conditions=dict(method=["GET"]))
198 198 m.connect("update_user", "/users/{id}",
199 199 action="update", conditions=dict(method=["PUT"]))
200 200 m.connect("delete_user", "/users/{id}",
201 201 action="delete", conditions=dict(method=["DELETE"]))
202 202 m.connect("edit_user", "/users/{id}/edit",
203 203 action="edit", conditions=dict(method=["GET"]))
204 204 m.connect("formatted_edit_user",
205 205 "/users/{id}.{format}/edit",
206 206 action="edit", conditions=dict(method=["GET"]))
207 207 m.connect("user", "/users/{id}",
208 208 action="show", conditions=dict(method=["GET"]))
209 209 m.connect("formatted_user", "/users/{id}.{format}",
210 210 action="show", conditions=dict(method=["GET"]))
211 211
212 212 #EXTRAS USER ROUTES
213 213 m.connect("user_perm", "/users_perm/{id}",
214 214 action="update_perm", conditions=dict(method=["PUT"]))
215 215
216 216 #ADMIN USERS REST ROUTES
217 217 with rmap.submapper(path_prefix=ADMIN_PREFIX,
218 218 controller='admin/users_groups') as m:
219 219 m.connect("users_groups", "/users_groups",
220 220 action="create", conditions=dict(method=["POST"]))
221 221 m.connect("users_groups", "/users_groups",
222 222 action="index", conditions=dict(method=["GET"]))
223 223 m.connect("formatted_users_groups", "/users_groups.{format}",
224 224 action="index", conditions=dict(method=["GET"]))
225 225 m.connect("new_users_group", "/users_groups/new",
226 226 action="new", conditions=dict(method=["GET"]))
227 227 m.connect("formatted_new_users_group", "/users_groups/new.{format}",
228 228 action="new", conditions=dict(method=["GET"]))
229 229 m.connect("update_users_group", "/users_groups/{id}",
230 230 action="update", conditions=dict(method=["PUT"]))
231 231 m.connect("delete_users_group", "/users_groups/{id}",
232 232 action="delete", conditions=dict(method=["DELETE"]))
233 233 m.connect("edit_users_group", "/users_groups/{id}/edit",
234 234 action="edit", conditions=dict(method=["GET"]))
235 235 m.connect("formatted_edit_users_group",
236 236 "/users_groups/{id}.{format}/edit",
237 237 action="edit", conditions=dict(method=["GET"]))
238 238 m.connect("users_group", "/users_groups/{id}",
239 239 action="show", conditions=dict(method=["GET"]))
240 240 m.connect("formatted_users_group", "/users_groups/{id}.{format}",
241 241 action="show", conditions=dict(method=["GET"]))
242 242
243 243 #EXTRAS USER ROUTES
244 244 m.connect("users_group_perm", "/users_groups_perm/{id}",
245 245 action="update_perm", conditions=dict(method=["PUT"]))
246 246
247 247 #ADMIN GROUP REST ROUTES
248 248 rmap.resource('group', 'groups',
249 249 controller='admin/groups', path_prefix=ADMIN_PREFIX)
250 250
251 251 #ADMIN PERMISSIONS REST ROUTES
252 252 rmap.resource('permission', 'permissions',
253 253 controller='admin/permissions', path_prefix=ADMIN_PREFIX)
254 254
255 255 ##ADMIN LDAP SETTINGS
256 256 rmap.connect('ldap_settings', '%s/ldap' % ADMIN_PREFIX,
257 257 controller='admin/ldap_settings', action='ldap_settings',
258 258 conditions=dict(method=["POST"]))
259 259
260 260 rmap.connect('ldap_home', '%s/ldap' % ADMIN_PREFIX,
261 261 controller='admin/ldap_settings')
262 262
263 263 #ADMIN SETTINGS REST ROUTES
264 264 with rmap.submapper(path_prefix=ADMIN_PREFIX,
265 265 controller='admin/settings') as m:
266 266 m.connect("admin_settings", "/settings",
267 267 action="create", conditions=dict(method=["POST"]))
268 268 m.connect("admin_settings", "/settings",
269 269 action="index", conditions=dict(method=["GET"]))
270 270 m.connect("formatted_admin_settings", "/settings.{format}",
271 271 action="index", conditions=dict(method=["GET"]))
272 272 m.connect("admin_new_setting", "/settings/new",
273 273 action="new", conditions=dict(method=["GET"]))
274 274 m.connect("formatted_admin_new_setting", "/settings/new.{format}",
275 275 action="new", conditions=dict(method=["GET"]))
276 276 m.connect("/settings/{setting_id}",
277 277 action="update", conditions=dict(method=["PUT"]))
278 278 m.connect("/settings/{setting_id}",
279 279 action="delete", conditions=dict(method=["DELETE"]))
280 280 m.connect("admin_edit_setting", "/settings/{setting_id}/edit",
281 281 action="edit", conditions=dict(method=["GET"]))
282 282 m.connect("formatted_admin_edit_setting",
283 283 "/settings/{setting_id}.{format}/edit",
284 284 action="edit", conditions=dict(method=["GET"]))
285 285 m.connect("admin_setting", "/settings/{setting_id}",
286 286 action="show", conditions=dict(method=["GET"]))
287 287 m.connect("formatted_admin_setting", "/settings/{setting_id}.{format}",
288 288 action="show", conditions=dict(method=["GET"]))
289 289 m.connect("admin_settings_my_account", "/my_account",
290 290 action="my_account", conditions=dict(method=["GET"]))
291 291 m.connect("admin_settings_my_account_update", "/my_account_update",
292 292 action="my_account_update", conditions=dict(method=["PUT"]))
293 293 m.connect("admin_settings_create_repository", "/create_repository",
294 294 action="create_repository", conditions=dict(method=["GET"]))
295 295
296 296 #NOTIFICATION REST ROUTES
297 297 with rmap.submapper(path_prefix=ADMIN_PREFIX,
298 298 controller='admin/notifications') as m:
299 299 m.connect("notifications", "/notifications",
300 300 action="create", conditions=dict(method=["POST"]))
301 301 m.connect("notifications", "/notifications",
302 302 action="index", conditions=dict(method=["GET"]))
303 303 m.connect("notifications_mark_all_read", "/notifications/mark_all_read",
304 304 action="mark_all_read", conditions=dict(method=["GET"]))
305 305 m.connect("formatted_notifications", "/notifications.{format}",
306 306 action="index", conditions=dict(method=["GET"]))
307 307 m.connect("new_notification", "/notifications/new",
308 308 action="new", conditions=dict(method=["GET"]))
309 309 m.connect("formatted_new_notification", "/notifications/new.{format}",
310 310 action="new", conditions=dict(method=["GET"]))
311 311 m.connect("/notification/{notification_id}",
312 312 action="update", conditions=dict(method=["PUT"]))
313 313 m.connect("/notification/{notification_id}",
314 314 action="delete", conditions=dict(method=["DELETE"]))
315 315 m.connect("edit_notification", "/notification/{notification_id}/edit",
316 316 action="edit", conditions=dict(method=["GET"]))
317 317 m.connect("formatted_edit_notification",
318 318 "/notification/{notification_id}.{format}/edit",
319 319 action="edit", conditions=dict(method=["GET"]))
320 320 m.connect("notification", "/notification/{notification_id}",
321 321 action="show", conditions=dict(method=["GET"]))
322 322 m.connect("formatted_notification", "/notifications/{notification_id}.{format}",
323 323 action="show", conditions=dict(method=["GET"]))
324 324
325 325 #ADMIN MAIN PAGES
326 326 with rmap.submapper(path_prefix=ADMIN_PREFIX,
327 327 controller='admin/admin') as m:
328 328 m.connect('admin_home', '', action='index')
329 329 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
330 330 action='add_repo')
331 331
332 332 #==========================================================================
333 333 # API V2
334 334 #==========================================================================
335 335 with rmap.submapper(path_prefix=ADMIN_PREFIX,
336 336 controller='api/api') as m:
337 337 m.connect('api', '/api')
338 338
339 339 #USER JOURNAL
340 340 rmap.connect('journal', '%s/journal' % ADMIN_PREFIX, controller='journal')
341 341
342 342 rmap.connect('public_journal', '%s/public_journal' % ADMIN_PREFIX,
343 343 controller='journal', action="public_journal")
344 344
345 345 rmap.connect('public_journal_rss', '%s/public_journal_rss' % ADMIN_PREFIX,
346 346 controller='journal', action="public_journal_rss")
347 347
348 348 rmap.connect('public_journal_atom',
349 349 '%s/public_journal_atom' % ADMIN_PREFIX, controller='journal',
350 350 action="public_journal_atom")
351 351
352 352 rmap.connect('toggle_following', '%s/toggle_following' % ADMIN_PREFIX,
353 353 controller='journal', action='toggle_following',
354 354 conditions=dict(method=["POST"]))
355 355
356 356 #SEARCH
357 357 rmap.connect('search', '%s/search' % ADMIN_PREFIX, controller='search',)
358 358 rmap.connect('search_repo', '%s/search/{search_repo:.*}' % ADMIN_PREFIX,
359 359 controller='search')
360 360
361 361 #LOGIN/LOGOUT/REGISTER/SIGN IN
362 362 rmap.connect('login_home', '%s/login' % ADMIN_PREFIX, controller='login')
363 363 rmap.connect('logout_home', '%s/logout' % ADMIN_PREFIX, controller='login',
364 364 action='logout')
365 365
366 366 rmap.connect('register', '%s/register' % ADMIN_PREFIX, controller='login',
367 367 action='register')
368 368
369 369 rmap.connect('reset_password', '%s/password_reset' % ADMIN_PREFIX,
370 370 controller='login', action='password_reset')
371 371
372 372 rmap.connect('reset_password_confirmation',
373 373 '%s/password_reset_confirmation' % ADMIN_PREFIX,
374 374 controller='login', action='password_reset_confirmation')
375 375
376 376 #FEEDS
377 377 rmap.connect('rss_feed_home', '/{repo_name:.*}/feed/rss',
378 378 controller='feed', action='rss',
379 379 conditions=dict(function=check_repo))
380 380
381 381 rmap.connect('atom_feed_home', '/{repo_name:.*}/feed/atom',
382 382 controller='feed', action='atom',
383 383 conditions=dict(function=check_repo))
384 384
385 385 #==========================================================================
386 386 # REPOSITORY ROUTES
387 387 #==========================================================================
388 388 rmap.connect('summary_home', '/{repo_name:.*}',
389 389 controller='summary',
390 390 conditions=dict(function=check_repo))
391 391
392 392 rmap.connect('repos_group_home', '/{group_name:.*}',
393 393 controller='admin/repos_groups', action="show_by_name",
394 394 conditions=dict(function=check_group))
395 395
396 396 rmap.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
397 397 controller='changeset', revision='tip',
398 398 conditions=dict(function=check_repo))
399 399
400 400 rmap.connect('changeset_comment',
401 401 '/{repo_name:.*}/changeset/{revision}/comment',
402 402 controller='changeset', revision='tip', action='comment',
403 403 conditions=dict(function=check_repo))
404 404
405 405 rmap.connect('changeset_comment_delete',
406 406 '/{repo_name:.*}/changeset/comment/{comment_id}/delete',
407 407 controller='changeset', action='delete_comment',
408 408 conditions=dict(function=check_repo, method=["DELETE"]))
409 409
410 410 rmap.connect('raw_changeset_home',
411 411 '/{repo_name:.*}/raw-changeset/{revision}',
412 412 controller='changeset', action='raw_changeset',
413 413 revision='tip', conditions=dict(function=check_repo))
414 414
415 415 rmap.connect('summary_home', '/{repo_name:.*}/summary',
416 416 controller='summary', conditions=dict(function=check_repo))
417 417
418 418 rmap.connect('shortlog_home', '/{repo_name:.*}/shortlog',
419 419 controller='shortlog', conditions=dict(function=check_repo))
420 420
421 421 rmap.connect('branches_home', '/{repo_name:.*}/branches',
422 422 controller='branches', conditions=dict(function=check_repo))
423 423
424 424 rmap.connect('tags_home', '/{repo_name:.*}/tags',
425 425 controller='tags', conditions=dict(function=check_repo))
426 426
427 427 rmap.connect('bookmarks_home', '/{repo_name:.*}/bookmarks',
428 428 controller='bookmarks', conditions=dict(function=check_repo))
429 429
430 430 rmap.connect('changelog_home', '/{repo_name:.*}/changelog',
431 431 controller='changelog', conditions=dict(function=check_repo))
432 432
433 433 rmap.connect('changelog_details', '/{repo_name:.*}/changelog_details/{cs}',
434 434 controller='changelog', action='changelog_details',
435 435 conditions=dict(function=check_repo))
436 436
437 437 rmap.connect('files_home', '/{repo_name:.*}/files/{revision}/{f_path:.*}',
438 438 controller='files', revision='tip', f_path='',
439 439 conditions=dict(function=check_repo))
440 440
441 441 rmap.connect('files_diff_home', '/{repo_name:.*}/diff/{f_path:.*}',
442 442 controller='files', action='diff', revision='tip', f_path='',
443 443 conditions=dict(function=check_repo))
444 444
445 445 rmap.connect('files_rawfile_home',
446 446 '/{repo_name:.*}/rawfile/{revision}/{f_path:.*}',
447 447 controller='files', action='rawfile', revision='tip',
448 448 f_path='', conditions=dict(function=check_repo))
449 449
450 450 rmap.connect('files_raw_home',
451 451 '/{repo_name:.*}/raw/{revision}/{f_path:.*}',
452 452 controller='files', action='raw', revision='tip', f_path='',
453 453 conditions=dict(function=check_repo))
454 454
455 455 rmap.connect('files_annotate_home',
456 456 '/{repo_name:.*}/annotate/{revision}/{f_path:.*}',
457 controller='files', action='annotate', revision='tip',
458 f_path='', conditions=dict(function=check_repo))
457 controller='files', action='index', revision='tip',
458 f_path='', annotate=True, conditions=dict(function=check_repo))
459 459
460 460 rmap.connect('files_edit_home',
461 461 '/{repo_name:.*}/edit/{revision}/{f_path:.*}',
462 462 controller='files', action='edit', revision='tip',
463 463 f_path='', conditions=dict(function=check_repo))
464 464
465 465 rmap.connect('files_add_home',
466 466 '/{repo_name:.*}/add/{revision}/{f_path:.*}',
467 467 controller='files', action='add', revision='tip',
468 468 f_path='', conditions=dict(function=check_repo))
469 469
470 470 rmap.connect('files_archive_home', '/{repo_name:.*}/archive/{fname}',
471 471 controller='files', action='archivefile',
472 472 conditions=dict(function=check_repo))
473 473
474 474 rmap.connect('files_nodelist_home',
475 475 '/{repo_name:.*}/nodelist/{revision}/{f_path:.*}',
476 476 controller='files', action='nodelist',
477 477 conditions=dict(function=check_repo))
478 478
479 479 rmap.connect('repo_settings_delete', '/{repo_name:.*}/settings',
480 480 controller='settings', action="delete",
481 481 conditions=dict(method=["DELETE"], function=check_repo))
482 482
483 483 rmap.connect('repo_settings_update', '/{repo_name:.*}/settings',
484 484 controller='settings', action="update",
485 485 conditions=dict(method=["PUT"], function=check_repo))
486 486
487 487 rmap.connect('repo_settings_home', '/{repo_name:.*}/settings',
488 488 controller='settings', action='index',
489 489 conditions=dict(function=check_repo))
490 490
491 491 rmap.connect('repo_fork_create_home', '/{repo_name:.*}/fork',
492 492 controller='forks', action='fork_create',
493 493 conditions=dict(function=check_repo, method=["POST"]))
494 494
495 495 rmap.connect('repo_fork_home', '/{repo_name:.*}/fork',
496 496 controller='forks', action='fork',
497 497 conditions=dict(function=check_repo))
498 498
499 499 rmap.connect('repo_forks_home', '/{repo_name:.*}/forks',
500 500 controller='forks', action='forks',
501 501 conditions=dict(function=check_repo))
502 502
503 503 rmap.connect('repo_followers_home', '/{repo_name:.*}/followers',
504 504 controller='followers', action='followers',
505 505 conditions=dict(function=check_repo))
506 506
507 507 return rmap
@@ -1,496 +1,486 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.files
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Files controller for RhodeCode
7 7
8 8 :created_on: Apr 21, 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 logging
28 28 import traceback
29 29
30 30 from pylons import request, response, tmpl_context as c, url
31 31 from pylons.i18n.translation import _
32 32 from pylons.controllers.util import redirect
33 33 from pylons.decorators import jsonify
34 34
35 35 from rhodecode.lib import diffs
36 36 from rhodecode.lib import helpers as h
37 37
38 38 from rhodecode.lib.compat import OrderedDict
39 39 from rhodecode.lib.utils2 import convert_line_endings, detect_mode, safe_str
40 40 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
41 41 from rhodecode.lib.base import BaseRepoController, render
42 42 from rhodecode.lib.utils import EmptyChangeset
43 43 from rhodecode.lib.vcs.conf import settings
44 44 from rhodecode.lib.vcs.exceptions import RepositoryError, \
45 45 ChangesetDoesNotExistError, EmptyRepositoryError, \
46 46 ImproperArchiveTypeError, VCSError, NodeAlreadyExistsError
47 47 from rhodecode.lib.vcs.nodes import FileNode
48 48
49 49 from rhodecode.model.repo import RepoModel
50 50 from rhodecode.model.scm import ScmModel
51 51
52 52 from rhodecode.controllers.changeset import anchor_url, _ignorews_url,\
53 53 _context_url, get_line_ctx, get_ignore_ws
54 54
55 55
56 56 log = logging.getLogger(__name__)
57 57
58 58
59 59 class FilesController(BaseRepoController):
60 60
61 61 @LoginRequired()
62 62 def __before__(self):
63 63 super(FilesController, self).__before__()
64 64 c.cut_off_limit = self.cut_off_limit
65 65
66 66 def __get_cs_or_redirect(self, rev, repo_name, redirect_after=True):
67 67 """
68 68 Safe way to get changeset if error occur it redirects to tip with
69 69 proper message
70 70
71 71 :param rev: revision to fetch
72 72 :param repo_name: repo name to redirect after
73 73 """
74 74
75 75 try:
76 76 return c.rhodecode_repo.get_changeset(rev)
77 77 except EmptyRepositoryError, e:
78 78 if not redirect_after:
79 79 return None
80 80 url_ = url('files_add_home',
81 81 repo_name=c.repo_name,
82 82 revision=0, f_path='')
83 83 add_new = '<a href="%s">[%s]</a>' % (url_, _('add new'))
84 84 h.flash(h.literal(_('There are no files yet %s' % add_new)),
85 85 category='warning')
86 86 redirect(h.url('summary_home', repo_name=repo_name))
87 87
88 88 except RepositoryError, e:
89 89 h.flash(str(e), category='warning')
90 90 redirect(h.url('files_home', repo_name=repo_name, revision='tip'))
91 91
92 92 def __get_filenode_or_redirect(self, repo_name, cs, path):
93 93 """
94 94 Returns file_node, if error occurs or given path is directory,
95 95 it'll redirect to top level path
96 96
97 97 :param repo_name: repo_name
98 98 :param cs: given changeset
99 99 :param path: path to lookup
100 100 """
101 101
102 102 try:
103 103 file_node = cs.get_node(path)
104 104 if file_node.is_dir():
105 105 raise RepositoryError('given path is a directory')
106 106 except RepositoryError, e:
107 107 h.flash(str(e), category='warning')
108 108 redirect(h.url('files_home', repo_name=repo_name,
109 109 revision=cs.raw_id))
110 110
111 111 return file_node
112 112
113 113 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
114 114 'repository.admin')
115 def index(self, repo_name, revision, f_path):
115 def index(self, repo_name, revision, f_path, annotate=False):
116 116 # redirect to given revision from form if given
117 117 post_revision = request.POST.get('at_rev', None)
118 118 if post_revision:
119 119 cs = self.__get_cs_or_redirect(post_revision, repo_name)
120 120 redirect(url('files_home', repo_name=c.repo_name,
121 121 revision=cs.raw_id, f_path=f_path))
122 122
123 123 c.changeset = self.__get_cs_or_redirect(revision, repo_name)
124 124 c.branch = request.GET.get('branch', None)
125 125 c.f_path = f_path
126
126 c.annotate = annotate
127 127 cur_rev = c.changeset.revision
128 128
129 129 # prev link
130 130 try:
131 131 prev_rev = c.rhodecode_repo.get_changeset(cur_rev).prev(c.branch)
132 132 c.url_prev = url('files_home', repo_name=c.repo_name,
133 133 revision=prev_rev.raw_id, f_path=f_path)
134 134 if c.branch:
135 135 c.url_prev += '?branch=%s' % c.branch
136 136 except (ChangesetDoesNotExistError, VCSError):
137 137 c.url_prev = '#'
138 138
139 139 # next link
140 140 try:
141 141 next_rev = c.rhodecode_repo.get_changeset(cur_rev).next(c.branch)
142 142 c.url_next = url('files_home', repo_name=c.repo_name,
143 143 revision=next_rev.raw_id, f_path=f_path)
144 144 if c.branch:
145 145 c.url_next += '?branch=%s' % c.branch
146 146 except (ChangesetDoesNotExistError, VCSError):
147 147 c.url_next = '#'
148 148
149 149 # files or dirs
150 150 try:
151 151 c.file = c.changeset.get_node(f_path)
152 152
153 153 if c.file.is_file():
154 154 c.file_history = self._get_node_history(c.changeset, f_path)
155 155 else:
156 156 c.file_history = []
157 157 except RepositoryError, e:
158 158 h.flash(str(e), category='warning')
159 159 redirect(h.url('files_home', repo_name=repo_name,
160 160 revision=revision))
161 161
162 162 return render('files/files.html')
163 163
164 164 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
165 165 'repository.admin')
166 166 def rawfile(self, repo_name, revision, f_path):
167 167 cs = self.__get_cs_or_redirect(revision, repo_name)
168 168 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
169 169
170 170 response.content_disposition = 'attachment; filename=%s' % \
171 171 safe_str(f_path.split(os.sep)[-1])
172 172
173 173 response.content_type = file_node.mimetype
174 174 return file_node.content
175 175
176 176 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
177 177 'repository.admin')
178 178 def raw(self, repo_name, revision, f_path):
179 179 cs = self.__get_cs_or_redirect(revision, repo_name)
180 180 file_node = self.__get_filenode_or_redirect(repo_name, cs, f_path)
181 181
182 182 raw_mimetype_mapping = {
183 183 # map original mimetype to a mimetype used for "show as raw"
184 184 # you can also provide a content-disposition to override the
185 185 # default "attachment" disposition.
186 186 # orig_type: (new_type, new_dispo)
187 187
188 188 # show images inline:
189 189 'image/x-icon': ('image/x-icon', 'inline'),
190 190 'image/png': ('image/png', 'inline'),
191 191 'image/gif': ('image/gif', 'inline'),
192 192 'image/jpeg': ('image/jpeg', 'inline'),
193 193 'image/svg+xml': ('image/svg+xml', 'inline'),
194 194 }
195 195
196 196 mimetype = file_node.mimetype
197 197 try:
198 198 mimetype, dispo = raw_mimetype_mapping[mimetype]
199 199 except KeyError:
200 200 # we don't know anything special about this, handle it safely
201 201 if file_node.is_binary:
202 202 # do same as download raw for binary files
203 203 mimetype, dispo = 'application/octet-stream', 'attachment'
204 204 else:
205 205 # do not just use the original mimetype, but force text/plain,
206 206 # otherwise it would serve text/html and that might be unsafe.
207 207 # Note: underlying vcs library fakes text/plain mimetype if the
208 208 # mimetype can not be determined and it thinks it is not
209 209 # binary.This might lead to erroneous text display in some
210 210 # cases, but helps in other cases, like with text files
211 211 # without extension.
212 212 mimetype, dispo = 'text/plain', 'inline'
213 213
214 214 if dispo == 'attachment':
215 215 dispo = 'attachment; filename=%s' % \
216 216 safe_str(f_path.split(os.sep)[-1])
217 217
218 218 response.content_disposition = dispo
219 219 response.content_type = mimetype
220 220 return file_node.content
221 221
222 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
223 'repository.admin')
224 def annotate(self, repo_name, revision, f_path):
225 c.cs = self.__get_cs_or_redirect(revision, repo_name)
226 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
227
228 c.file_history = self._get_node_history(c.cs, f_path)
229 c.f_path = f_path
230 return render('files/files_annotate.html')
231
232 222 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
233 223 def edit(self, repo_name, revision, f_path):
234 224 r_post = request.POST
235 225
236 226 c.cs = self.__get_cs_or_redirect(revision, repo_name)
237 227 c.file = self.__get_filenode_or_redirect(repo_name, c.cs, f_path)
238 228
239 229 if c.file.is_binary:
240 230 return redirect(url('files_home', repo_name=c.repo_name,
241 231 revision=c.cs.raw_id, f_path=f_path))
242 232
243 233 c.f_path = f_path
244 234
245 235 if r_post:
246 236
247 237 old_content = c.file.content
248 238 sl = old_content.splitlines(1)
249 239 first_line = sl[0] if sl else ''
250 240 # modes: 0 - Unix, 1 - Mac, 2 - DOS
251 241 mode = detect_mode(first_line, 0)
252 242 content = convert_line_endings(r_post.get('content'), mode)
253 243
254 244 message = r_post.get('message') or (_('Edited %s via RhodeCode')
255 245 % (f_path))
256 246 author = self.rhodecode_user.full_contact
257 247
258 248 if content == old_content:
259 249 h.flash(_('No changes'),
260 250 category='warning')
261 251 return redirect(url('changeset_home', repo_name=c.repo_name,
262 252 revision='tip'))
263 253
264 254 try:
265 255 self.scm_model.commit_change(repo=c.rhodecode_repo,
266 256 repo_name=repo_name, cs=c.cs,
267 257 user=self.rhodecode_user,
268 258 author=author, message=message,
269 259 content=content, f_path=f_path)
270 260 h.flash(_('Successfully committed to %s' % f_path),
271 261 category='success')
272 262
273 263 except Exception:
274 264 log.error(traceback.format_exc())
275 265 h.flash(_('Error occurred during commit'), category='error')
276 266 return redirect(url('changeset_home',
277 267 repo_name=c.repo_name, revision='tip'))
278 268
279 269 return render('files/files_edit.html')
280 270
281 271 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
282 272 def add(self, repo_name, revision, f_path):
283 273 r_post = request.POST
284 274 c.cs = self.__get_cs_or_redirect(revision, repo_name,
285 275 redirect_after=False)
286 276 if c.cs is None:
287 277 c.cs = EmptyChangeset(alias=c.rhodecode_repo.alias)
288 278
289 279 c.f_path = f_path
290 280
291 281 if r_post:
292 282 unix_mode = 0
293 283 content = convert_line_endings(r_post.get('content'), unix_mode)
294 284
295 285 message = r_post.get('message') or (_('Added %s via RhodeCode')
296 286 % (f_path))
297 287 location = r_post.get('location')
298 288 filename = r_post.get('filename')
299 289 file_obj = r_post.get('upload_file', None)
300 290
301 291 if file_obj is not None and hasattr(file_obj, 'filename'):
302 292 filename = file_obj.filename
303 293 content = file_obj.file
304 294
305 295 node_path = os.path.join(location, filename)
306 296 author = self.rhodecode_user.full_contact
307 297
308 298 if not content:
309 299 h.flash(_('No content'), category='warning')
310 300 return redirect(url('changeset_home', repo_name=c.repo_name,
311 301 revision='tip'))
312 302 if not filename:
313 303 h.flash(_('No filename'), category='warning')
314 304 return redirect(url('changeset_home', repo_name=c.repo_name,
315 305 revision='tip'))
316 306
317 307 try:
318 308 self.scm_model.create_node(repo=c.rhodecode_repo,
319 309 repo_name=repo_name, cs=c.cs,
320 310 user=self.rhodecode_user,
321 311 author=author, message=message,
322 312 content=content, f_path=node_path)
323 313 h.flash(_('Successfully committed to %s' % node_path),
324 314 category='success')
325 315 except NodeAlreadyExistsError, e:
326 316 h.flash(_(e), category='error')
327 317 except Exception:
328 318 log.error(traceback.format_exc())
329 319 h.flash(_('Error occurred during commit'), category='error')
330 320 return redirect(url('changeset_home',
331 321 repo_name=c.repo_name, revision='tip'))
332 322
333 323 return render('files/files_add.html')
334 324
335 325 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
336 326 'repository.admin')
337 327 def archivefile(self, repo_name, fname):
338 328
339 329 fileformat = None
340 330 revision = None
341 331 ext = None
342 332 subrepos = request.GET.get('subrepos') == 'true'
343 333
344 334 for a_type, ext_data in settings.ARCHIVE_SPECS.items():
345 335 archive_spec = fname.split(ext_data[1])
346 336 if len(archive_spec) == 2 and archive_spec[1] == '':
347 337 fileformat = a_type or ext_data[1]
348 338 revision = archive_spec[0]
349 339 ext = ext_data[1]
350 340
351 341 try:
352 342 dbrepo = RepoModel().get_by_repo_name(repo_name)
353 343 if dbrepo.enable_downloads is False:
354 344 return _('downloads disabled')
355 345
356 346 if c.rhodecode_repo.alias == 'hg':
357 347 # patch and reset hooks section of UI config to not run any
358 348 # hooks on fetching archives with subrepos
359 349 for k, v in c.rhodecode_repo._repo.ui.configitems('hooks'):
360 350 c.rhodecode_repo._repo.ui.setconfig('hooks', k, None)
361 351
362 352 cs = c.rhodecode_repo.get_changeset(revision)
363 353 content_type = settings.ARCHIVE_SPECS[fileformat][0]
364 354 except ChangesetDoesNotExistError:
365 355 return _('Unknown revision %s') % revision
366 356 except EmptyRepositoryError:
367 357 return _('Empty repository')
368 358 except (ImproperArchiveTypeError, KeyError):
369 359 return _('Unknown archive type')
370 360
371 361 response.content_type = content_type
372 362 response.content_disposition = 'attachment; filename=%s-%s%s' \
373 363 % (repo_name, revision, ext)
374 364
375 365 import tempfile
376 366 archive = tempfile.mkstemp()[1]
377 367 t = open(archive, 'wb')
378 368 cs.fill_archive(stream=t, kind=fileformat, subrepos=subrepos)
379 369
380 370 def get_chunked_archive(archive):
381 371 stream = open(archive, 'rb')
382 372 while True:
383 373 data = stream.read(4096)
384 374 if not data:
385 375 os.remove(archive)
386 376 break
387 377 yield data
388 378
389 379 return get_chunked_archive(archive)
390 380
391 381 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
392 382 'repository.admin')
393 383 def diff(self, repo_name, f_path):
394 384 ignore_whitespace = request.GET.get('ignorews') == '1'
395 385 line_context = request.GET.get('context', 3)
396 386 diff1 = request.GET.get('diff1', '')
397 387 diff2 = request.GET.get('diff2', '')
398 388 c.action = request.GET.get('diff')
399 389 c.no_changes = diff1 == diff2
400 390 c.f_path = f_path
401 391 c.big_diff = False
402 392 c.anchor_url = anchor_url
403 393 c.ignorews_url = _ignorews_url
404 394 c.context_url = _context_url
405 395 c.changes = OrderedDict()
406 396 c.changes[diff2] = []
407 397 try:
408 398 if diff1 not in ['', None, 'None', '0' * 12, '0' * 40]:
409 399 c.changeset_1 = c.rhodecode_repo.get_changeset(diff1)
410 400 node1 = c.changeset_1.get_node(f_path)
411 401 else:
412 402 c.changeset_1 = EmptyChangeset(repo=c.rhodecode_repo)
413 403 node1 = FileNode('.', '', changeset=c.changeset_1)
414 404
415 405 if diff2 not in ['', None, 'None', '0' * 12, '0' * 40]:
416 406 c.changeset_2 = c.rhodecode_repo.get_changeset(diff2)
417 407 node2 = c.changeset_2.get_node(f_path)
418 408 else:
419 409 c.changeset_2 = EmptyChangeset(repo=c.rhodecode_repo)
420 410 node2 = FileNode('.', '', changeset=c.changeset_2)
421 411 except RepositoryError:
422 412 return redirect(url('files_home', repo_name=c.repo_name,
423 413 f_path=f_path))
424 414
425 415 if c.action == 'download':
426 416 _diff = diffs.get_gitdiff(node1, node2,
427 417 ignore_whitespace=ignore_whitespace,
428 418 context=line_context)
429 419 diff = diffs.DiffProcessor(_diff, format='gitdiff')
430 420
431 421 diff_name = '%s_vs_%s.diff' % (diff1, diff2)
432 422 response.content_type = 'text/plain'
433 423 response.content_disposition = (
434 424 'attachment; filename=%s' % diff_name
435 425 )
436 426 return diff.raw_diff()
437 427
438 428 elif c.action == 'raw':
439 429 _diff = diffs.get_gitdiff(node1, node2,
440 430 ignore_whitespace=ignore_whitespace,
441 431 context=line_context)
442 432 diff = diffs.DiffProcessor(_diff, format='gitdiff')
443 433 response.content_type = 'text/plain'
444 434 return diff.raw_diff()
445 435
446 436 else:
447 437 fid = h.FID(diff2, node2.path)
448 438 line_context_lcl = get_line_ctx(fid, request.GET)
449 439 ign_whitespace_lcl = get_ignore_ws(fid, request.GET)
450 440
451 441 lim = request.GET.get('fulldiff') or self.cut_off_limit
452 442 _, cs1, cs2, diff, st = diffs.wrapped_diff(filenode_old=node1,
453 443 filenode_new=node2,
454 444 cut_off_limit=lim,
455 445 ignore_whitespace=ign_whitespace_lcl,
456 446 line_context=line_context_lcl,
457 447 enable_comments=False)
458 448
459 449 c.changes = [('', node2, diff, cs1, cs2, st,)]
460 450
461 451 return render('files/file_diff.html')
462 452
463 453 def _get_node_history(self, cs, f_path):
464 454 changesets = cs.get_file_history(f_path)
465 455 hist_l = []
466 456
467 457 changesets_group = ([], _("Changesets"))
468 458 branches_group = ([], _("Branches"))
469 459 tags_group = ([], _("Tags"))
470 460 _hg = cs.repository.alias == 'hg'
471 461 for chs in changesets:
472 462 _branch = '(%s)' % chs.branch if _hg else ''
473 463 n_desc = 'r%s:%s %s' % (chs.revision, chs.short_id, _branch)
474 464 changesets_group[0].append((chs.raw_id, n_desc,))
475 465
476 466 hist_l.append(changesets_group)
477 467
478 468 for name, chs in c.rhodecode_repo.branches.items():
479 469 branches_group[0].append((chs, name),)
480 470 hist_l.append(branches_group)
481 471
482 472 for name, chs in c.rhodecode_repo.tags.items():
483 473 tags_group[0].append((chs, name),)
484 474 hist_l.append(tags_group)
485 475
486 476 return hist_l
487 477
488 478 @jsonify
489 479 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
490 480 'repository.admin')
491 481 def nodelist(self, repo_name, revision, f_path):
492 482 if request.environ.get('HTTP_X_PARTIAL_XHR'):
493 483 cs = self.__get_cs_or_redirect(revision, repo_name)
494 484 _d, _f = ScmModel().get_nodes(repo_name, cs.raw_id, f_path,
495 485 flat=False)
496 486 return _d + _f
@@ -1,104 +1,114 b''
1 1 <dl>
2 2 <dt style="padding-top:10px;font-size:16px">${_('History')}</dt>
3 3 <dd>
4 4 <div>
5 5 ${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')}
6 6 ${h.hidden('diff2',c.file.changeset.raw_id)}
7 7 ${h.select('diff1',c.file.changeset.raw_id,c.file_history)}
8 8 ${h.submit('diff','diff to revision',class_="ui-btn")}
9 9 ${h.submit('show_rev','show at revision',class_="ui-btn")}
10 10 ${h.end_form()}
11 11 </div>
12 12 </dd>
13 13 </dl>
14 14
15 15 <div id="body" class="codeblock">
16 16 <div class="code-header">
17 17 <div class="stats">
18 18 <div class="left img"><img src="${h.url('/images/icons/file.png')}"/></div>
19 19 <div class="left item"><pre>${h.link_to("r%s:%s" % (c.file.changeset.revision,h.short_id(c.file.changeset.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=c.file.changeset.raw_id))}</pre></div>
20 20 <div class="left item"><pre>${h.format_byte_size(c.file.size,binary=True)}</pre></div>
21 21 <div class="left item last"><pre>${c.file.mimetype}</pre></div>
22 22 <div class="buttons">
23 ${h.link_to(_('show annotation'),h.url('files_annotate_home',repo_name=c.repo_name,revision=c.file.changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
23 %if c.annotate:
24 ${h.link_to(_('show source'), h.url('files_home', repo_name=c.repo_name,revision=c.file.changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
25 %else:
26 ${h.link_to(_('show annotation'),h.url('files_annotate_home',repo_name=c.repo_name,revision=c.file.changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
27 %endif
24 28 ${h.link_to(_('show as raw'),h.url('files_raw_home',repo_name=c.repo_name,revision=c.file.changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
25 29 ${h.link_to(_('download as raw'),h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.file.changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
26 30 % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
27 31 % if not c.file.is_binary:
28 32 ${h.link_to(_('edit'),h.url('files_edit_home',repo_name=c.repo_name,revision=c.file.changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
29 33 % endif
30 34 % endif
31 35 </div>
32 36 </div>
33 37 <div class="author">
34 38 <div class="gravatar">
35 39 <img alt="gravatar" src="${h.gravatar_url(h.email(c.file.changeset.author),16)}"/>
36 40 </div>
37 41 <div title="${c.file.changeset.author}" class="user">${h.person(c.file.changeset.author)}</div>
38 42 </div>
39 43 <div class="commit">${h.urlify_commit(c.file.changeset.message,c.repo_name)}</div>
40 44 </div>
41 45 <div class="code-body">
42 46 %if c.file.is_binary:
43 47 ${_('Binary file (%s)') % c.file.mimetype}
44 48 %else:
45 49 % if c.file.size < c.cut_off_limit:
46 ${h.pygmentize(c.file,linenos=True,anchorlinenos=True,lineanchors='L',cssclass="code-highlight")}
50 %if c.annotate:
51 ${h.pygmentize_annotation(c.repo_name,c.file,linenos=True,anchorlinenos=True,lineanchors='L',cssclass="code-highlight")}
52 %else:
53 ${h.pygmentize(c.file,linenos=True,anchorlinenos=True,lineanchors='L',cssclass="code-highlight")}
54 %endif
47 55 %else:
48 56 ${_('File is too big to display')} ${h.link_to(_('show as raw'),
49 57 h.url('files_raw_home',repo_name=c.repo_name,revision=c.file.changeset.raw_id,f_path=c.f_path))}
50 58 %endif
51 <script type="text/javascript">
52 function highlight_lines(lines){
53 for(pos in lines){
54 YUD.setStyle('L'+lines[pos],'background-color','#FFFFBE');
55 }
56 }
57 page_highlights = location.href.substring(location.href.indexOf('#')+1).split('L');
58 if (page_highlights.length == 2){
59 highlight_ranges = page_highlights[1].split(",");
60
61 var h_lines = [];
62 for (pos in highlight_ranges){
63 var _range = highlight_ranges[pos].split('-');
64 if(_range.length == 2){
65 var start = parseInt(_range[0]);
66 var end = parseInt(_range[1]);
67 if (start < end){
68 for(var i=start;i<=end;i++){
69 h_lines.push(i);
70 }
71 }
72 }
73 else{
74 h_lines.push(parseInt(highlight_ranges[pos]));
75 }
76 }
77 highlight_lines(h_lines);
78
79 //remember original location
80 var old_hash = location.href.substring(location.href.indexOf('#'));
81
82 // this makes a jump to anchor moved by 3 posstions for padding
83 window.location.hash = '#L'+Math.max(parseInt(h_lines[0])-3,1);
84
85 //sets old anchor
86 window.location.hash = old_hash;
87
88 }
89 </script>
90 59 %endif
91 60 </div>
92 61 </div>
93 62
94 63 <script type="text/javascript">
95 64 YUE.onDOMReady(function(){
65 function highlight_lines(lines){
66 for(pos in lines){
67 YUD.setStyle('L'+lines[pos],'background-color','#FFFFBE');
68 }
69 }
70 page_highlights = location.href.substring(location.href.indexOf('#')+1).split('L');
71 if (page_highlights.length == 2){
72 highlight_ranges = page_highlights[1].split(",");
73
74 var h_lines = [];
75 for (pos in highlight_ranges){
76 var _range = highlight_ranges[pos].split('-');
77 if(_range.length == 2){
78 var start = parseInt(_range[0]);
79 var end = parseInt(_range[1]);
80 if (start < end){
81 for(var i=start;i<=end;i++){
82 h_lines.push(i);
83 }
84 }
85 }
86 else{
87 h_lines.push(parseInt(highlight_ranges[pos]));
88 }
89 }
90 highlight_lines(h_lines);
91
92 //remember original location
93 var old_hash = location.href.substring(location.href.indexOf('#'));
94
95 // this makes a jump to anchor moved by 3 posstions for padding
96 window.location.hash = '#L'+Math.max(parseInt(h_lines[0])-3,1);
97
98 //sets old anchor
99 window.location.hash = old_hash;
100
101 }
96 102 YUE.on('show_rev','click',function(e){
97 103 YUE.preventDefault(e);
98 104 var cs = YUD.get('diff1').value;
99 var url = "${h.url('files_home',repo_name=c.repo_name,revision='__CS__',f_path=c.f_path)}".replace('__CS__',cs);
105 %if c.annotate:
106 var url = "${h.url('files_annotate_home',repo_name=c.repo_name,revision='__CS__',f_path=c.f_path)}".replace('__CS__',cs);
107 %else:
108 var url = "${h.url('files_home',repo_name=c.repo_name,revision='__CS__',f_path=c.f_path)}".replace('__CS__',cs);
109 %endif
100 110 window.location = url;
101 111 });
102 112 YUE.on('hlcode','mouseup',getSelectionLink("${_('Selection link')}"))
103 113 });
104 114 </script>
@@ -1,15 +1,18 b''
1 1 %if c.file:
2 2 <h3 class="files_location">
3 3 ${_('Location')}: ${h.files_breadcrumbs(c.repo_name,c.changeset.raw_id,c.file.path)}
4 %if c.annotate:
5 - ${_('annotation')}
6 %endif
4 7 </h3>
5 8 %if c.file.is_dir():
6 9 <%include file='files_browser.html'/>
7 10 %else:
8 11 <%include file='files_source.html'/>
9 12 %endif
10 13 %else:
11 14 <h2>
12 15 <a href="#" onClick="javascript:parent.history.back();" target="main">${_('Go back')}</a>
13 16 ${_('No files at given path')}: "${c.f_path or "/"}"
14 17 </h2>
15 18 %endif
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now