##// END OF EJS Templates
implements #239 manual marking of repos as forks for admins
marcink -
r1755:1088ded6 beta
parent child Browse files
Show More
@@ -1,487 +1,489
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
12 12 # prefix for non repository related links needs to be prefixed with `/`
13 13 ADMIN_PREFIX = '/_admin'
14 14
15 15
16 16 def make_map(config):
17 17 """Create, configure and return the routes Mapper"""
18 18 rmap = Mapper(directory=config['pylons.paths']['controllers'],
19 19 always_scan=config['debug'])
20 20 rmap.minimization = False
21 21 rmap.explicit = False
22 22
23 23 from rhodecode.lib.utils import is_valid_repo
24 24 from rhodecode.lib.utils import is_valid_repos_group
25 25
26 26 def check_repo(environ, match_dict):
27 27 """
28 28 check for valid repository for proper 404 handling
29 29
30 30 :param environ:
31 31 :param match_dict:
32 32 """
33 33
34 34 repo_name = match_dict.get('repo_name')
35 35 return is_valid_repo(repo_name, config['base_path'])
36 36
37 37 def check_group(environ, match_dict):
38 38 """
39 39 check for valid repositories group for proper 404 handling
40 40
41 41 :param environ:
42 42 :param match_dict:
43 43 """
44 44 repos_group_name = match_dict.get('group_name')
45 45
46 46 return is_valid_repos_group(repos_group_name, config['base_path'])
47 47
48 48
49 49 def check_int(environ, match_dict):
50 50 return match_dict.get('id').isdigit()
51 51
52 52 # The ErrorController route (handles 404/500 error pages); it should
53 53 # likely stay at the top, ensuring it can always be resolved
54 54 rmap.connect('/error/{action}', controller='error')
55 55 rmap.connect('/error/{action}/{id}', controller='error')
56 56
57 57 #==========================================================================
58 58 # CUSTOM ROUTES HERE
59 59 #==========================================================================
60 60
61 61 #MAIN PAGE
62 62 rmap.connect('home', '/', controller='home', action='index')
63 63 rmap.connect('repo_switcher', '/repos', controller='home',
64 64 action='repo_switcher')
65 65 rmap.connect('branch_tag_switcher', '/branches-tags/{repo_name:.*}',
66 66 controller='home', action='branch_tag_switcher')
67 67 rmap.connect('bugtracker',
68 68 "http://bitbucket.org/marcinkuzminski/rhodecode/issues",
69 69 _static=True)
70 70 rmap.connect('rst_help',
71 71 "http://docutils.sourceforge.net/docs/user/rst/quickref.html",
72 72 _static=True)
73 73 rmap.connect('rhodecode_official', "http://rhodecode.org", _static=True)
74 74
75 75 #ADMIN REPOSITORY REST ROUTES
76 76 with rmap.submapper(path_prefix=ADMIN_PREFIX,
77 77 controller='admin/repos') as m:
78 78 m.connect("repos", "/repos",
79 79 action="create", conditions=dict(method=["POST"]))
80 80 m.connect("repos", "/repos",
81 81 action="index", conditions=dict(method=["GET"]))
82 82 m.connect("formatted_repos", "/repos.{format}",
83 83 action="index",
84 84 conditions=dict(method=["GET"]))
85 85 m.connect("new_repo", "/repos/new",
86 86 action="new", conditions=dict(method=["GET"]))
87 87 m.connect("formatted_new_repo", "/repos/new.{format}",
88 88 action="new", conditions=dict(method=["GET"]))
89 89 m.connect("/repos/{repo_name:.*}",
90 90 action="update", conditions=dict(method=["PUT"],
91 91 function=check_repo))
92 92 m.connect("/repos/{repo_name:.*}",
93 93 action="delete", conditions=dict(method=["DELETE"],
94 94 function=check_repo))
95 95 m.connect("edit_repo", "/repos/{repo_name:.*}/edit",
96 96 action="edit", conditions=dict(method=["GET"],
97 97 function=check_repo))
98 98 m.connect("formatted_edit_repo", "/repos/{repo_name:.*}.{format}/edit",
99 99 action="edit", conditions=dict(method=["GET"],
100 100 function=check_repo))
101 101 m.connect("repo", "/repos/{repo_name:.*}",
102 102 action="show", conditions=dict(method=["GET"],
103 103 function=check_repo))
104 104 m.connect("formatted_repo", "/repos/{repo_name:.*}.{format}",
105 105 action="show", conditions=dict(method=["GET"],
106 106 function=check_repo))
107 107 #ajax delete repo perm user
108 108 m.connect('delete_repo_user', "/repos_delete_user/{repo_name:.*}",
109 109 action="delete_perm_user", conditions=dict(method=["DELETE"],
110 110 function=check_repo))
111 111 #ajax delete repo perm users_group
112 112 m.connect('delete_repo_users_group',
113 113 "/repos_delete_users_group/{repo_name:.*}",
114 114 action="delete_perm_users_group",
115 115 conditions=dict(method=["DELETE"], function=check_repo))
116 116
117 117 #settings actions
118 118 m.connect('repo_stats', "/repos_stats/{repo_name:.*}",
119 action="repo_stats", conditions=dict(method=["DELETE"],
120 function=check_repo))
119 action="repo_stats", conditions=dict(method=["DELETE"],
120 function=check_repo))
121 121 m.connect('repo_cache', "/repos_cache/{repo_name:.*}",
122 action="repo_cache", conditions=dict(method=["DELETE"],
122 action="repo_cache", conditions=dict(method=["DELETE"],
123 function=check_repo))
124 m.connect('repo_public_journal',"/repos_public_journal/{repo_name:.*}",
125 action="repo_public_journal", conditions=dict(method=["PUT"],
123 126 function=check_repo))
124 m.connect('repo_public_journal',
125 "/repos_public_journal/{repo_name:.*}",
126 action="repo_public_journal", conditions=dict(method=["PUT"],
127 function=check_repo))
128 127 m.connect('repo_pull', "/repo_pull/{repo_name:.*}",
129 action="repo_pull", conditions=dict(method=["PUT"],
130 function=check_repo))
131
128 action="repo_pull", conditions=dict(method=["PUT"],
129 function=check_repo))
130 m.connect('repo_as_fork', "/repo_as_fork/{repo_name:.*}",
131 action="repo_as_fork", conditions=dict(method=["PUT"],
132 function=check_repo))
133
132 134 with rmap.submapper(path_prefix=ADMIN_PREFIX,
133 135 controller='admin/repos_groups') as m:
134 136 m.connect("repos_groups", "/repos_groups",
135 137 action="create", conditions=dict(method=["POST"]))
136 138 m.connect("repos_groups", "/repos_groups",
137 139 action="index", conditions=dict(method=["GET"]))
138 140 m.connect("formatted_repos_groups", "/repos_groups.{format}",
139 141 action="index", conditions=dict(method=["GET"]))
140 142 m.connect("new_repos_group", "/repos_groups/new",
141 143 action="new", conditions=dict(method=["GET"]))
142 144 m.connect("formatted_new_repos_group", "/repos_groups/new.{format}",
143 145 action="new", conditions=dict(method=["GET"]))
144 146 m.connect("update_repos_group", "/repos_groups/{id}",
145 147 action="update", conditions=dict(method=["PUT"],
146 148 function=check_int))
147 149 m.connect("delete_repos_group", "/repos_groups/{id}",
148 150 action="delete", conditions=dict(method=["DELETE"],
149 151 function=check_int))
150 152 m.connect("edit_repos_group", "/repos_groups/{id}/edit",
151 153 action="edit", conditions=dict(method=["GET"],
152 154 function=check_int))
153 155 m.connect("formatted_edit_repos_group",
154 156 "/repos_groups/{id}.{format}/edit",
155 157 action="edit", conditions=dict(method=["GET"],
156 158 function=check_int))
157 159 m.connect("repos_group", "/repos_groups/{id}",
158 160 action="show", conditions=dict(method=["GET"],
159 161 function=check_int))
160 162 m.connect("formatted_repos_group", "/repos_groups/{id}.{format}",
161 163 action="show", conditions=dict(method=["GET"],
162 164 function=check_int))
163 165
164 166 #ADMIN USER REST ROUTES
165 167 with rmap.submapper(path_prefix=ADMIN_PREFIX,
166 168 controller='admin/users') as m:
167 169 m.connect("users", "/users",
168 170 action="create", conditions=dict(method=["POST"]))
169 171 m.connect("users", "/users",
170 172 action="index", conditions=dict(method=["GET"]))
171 173 m.connect("formatted_users", "/users.{format}",
172 174 action="index", conditions=dict(method=["GET"]))
173 175 m.connect("new_user", "/users/new",
174 176 action="new", conditions=dict(method=["GET"]))
175 177 m.connect("formatted_new_user", "/users/new.{format}",
176 178 action="new", conditions=dict(method=["GET"]))
177 179 m.connect("update_user", "/users/{id}",
178 180 action="update", conditions=dict(method=["PUT"]))
179 181 m.connect("delete_user", "/users/{id}",
180 182 action="delete", conditions=dict(method=["DELETE"]))
181 183 m.connect("edit_user", "/users/{id}/edit",
182 184 action="edit", conditions=dict(method=["GET"]))
183 185 m.connect("formatted_edit_user",
184 186 "/users/{id}.{format}/edit",
185 187 action="edit", conditions=dict(method=["GET"]))
186 188 m.connect("user", "/users/{id}",
187 189 action="show", conditions=dict(method=["GET"]))
188 190 m.connect("formatted_user", "/users/{id}.{format}",
189 191 action="show", conditions=dict(method=["GET"]))
190 192
191 193 #EXTRAS USER ROUTES
192 194 m.connect("user_perm", "/users_perm/{id}",
193 195 action="update_perm", conditions=dict(method=["PUT"]))
194 196
195 197 #ADMIN USERS REST ROUTES
196 198 with rmap.submapper(path_prefix=ADMIN_PREFIX,
197 199 controller='admin/users_groups') as m:
198 200 m.connect("users_groups", "/users_groups",
199 201 action="create", conditions=dict(method=["POST"]))
200 202 m.connect("users_groups", "/users_groups",
201 203 action="index", conditions=dict(method=["GET"]))
202 204 m.connect("formatted_users_groups", "/users_groups.{format}",
203 205 action="index", conditions=dict(method=["GET"]))
204 206 m.connect("new_users_group", "/users_groups/new",
205 207 action="new", conditions=dict(method=["GET"]))
206 208 m.connect("formatted_new_users_group", "/users_groups/new.{format}",
207 209 action="new", conditions=dict(method=["GET"]))
208 210 m.connect("update_users_group", "/users_groups/{id}",
209 211 action="update", conditions=dict(method=["PUT"]))
210 212 m.connect("delete_users_group", "/users_groups/{id}",
211 213 action="delete", conditions=dict(method=["DELETE"]))
212 214 m.connect("edit_users_group", "/users_groups/{id}/edit",
213 215 action="edit", conditions=dict(method=["GET"]))
214 216 m.connect("formatted_edit_users_group",
215 217 "/users_groups/{id}.{format}/edit",
216 218 action="edit", conditions=dict(method=["GET"]))
217 219 m.connect("users_group", "/users_groups/{id}",
218 220 action="show", conditions=dict(method=["GET"]))
219 221 m.connect("formatted_users_group", "/users_groups/{id}.{format}",
220 222 action="show", conditions=dict(method=["GET"]))
221 223
222 224 #EXTRAS USER ROUTES
223 225 m.connect("users_group_perm", "/users_groups_perm/{id}",
224 226 action="update_perm", conditions=dict(method=["PUT"]))
225 227
226 228 #ADMIN GROUP REST ROUTES
227 229 rmap.resource('group', 'groups',
228 230 controller='admin/groups', path_prefix=ADMIN_PREFIX)
229 231
230 232 #ADMIN PERMISSIONS REST ROUTES
231 233 rmap.resource('permission', 'permissions',
232 234 controller='admin/permissions', path_prefix=ADMIN_PREFIX)
233 235
234 236 ##ADMIN LDAP SETTINGS
235 237 rmap.connect('ldap_settings', '%s/ldap' % ADMIN_PREFIX,
236 238 controller='admin/ldap_settings', action='ldap_settings',
237 239 conditions=dict(method=["POST"]))
238 240
239 241 rmap.connect('ldap_home', '%s/ldap' % ADMIN_PREFIX,
240 242 controller='admin/ldap_settings')
241 243
242 244 #ADMIN SETTINGS REST ROUTES
243 245 with rmap.submapper(path_prefix=ADMIN_PREFIX,
244 246 controller='admin/settings') as m:
245 247 m.connect("admin_settings", "/settings",
246 248 action="create", conditions=dict(method=["POST"]))
247 249 m.connect("admin_settings", "/settings",
248 250 action="index", conditions=dict(method=["GET"]))
249 251 m.connect("formatted_admin_settings", "/settings.{format}",
250 252 action="index", conditions=dict(method=["GET"]))
251 253 m.connect("admin_new_setting", "/settings/new",
252 254 action="new", conditions=dict(method=["GET"]))
253 255 m.connect("formatted_admin_new_setting", "/settings/new.{format}",
254 256 action="new", conditions=dict(method=["GET"]))
255 257 m.connect("/settings/{setting_id}",
256 258 action="update", conditions=dict(method=["PUT"]))
257 259 m.connect("/settings/{setting_id}",
258 260 action="delete", conditions=dict(method=["DELETE"]))
259 261 m.connect("admin_edit_setting", "/settings/{setting_id}/edit",
260 262 action="edit", conditions=dict(method=["GET"]))
261 263 m.connect("formatted_admin_edit_setting",
262 264 "/settings/{setting_id}.{format}/edit",
263 265 action="edit", conditions=dict(method=["GET"]))
264 266 m.connect("admin_setting", "/settings/{setting_id}",
265 267 action="show", conditions=dict(method=["GET"]))
266 268 m.connect("formatted_admin_setting", "/settings/{setting_id}.{format}",
267 269 action="show", conditions=dict(method=["GET"]))
268 270 m.connect("admin_settings_my_account", "/my_account",
269 271 action="my_account", conditions=dict(method=["GET"]))
270 272 m.connect("admin_settings_my_account_update", "/my_account_update",
271 273 action="my_account_update", conditions=dict(method=["PUT"]))
272 274 m.connect("admin_settings_create_repository", "/create_repository",
273 275 action="create_repository", conditions=dict(method=["GET"]))
274 276
275 277
276 278 #NOTIFICATION REST ROUTES
277 279 with rmap.submapper(path_prefix=ADMIN_PREFIX,
278 280 controller='admin/notifications') as m:
279 281 m.connect("notifications", "/notifications",
280 282 action="create", conditions=dict(method=["POST"]))
281 283 m.connect("notifications", "/notifications",
282 284 action="index", conditions=dict(method=["GET"]))
283 285 m.connect("formatted_notifications", "/notifications.{format}",
284 286 action="index", conditions=dict(method=["GET"]))
285 287 m.connect("new_notification", "/notifications/new",
286 288 action="new", conditions=dict(method=["GET"]))
287 289 m.connect("formatted_new_notification", "/notifications/new.{format}",
288 290 action="new", conditions=dict(method=["GET"]))
289 291 m.connect("/notification/{notification_id}",
290 292 action="update", conditions=dict(method=["PUT"]))
291 293 m.connect("/notification/{notification_id}",
292 294 action="delete", conditions=dict(method=["DELETE"]))
293 295 m.connect("edit_notification", "/notification/{notification_id}/edit",
294 296 action="edit", conditions=dict(method=["GET"]))
295 297 m.connect("formatted_edit_notification",
296 298 "/notification/{notification_id}.{format}/edit",
297 299 action="edit", conditions=dict(method=["GET"]))
298 300 m.connect("notification", "/notification/{notification_id}",
299 301 action="show", conditions=dict(method=["GET"]))
300 302 m.connect("formatted_notification", "/notifications/{notification_id}.{format}",
301 303 action="show", conditions=dict(method=["GET"]))
302 304
303 305
304 306
305 307 #ADMIN MAIN PAGES
306 308 with rmap.submapper(path_prefix=ADMIN_PREFIX,
307 309 controller='admin/admin') as m:
308 310 m.connect('admin_home', '', action='index')
309 311 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
310 312 action='add_repo')
311 313
312 314 #==========================================================================
313 315 # API V1
314 316 #==========================================================================
315 317 with rmap.submapper(path_prefix=ADMIN_PREFIX,
316 318 controller='api/api') as m:
317 319 m.connect('api', '/api')
318 320
319 321
320 322 #USER JOURNAL
321 323 rmap.connect('journal', '%s/journal' % ADMIN_PREFIX, controller='journal')
322 324
323 325 rmap.connect('public_journal', '%s/public_journal' % ADMIN_PREFIX,
324 326 controller='journal', action="public_journal")
325 327
326 328 rmap.connect('public_journal_rss', '%s/public_journal_rss' % ADMIN_PREFIX,
327 329 controller='journal', action="public_journal_rss")
328 330
329 331 rmap.connect('public_journal_atom',
330 332 '%s/public_journal_atom' % ADMIN_PREFIX, controller='journal',
331 333 action="public_journal_atom")
332 334
333 335 rmap.connect('toggle_following', '%s/toggle_following' % ADMIN_PREFIX,
334 336 controller='journal', action='toggle_following',
335 337 conditions=dict(method=["POST"]))
336 338
337 339 #SEARCH
338 340 rmap.connect('search', '%s/search' % ADMIN_PREFIX, controller='search',)
339 341 rmap.connect('search_repo', '%s/search/{search_repo:.*}' % ADMIN_PREFIX,
340 342 controller='search')
341 343
342 344 #LOGIN/LOGOUT/REGISTER/SIGN IN
343 345 rmap.connect('login_home', '%s/login' % ADMIN_PREFIX, controller='login')
344 346 rmap.connect('logout_home', '%s/logout' % ADMIN_PREFIX, controller='login',
345 347 action='logout')
346 348
347 349 rmap.connect('register', '%s/register' % ADMIN_PREFIX, controller='login',
348 350 action='register')
349 351
350 352 rmap.connect('reset_password', '%s/password_reset' % ADMIN_PREFIX,
351 353 controller='login', action='password_reset')
352 354
353 355 rmap.connect('reset_password_confirmation',
354 356 '%s/password_reset_confirmation' % ADMIN_PREFIX,
355 357 controller='login', action='password_reset_confirmation')
356 358
357 359 #FEEDS
358 360 rmap.connect('rss_feed_home', '/{repo_name:.*}/feed/rss',
359 361 controller='feed', action='rss',
360 362 conditions=dict(function=check_repo))
361 363
362 364 rmap.connect('atom_feed_home', '/{repo_name:.*}/feed/atom',
363 365 controller='feed', action='atom',
364 366 conditions=dict(function=check_repo))
365 367
366 368 #==========================================================================
367 369 # REPOSITORY ROUTES
368 370 #==========================================================================
369 371 rmap.connect('summary_home', '/{repo_name:.*}',
370 372 controller='summary',
371 373 conditions=dict(function=check_repo))
372 374
373 375 rmap.connect('repos_group_home', '/{group_name:.*}',
374 376 controller='admin/repos_groups', action="show_by_name",
375 377 conditions=dict(function=check_group))
376 378
377 379 rmap.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
378 380 controller='changeset', revision='tip',
379 381 conditions=dict(function=check_repo))
380 382
381 383 rmap.connect('changeset_comment', '/{repo_name:.*}/changeset/{revision}/comment',
382 384 controller='changeset', revision='tip', action='comment',
383 385 conditions=dict(function=check_repo))
384 386
385 387 rmap.connect('changeset_comment_delete', '/{repo_name:.*}/changeset/comment/{comment_id}/delete',
386 388 controller='changeset', action='delete_comment',
387 389 conditions=dict(function=check_repo, method=["DELETE"]))
388 390
389 391 rmap.connect('raw_changeset_home',
390 392 '/{repo_name:.*}/raw-changeset/{revision}',
391 393 controller='changeset', action='raw_changeset',
392 394 revision='tip', conditions=dict(function=check_repo))
393 395
394 396 rmap.connect('summary_home', '/{repo_name:.*}/summary',
395 397 controller='summary', conditions=dict(function=check_repo))
396 398
397 399 rmap.connect('shortlog_home', '/{repo_name:.*}/shortlog',
398 400 controller='shortlog', conditions=dict(function=check_repo))
399 401
400 402 rmap.connect('branches_home', '/{repo_name:.*}/branches',
401 403 controller='branches', conditions=dict(function=check_repo))
402 404
403 405 rmap.connect('tags_home', '/{repo_name:.*}/tags',
404 406 controller='tags', conditions=dict(function=check_repo))
405 407
406 408 rmap.connect('bookmarks_home', '/{repo_name:.*}/bookmarks',
407 409 controller='bookmarks', conditions=dict(function=check_repo))
408 410
409 411 rmap.connect('changelog_home', '/{repo_name:.*}/changelog',
410 412 controller='changelog', conditions=dict(function=check_repo))
411 413
412 414 rmap.connect('changelog_details', '/{repo_name:.*}/changelog_details/{cs}',
413 415 controller='changelog', action='changelog_details',
414 416 conditions=dict(function=check_repo))
415 417
416 418 rmap.connect('files_home', '/{repo_name:.*}/files/{revision}/{f_path:.*}',
417 419 controller='files', revision='tip', f_path='',
418 420 conditions=dict(function=check_repo))
419 421
420 422 rmap.connect('files_diff_home', '/{repo_name:.*}/diff/{f_path:.*}',
421 423 controller='files', action='diff', revision='tip', f_path='',
422 424 conditions=dict(function=check_repo))
423 425
424 426 rmap.connect('files_rawfile_home',
425 427 '/{repo_name:.*}/rawfile/{revision}/{f_path:.*}',
426 428 controller='files', action='rawfile', revision='tip',
427 429 f_path='', conditions=dict(function=check_repo))
428 430
429 431 rmap.connect('files_raw_home',
430 432 '/{repo_name:.*}/raw/{revision}/{f_path:.*}',
431 433 controller='files', action='raw', revision='tip', f_path='',
432 434 conditions=dict(function=check_repo))
433 435
434 436 rmap.connect('files_annotate_home',
435 437 '/{repo_name:.*}/annotate/{revision}/{f_path:.*}',
436 438 controller='files', action='annotate', revision='tip',
437 439 f_path='', conditions=dict(function=check_repo))
438 440
439 441 rmap.connect('files_edit_home',
440 442 '/{repo_name:.*}/edit/{revision}/{f_path:.*}',
441 443 controller='files', action='edit', revision='tip',
442 444 f_path='', conditions=dict(function=check_repo))
443 445
444 446 rmap.connect('files_add_home',
445 447 '/{repo_name:.*}/add/{revision}/{f_path:.*}',
446 448 controller='files', action='add', revision='tip',
447 449 f_path='', conditions=dict(function=check_repo))
448 450
449 451 rmap.connect('files_archive_home', '/{repo_name:.*}/archive/{fname}',
450 452 controller='files', action='archivefile',
451 453 conditions=dict(function=check_repo))
452 454
453 455 rmap.connect('files_nodelist_home',
454 456 '/{repo_name:.*}/nodelist/{revision}/{f_path:.*}',
455 457 controller='files', action='nodelist',
456 458 conditions=dict(function=check_repo))
457 459
458 460 rmap.connect('repo_settings_delete', '/{repo_name:.*}/settings',
459 461 controller='settings', action="delete",
460 462 conditions=dict(method=["DELETE"], function=check_repo))
461 463
462 464 rmap.connect('repo_settings_update', '/{repo_name:.*}/settings',
463 465 controller='settings', action="update",
464 466 conditions=dict(method=["PUT"], function=check_repo))
465 467
466 468 rmap.connect('repo_settings_home', '/{repo_name:.*}/settings',
467 469 controller='settings', action='index',
468 470 conditions=dict(function=check_repo))
469 471
470 472 rmap.connect('repo_fork_create_home', '/{repo_name:.*}/fork',
471 473 controller='forks', action='fork_create',
472 474 conditions=dict(function=check_repo, method=["POST"]))
473 475
474 476 rmap.connect('repo_fork_home', '/{repo_name:.*}/fork',
475 477 controller='forks', action='fork',
476 478 conditions=dict(function=check_repo))
477 479
478 480 rmap.connect('repo_forks_home', '/{repo_name:.*}/forks',
479 481 controller='forks', action='forks',
480 482 conditions=dict(function=check_repo))
481 483
482 484 rmap.connect('repo_followers_home', '/{repo_name:.*}/followers',
483 485 controller='followers', action='followers',
484 486 conditions=dict(function=check_repo))
485 487
486 488
487 489 return rmap
@@ -1,398 +1,424
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.admin.repos
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Admin controller for RhodeCode
7 7
8 8 :created_on: Apr 7, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 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 from formencode import htmlfill
30 30
31 31 from paste.httpexceptions import HTTPInternalServerError
32 32 from pylons import request, session, tmpl_context as c, url
33 33 from pylons.controllers.util import redirect
34 34 from pylons.i18n.translation import _
35 35 from sqlalchemy.exc import IntegrityError
36 36
37 37 from rhodecode.lib import helpers as h
38 38 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
39 39 HasPermissionAnyDecorator
40 40 from rhodecode.lib.base import BaseController, render
41 41 from rhodecode.lib.utils import invalidate_cache, action_logger, repo_name_slug
42 42 from rhodecode.lib.helpers import get_token
43 43 from rhodecode.model.meta import Session
44 44 from rhodecode.model.db import User, Repository, UserFollowing, RepoGroup
45 45 from rhodecode.model.forms import RepoForm
46 46 from rhodecode.model.scm import ScmModel
47 47 from rhodecode.model.repo import RepoModel
48 48
49 49 log = logging.getLogger(__name__)
50 50
51 51
52 52 class ReposController(BaseController):
53 53 """
54 54 REST Controller styled on the Atom Publishing Protocol"""
55 55 # To properly map this controller, ensure your config/routing.py
56 56 # file has a resource setup:
57 57 # map.resource('repo', 'repos')
58 58
59 59 @LoginRequired()
60 60 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
61 61 def __before__(self):
62 62 c.admin_user = session.get('admin_user')
63 63 c.admin_username = session.get('admin_username')
64 64 super(ReposController, self).__before__()
65 65
66 66 def __load_defaults(self):
67 67 c.repo_groups = RepoGroup.groups_choices()
68 68 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
69 69
70 70 repo_model = RepoModel()
71 71 c.users_array = repo_model.get_users_js()
72 72 c.users_groups_array = repo_model.get_users_groups_js()
73 73
74 74 def __load_data(self, repo_name=None):
75 75 """
76 76 Load defaults settings for edit, and update
77 77
78 78 :param repo_name:
79 79 """
80 80 self.__load_defaults()
81 81
82 82 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
83 83 repo = db_repo.scm_instance
84 84
85 85 if c.repo_info is None:
86 86 h.flash(_('%s repository is not mapped to db perhaps'
87 87 ' it was created or renamed from the filesystem'
88 88 ' please run the application again'
89 89 ' in order to rescan repositories') % repo_name,
90 90 category='error')
91 91
92 92 return redirect(url('repos'))
93 93
94 94 c.default_user_id = User.get_by_username('default').user_id
95 95 c.in_public_journal = UserFollowing.query()\
96 96 .filter(UserFollowing.user_id == c.default_user_id)\
97 97 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
98 98
99 99 if c.repo_info.stats:
100 100 last_rev = c.repo_info.stats.stat_on_revision
101 101 else:
102 102 last_rev = 0
103 103 c.stats_revision = last_rev
104 104
105 105 c.repo_last_rev = repo.count() - 1 if repo.revisions else 0
106 106
107 107 if last_rev == 0 or c.repo_last_rev == 0:
108 108 c.stats_percentage = 0
109 109 else:
110 110 c.stats_percentage = '%.2f' % ((float((last_rev)) /
111 111 c.repo_last_rev) * 100)
112 112
113 113 defaults = RepoModel()._get_defaults(repo_name)
114
115 c.repos_list = [('', _('--REMOVE FORK--'))]
116 c.repos_list += [(x.repo_id, x.repo_name) for x in
117 Repository.query().order_by(Repository.repo_name).all()]
114 118 return defaults
115 119
116 120 @HasPermissionAllDecorator('hg.admin')
117 121 def index(self, format='html'):
118 122 """GET /repos: All items in the collection"""
119 123 # url('repos')
120 124
121 125 c.repos_list = ScmModel().get_repos(Repository.query()
122 126 .order_by(Repository.repo_name)
123 127 .all(), sort_key='name_sort')
124 128 return render('admin/repos/repos.html')
125 129
126 130 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
127 131 def create(self):
128 132 """
129 133 POST /repos: Create a new item"""
130 134 # url('repos')
131 135
132 136 self.__load_defaults()
133 137 form_result = {}
134 138 try:
135 139 form_result = RepoForm(repo_groups=c.repo_groups_choices)()\
136 140 .to_python(dict(request.POST))
137 141 RepoModel().create(form_result, self.rhodecode_user)
138 142 if form_result['clone_uri']:
139 143 h.flash(_('created repository %s from %s') \
140 144 % (form_result['repo_name'], form_result['clone_uri']),
141 145 category='success')
142 146 else:
143 147 h.flash(_('created repository %s') % form_result['repo_name'],
144 148 category='success')
145 149
146 150 if request.POST.get('user_created'):
147 151 # created by regular non admin user
148 152 action_logger(self.rhodecode_user, 'user_created_repo',
149 153 form_result['repo_name_full'], '', self.sa)
150 154 else:
151 155 action_logger(self.rhodecode_user, 'admin_created_repo',
152 156 form_result['repo_name_full'], '', self.sa)
153 157 Session.commit()
154 158 except formencode.Invalid, errors:
155 159
156 160 c.new_repo = errors.value['repo_name']
157 161
158 162 if request.POST.get('user_created'):
159 163 r = render('admin/repos/repo_add_create_repository.html')
160 164 else:
161 165 r = render('admin/repos/repo_add.html')
162 166
163 167 return htmlfill.render(
164 168 r,
165 169 defaults=errors.value,
166 170 errors=errors.error_dict or {},
167 171 prefix_error=False,
168 172 encoding="UTF-8")
169 173
170 174 except Exception:
171 175 log.error(traceback.format_exc())
172 176 msg = _('error occurred during creation of repository %s') \
173 177 % form_result.get('repo_name')
174 178 h.flash(msg, category='error')
175 179 if request.POST.get('user_created'):
176 180 return redirect(url('home'))
177 181 return redirect(url('repos'))
178 182
179 183 @HasPermissionAllDecorator('hg.admin')
180 184 def new(self, format='html'):
181 185 """GET /repos/new: Form to create a new item"""
182 186 new_repo = request.GET.get('repo', '')
183 187 c.new_repo = repo_name_slug(new_repo)
184 188 self.__load_defaults()
185 189 return render('admin/repos/repo_add.html')
186 190
187 191 @HasPermissionAllDecorator('hg.admin')
188 192 def update(self, repo_name):
189 193 """
190 194 PUT /repos/repo_name: Update an existing item"""
191 195 # Forms posted to this method should contain a hidden field:
192 196 # <input type="hidden" name="_method" value="PUT" />
193 197 # Or using helpers:
194 198 # h.form(url('repo', repo_name=ID),
195 199 # method='put')
196 200 # url('repo', repo_name=ID)
197 201 self.__load_defaults()
198 202 repo_model = RepoModel()
199 203 changed_name = repo_name
200 204 _form = RepoForm(edit=True, old_data={'repo_name': repo_name},
201 205 repo_groups=c.repo_groups_choices)()
202 206 try:
203 207 form_result = _form.to_python(dict(request.POST))
204 208 repo = repo_model.update(repo_name, form_result)
205 209 invalidate_cache('get_repo_cached_%s' % repo_name)
206 210 h.flash(_('Repository %s updated successfully' % repo_name),
207 211 category='success')
208 212 changed_name = repo.repo_name
209 213 action_logger(self.rhodecode_user, 'admin_updated_repo',
210 214 changed_name, '', self.sa)
211 215 Session.commit()
212 216 except formencode.Invalid, errors:
213 217 defaults = self.__load_data(repo_name)
214 218 defaults.update(errors.value)
215 219 return htmlfill.render(
216 220 render('admin/repos/repo_edit.html'),
217 221 defaults=defaults,
218 222 errors=errors.error_dict or {},
219 223 prefix_error=False,
220 224 encoding="UTF-8")
221 225
222 226 except Exception:
223 227 log.error(traceback.format_exc())
224 228 h.flash(_('error occurred during update of repository %s') \
225 229 % repo_name, category='error')
226 230 return redirect(url('edit_repo', repo_name=changed_name))
227 231
228 232 @HasPermissionAllDecorator('hg.admin')
229 233 def delete(self, repo_name):
230 234 """
231 235 DELETE /repos/repo_name: Delete an existing item"""
232 236 # Forms posted to this method should contain a hidden field:
233 237 # <input type="hidden" name="_method" value="DELETE" />
234 238 # Or using helpers:
235 239 # h.form(url('repo', repo_name=ID),
236 240 # method='delete')
237 241 # url('repo', repo_name=ID)
238 242
239 243 repo_model = RepoModel()
240 244 repo = repo_model.get_by_repo_name(repo_name)
241 245 if not repo:
242 246 h.flash(_('%s repository is not mapped to db perhaps'
243 247 ' it was moved or renamed from the filesystem'
244 248 ' please run the application again'
245 249 ' in order to rescan repositories') % repo_name,
246 250 category='error')
247 251
248 252 return redirect(url('repos'))
249 253 try:
250 254 action_logger(self.rhodecode_user, 'admin_deleted_repo',
251 255 repo_name, '', self.sa)
252 256 repo_model.delete(repo)
253 257 invalidate_cache('get_repo_cached_%s' % repo_name)
254 258 h.flash(_('deleted repository %s') % repo_name, category='success')
255 259 Session.commit()
256 260 except IntegrityError, e:
257 261 if e.message.find('repositories_fork_id_fkey') != -1:
258 262 log.error(traceback.format_exc())
259 263 h.flash(_('Cannot delete %s it still contains attached '
260 264 'forks') % repo_name,
261 265 category='warning')
262 266 else:
263 267 log.error(traceback.format_exc())
264 268 h.flash(_('An error occurred during '
265 269 'deletion of %s') % repo_name,
266 270 category='error')
267 271
268 272 except Exception, e:
269 273 log.error(traceback.format_exc())
270 274 h.flash(_('An error occurred during deletion of %s') % repo_name,
271 275 category='error')
272 276
273 277 return redirect(url('repos'))
274 278
275 279 @HasPermissionAllDecorator('hg.admin')
276 280 def delete_perm_user(self, repo_name):
277 281 """
278 282 DELETE an existing repository permission user
279 283
280 284 :param repo_name:
281 285 """
282 286
283 287 try:
284 288 repo_model = RepoModel()
285 289 repo_model.delete_perm_user(request.POST, repo_name)
286 290 except Exception, e:
287 291 h.flash(_('An error occurred during deletion of repository user'),
288 292 category='error')
289 293 raise HTTPInternalServerError()
290 294
291 295 @HasPermissionAllDecorator('hg.admin')
292 296 def delete_perm_users_group(self, repo_name):
293 297 """
294 298 DELETE an existing repository permission users group
295 299
296 300 :param repo_name:
297 301 """
298 302 try:
299 303 repo_model = RepoModel()
300 304 repo_model.delete_perm_users_group(request.POST, repo_name)
301 305 except Exception, e:
302 306 h.flash(_('An error occurred during deletion of repository'
303 307 ' users groups'),
304 308 category='error')
305 309 raise HTTPInternalServerError()
306 310
307 311 @HasPermissionAllDecorator('hg.admin')
308 312 def repo_stats(self, repo_name):
309 313 """
310 314 DELETE an existing repository statistics
311 315
312 316 :param repo_name:
313 317 """
314 318
315 319 try:
316 320 repo_model = RepoModel()
317 321 repo_model.delete_stats(repo_name)
318 322 except Exception, e:
319 323 h.flash(_('An error occurred during deletion of repository stats'),
320 324 category='error')
321 325 return redirect(url('edit_repo', repo_name=repo_name))
322 326
323 327 @HasPermissionAllDecorator('hg.admin')
324 328 def repo_cache(self, repo_name):
325 329 """
326 330 INVALIDATE existing repository cache
327 331
328 332 :param repo_name:
329 333 """
330 334
331 335 try:
332 336 ScmModel().mark_for_invalidation(repo_name)
333 337 except Exception, e:
334 338 h.flash(_('An error occurred during cache invalidation'),
335 339 category='error')
336 340 return redirect(url('edit_repo', repo_name=repo_name))
337 341
338 342 @HasPermissionAllDecorator('hg.admin')
339 343 def repo_public_journal(self, repo_name):
340 344 """
341 345 Set's this repository to be visible in public journal,
342 346 in other words assing default user to follow this repo
343 347
344 348 :param repo_name:
345 349 """
346 350
347 351 cur_token = request.POST.get('auth_token')
348 352 token = get_token()
349 353 if cur_token == token:
350 354 try:
351 355 repo_id = Repository.get_by_repo_name(repo_name).repo_id
352 356 user_id = User.get_by_username('default').user_id
353 357 self.scm_model.toggle_following_repo(repo_id, user_id)
354 358 h.flash(_('Updated repository visibility in public journal'),
355 359 category='success')
356 360 except:
357 361 h.flash(_('An error occurred during setting this'
358 362 ' repository in public journal'),
359 363 category='error')
360 364
361 365 else:
362 366 h.flash(_('Token mismatch'), category='error')
363 367 return redirect(url('edit_repo', repo_name=repo_name))
364 368
365 369 @HasPermissionAllDecorator('hg.admin')
366 370 def repo_pull(self, repo_name):
367 371 """
368 372 Runs task to update given repository with remote changes,
369 373 ie. make pull on remote location
370 374
371 375 :param repo_name:
372 376 """
373 377 try:
374 378 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
375 379 h.flash(_('Pulled from remote location'), category='success')
376 380 except Exception, e:
377 381 h.flash(_('An error occurred during pull from remote location'),
378 382 category='error')
379 383
380 384 return redirect(url('edit_repo', repo_name=repo_name))
381 385
382 386 @HasPermissionAllDecorator('hg.admin')
387 def repo_as_fork(self, repo_name):
388 """
389 Mark given repository as a fork of another
390
391 :param repo_name:
392 """
393 try:
394 fork_id = request.POST.get('id_fork_of')
395 repo = ScmModel().mark_as_fork(repo_name, fork_id,
396 self.rhodecode_user.username)
397 fork = repo.fork.repo_name if repo.fork else _('Nothing')
398 Session.commit()
399 h.flash(_('Marked repo %s as fork of %s' % (repo_name,fork)),
400 category='success')
401 except Exception, e:
402 raise
403 h.flash(_('An error occurred during this operation'),
404 category='error')
405
406 return redirect(url('edit_repo', repo_name=repo_name))
407
408 @HasPermissionAllDecorator('hg.admin')
383 409 def show(self, repo_name, format='html'):
384 410 """GET /repos/repo_name: Show a specific item"""
385 411 # url('repo', repo_name=ID)
386 412
387 413 @HasPermissionAllDecorator('hg.admin')
388 414 def edit(self, repo_name, format='html'):
389 415 """GET /repos/repo_name/edit: Form to edit an existing item"""
390 416 # url('edit_repo', repo_name=ID)
391 417 defaults = self.__load_data(repo_name)
392 418
393 419 return htmlfill.render(
394 420 render('admin/repos/repo_edit.html'),
395 421 defaults=defaults,
396 422 encoding="UTF-8",
397 423 force_defaults=False
398 424 )
@@ -1,93 +1,92
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.__init__
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 The application's model objects
7 7
8 8 :created_on: Nov 25, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12
13 13
14 14 :example:
15 15
16 16 .. code-block:: python
17 17
18 18 from paste.deploy import appconfig
19 19 from pylons import config
20 20 from sqlalchemy import engine_from_config
21 21 from rhodecode.config.environment import load_environment
22 22
23 23 conf = appconfig('config:development.ini', relative_to = './../../')
24 24 load_environment(conf.global_conf, conf.local_conf)
25 25
26 26 engine = engine_from_config(config, 'sqlalchemy.')
27 27 init_model(engine)
28 28 # RUN YOUR CODE HERE
29 29
30 30 """
31 31 # This program is free software: you can redistribute it and/or modify
32 32 # it under the terms of the GNU General Public License as published by
33 33 # the Free Software Foundation, either version 3 of the License, or
34 34 # (at your option) any later version.
35 35 #
36 36 # This program is distributed in the hope that it will be useful,
37 37 # but WITHOUT ANY WARRANTY; without even the implied warranty of
38 38 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
39 39 # GNU General Public License for more details.
40 40 #
41 41 # You should have received a copy of the GNU General Public License
42 42 # along with this program. If not, see <http://www.gnu.org/licenses/>.
43 43
44 44 import logging
45 45
46 46 from rhodecode.model import meta
47 47
48 48 log = logging.getLogger(__name__)
49 49
50 50
51 51 def init_model(engine):
52 52 """
53 53 Initializes db session, bind the engine with the metadata,
54 54 Call this before using any of the tables or classes in the model,
55 55 preferably once in application start
56 56
57 57 :param engine: engine to bind to
58 58 """
59 59 log.info("initializing db for %s", engine)
60 60 meta.Base.metadata.bind = engine
61 61
62 62
63 63 class BaseModel(object):
64 64 """
65 65 Base Model for all RhodeCode models, it adds sql alchemy session
66 66 into instance of model
67 67
68 68 :param sa: If passed it reuses this session instead of creating a new one
69 69 """
70 70
71 71 def __init__(self, sa=None):
72 72 if sa is not None:
73 73 self.sa = sa
74 74 else:
75 75 self.sa = meta.Session
76 76
77 77 def _get_instance(self, cls, instance):
78 78 """
79 79 Get's instance of given cls using some simple lookup mechanism
80 80
81 81 :param cls: class to fetch
82 82 :param instance: int or Instance
83 83 """
84 84
85 85 if isinstance(instance, cls):
86 86 return instance
87 87 elif isinstance(instance, int) or str(instance).isdigit():
88 88 return cls.get(instance)
89 89 else:
90 90 if instance:
91 91 raise Exception('given object must be int or Instance'
92 ' of %s got %s' % (type(cls),
93 type(instance)))
92 ' of %s got %s' % (type(cls), type(instance)))
@@ -1,369 +1,388
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.scm
4 4 ~~~~~~~~~~~~~~~~~~~
5 5
6 6 Scm model for RhodeCode
7 7
8 8 :created_on: Apr 9, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 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 import os
26 26 import time
27 27 import traceback
28 28 import logging
29 29
30 30 from vcs import get_backend
31 31 from vcs.exceptions import RepositoryError
32 32 from vcs.utils.lazy import LazyProperty
33 33 from vcs.nodes import FileNode
34 34
35 35 from rhodecode import BACKENDS
36 36 from rhodecode.lib import helpers as h
37 37 from rhodecode.lib import safe_str
38 38 from rhodecode.lib.auth import HasRepoPermissionAny
39 39 from rhodecode.lib.utils import get_repos as get_filesystem_repos, make_ui, \
40 40 action_logger, EmptyChangeset
41 41 from rhodecode.model import BaseModel
42 42 from rhodecode.model.db import Repository, RhodeCodeUi, CacheInvalidation, \
43 43 UserFollowing, UserLog, User
44 44
45 45 log = logging.getLogger(__name__)
46 46
47 47
48 48 class UserTemp(object):
49 49 def __init__(self, user_id):
50 50 self.user_id = user_id
51 51
52 52 def __repr__(self):
53 53 return "<%s('id:%s')>" % (self.__class__.__name__, self.user_id)
54 54
55 55
56 56 class RepoTemp(object):
57 57 def __init__(self, repo_id):
58 58 self.repo_id = repo_id
59 59
60 60 def __repr__(self):
61 61 return "<%s('id:%s')>" % (self.__class__.__name__, self.repo_id)
62 62
63 63 class CachedRepoList(object):
64 64
65 65 def __init__(self, db_repo_list, repos_path, order_by=None):
66 66 self.db_repo_list = db_repo_list
67 67 self.repos_path = repos_path
68 68 self.order_by = order_by
69 69 self.reversed = (order_by or '').startswith('-')
70 70
71 71 def __len__(self):
72 72 return len(self.db_repo_list)
73 73
74 74 def __repr__(self):
75 75 return '<%s (%s)>' % (self.__class__.__name__, self.__len__())
76 76
77 77 def __iter__(self):
78 78 for dbr in self.db_repo_list:
79 79 scmr = dbr.scm_instance_cached
80 80 # check permission at this level
81 81 if not HasRepoPermissionAny('repository.read', 'repository.write',
82 82 'repository.admin')(dbr.repo_name,
83 83 'get repo check'):
84 84 continue
85 85
86 86 if scmr is None:
87 87 log.error('%s this repository is present in database but it '
88 88 'cannot be created as an scm instance',
89 89 dbr.repo_name)
90 90 continue
91 91
92 92 last_change = scmr.last_change
93 93 tip = h.get_changeset_safe(scmr, 'tip')
94 94
95 95 tmp_d = {}
96 96 tmp_d['name'] = dbr.repo_name
97 97 tmp_d['name_sort'] = tmp_d['name'].lower()
98 98 tmp_d['description'] = dbr.description
99 99 tmp_d['description_sort'] = tmp_d['description']
100 100 tmp_d['last_change'] = last_change
101 101 tmp_d['last_change_sort'] = time.mktime(last_change.timetuple())
102 102 tmp_d['tip'] = tip.raw_id
103 103 tmp_d['tip_sort'] = tip.revision
104 104 tmp_d['rev'] = tip.revision
105 105 tmp_d['contact'] = dbr.user.full_contact
106 106 tmp_d['contact_sort'] = tmp_d['contact']
107 107 tmp_d['owner_sort'] = tmp_d['contact']
108 108 tmp_d['repo_archives'] = list(scmr._get_archives())
109 109 tmp_d['last_msg'] = tip.message
110 110 tmp_d['author'] = tip.author
111 111 tmp_d['dbrepo'] = dbr.get_dict()
112 112 tmp_d['dbrepo_fork'] = dbr.fork.get_dict() if dbr.fork else {}
113 113 yield tmp_d
114 114
115 115 class ScmModel(BaseModel):
116 116 """
117 117 Generic Scm Model
118 118 """
119 119
120 def __get_repo(self, instance):
121 cls = Repository
122 if isinstance(instance, cls):
123 return instance
124 elif isinstance(instance, int) or str(instance).isdigit():
125 return cls.get(instance)
126 elif isinstance(instance, basestring):
127 return cls.get_by_repo_name(instance)
128 elif instance:
129 raise Exception('given object must be int, basestr or Instance'
130 ' of %s got %s' % (type(cls), type(instance)))
131
120 132 @LazyProperty
121 133 def repos_path(self):
122 134 """
123 135 Get's the repositories root path from database
124 136 """
125 137
126 138 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
127 139
128 140 return q.ui_value
129 141
130 142 def repo_scan(self, repos_path=None):
131 143 """
132 144 Listing of repositories in given path. This path should not be a
133 145 repository itself. Return a dictionary of repository objects
134 146
135 147 :param repos_path: path to directory containing repositories
136 148 """
137 149
138 150 log.info('scanning for repositories in %s', repos_path)
139 151
140 152 if repos_path is None:
141 153 repos_path = self.repos_path
142 154
143 155 baseui = make_ui('db')
144 156 repos = {}
145 157
146 158 for name, path in get_filesystem_repos(repos_path, recursive=True):
147 159
148 160 # name need to be decomposed and put back together using the /
149 161 # since this is internal storage separator for rhodecode
150 162 name = Repository.url_sep().join(name.split(os.sep))
151 163
152 164 try:
153 165 if name in repos:
154 166 raise RepositoryError('Duplicate repository name %s '
155 167 'found in %s' % (name, path))
156 168 else:
157 169
158 170 klass = get_backend(path[0])
159 171
160 172 if path[0] == 'hg' and path[0] in BACKENDS.keys():
161 173 repos[name] = klass(safe_str(path[1]), baseui=baseui)
162 174
163 175 if path[0] == 'git' and path[0] in BACKENDS.keys():
164 176 repos[name] = klass(path[1])
165 177 except OSError:
166 178 continue
167 179
168 180 return repos
169 181
170 182 def get_repos(self, all_repos=None, sort_key=None):
171 183 """
172 184 Get all repos from db and for each repo create it's
173 185 backend instance and fill that backed with information from database
174 186
175 187 :param all_repos: list of repository names as strings
176 188 give specific repositories list, good for filtering
177 189 """
178 190 if all_repos is None:
179 191 all_repos = self.sa.query(Repository)\
180 192 .filter(Repository.group_id == None)\
181 193 .order_by(Repository.repo_name).all()
182 194
183 195 repo_iter = CachedRepoList(all_repos, repos_path=self.repos_path,
184 196 order_by=sort_key)
185 197
186 198 return repo_iter
187 199
188 200 def mark_for_invalidation(self, repo_name):
189 201 """Puts cache invalidation task into db for
190 202 further global cache invalidation
191 203
192 204 :param repo_name: this repo that should invalidation take place
193 205 """
194 206 CacheInvalidation.set_invalidate(repo_name)
195 207 CacheInvalidation.set_invalidate(repo_name + "_README")
196 208
197 209 def toggle_following_repo(self, follow_repo_id, user_id):
198 210
199 211 f = self.sa.query(UserFollowing)\
200 212 .filter(UserFollowing.follows_repo_id == follow_repo_id)\
201 213 .filter(UserFollowing.user_id == user_id).scalar()
202 214
203 215 if f is not None:
204 216 try:
205 217 self.sa.delete(f)
206 218 action_logger(UserTemp(user_id),
207 219 'stopped_following_repo',
208 220 RepoTemp(follow_repo_id))
209 221 return
210 222 except:
211 223 log.error(traceback.format_exc())
212 224 raise
213 225
214 226 try:
215 227 f = UserFollowing()
216 228 f.user_id = user_id
217 229 f.follows_repo_id = follow_repo_id
218 230 self.sa.add(f)
219 231
220 232 action_logger(UserTemp(user_id),
221 233 'started_following_repo',
222 234 RepoTemp(follow_repo_id))
223 235 except:
224 236 log.error(traceback.format_exc())
225 237 raise
226 238
227 239 def toggle_following_user(self, follow_user_id, user_id):
228 240 f = self.sa.query(UserFollowing)\
229 241 .filter(UserFollowing.follows_user_id == follow_user_id)\
230 242 .filter(UserFollowing.user_id == user_id).scalar()
231 243
232 244 if f is not None:
233 245 try:
234 246 self.sa.delete(f)
235 247 return
236 248 except:
237 249 log.error(traceback.format_exc())
238 250 raise
239 251
240 252 try:
241 253 f = UserFollowing()
242 254 f.user_id = user_id
243 255 f.follows_user_id = follow_user_id
244 256 self.sa.add(f)
245 257 except:
246 258 log.error(traceback.format_exc())
247 259 raise
248 260
249 261 def is_following_repo(self, repo_name, user_id, cache=False):
250 262 r = self.sa.query(Repository)\
251 263 .filter(Repository.repo_name == repo_name).scalar()
252 264
253 265 f = self.sa.query(UserFollowing)\
254 266 .filter(UserFollowing.follows_repository == r)\
255 267 .filter(UserFollowing.user_id == user_id).scalar()
256 268
257 269 return f is not None
258 270
259 271 def is_following_user(self, username, user_id, cache=False):
260 272 u = User.get_by_username(username)
261 273
262 274 f = self.sa.query(UserFollowing)\
263 275 .filter(UserFollowing.follows_user == u)\
264 276 .filter(UserFollowing.user_id == user_id).scalar()
265 277
266 278 return f is not None
267 279
268 280 def get_followers(self, repo_id):
269 281 if not isinstance(repo_id, int):
270 282 repo_id = getattr(Repository.get_by_repo_name(repo_id), 'repo_id')
271 283
272 284 return self.sa.query(UserFollowing)\
273 285 .filter(UserFollowing.follows_repo_id == repo_id).count()
274 286
275 287 def get_forks(self, repo_id):
276 288 if not isinstance(repo_id, int):
277 289 repo_id = getattr(Repository.get_by_repo_name(repo_id), 'repo_id')
278 290
279 291 return self.sa.query(Repository)\
280 292 .filter(Repository.fork_id == repo_id).count()
281 293
294 def mark_as_fork(self, repo, fork, user):
295 repo = self.__get_repo(repo)
296 fork = self.__get_repo(fork)
297 repo.fork = fork
298 self.sa.add(repo)
299 return repo
300
282 301 def pull_changes(self, repo_name, username):
283 302 dbrepo = Repository.get_by_repo_name(repo_name)
284 303 clone_uri = dbrepo.clone_uri
285 304 if not clone_uri:
286 305 raise Exception("This repository doesn't have a clone uri")
287 306
288 307 repo = dbrepo.scm_instance
289 308 try:
290 309 extras = {'ip': '',
291 310 'username': username,
292 311 'action': 'push_remote',
293 312 'repository': repo_name}
294 313
295 314 #inject ui extra param to log this action via push logger
296 315 for k, v in extras.items():
297 316 repo._repo.ui.setconfig('rhodecode_extras', k, v)
298 317
299 318 repo.pull(clone_uri)
300 319 self.mark_for_invalidation(repo_name)
301 320 except:
302 321 log.error(traceback.format_exc())
303 322 raise
304 323
305 324 def commit_change(self, repo, repo_name, cs, user, author, message,
306 325 content, f_path):
307 326
308 327 if repo.alias == 'hg':
309 328 from vcs.backends.hg import MercurialInMemoryChangeset as IMC
310 329 elif repo.alias == 'git':
311 330 from vcs.backends.git import GitInMemoryChangeset as IMC
312 331
313 332 # decoding here will force that we have proper encoded values
314 333 # in any other case this will throw exceptions and deny commit
315 334 content = safe_str(content)
316 335 message = safe_str(message)
317 336 path = safe_str(f_path)
318 337 author = safe_str(author)
319 338 m = IMC(repo)
320 339 m.change(FileNode(path, content))
321 340 tip = m.commit(message=message,
322 341 author=author,
323 342 parents=[cs], branch=cs.branch)
324 343
325 344 new_cs = tip.short_id
326 345 action = 'push_local:%s' % new_cs
327 346
328 347 action_logger(user, action, repo_name)
329 348
330 349 self.mark_for_invalidation(repo_name)
331 350
332 351 def create_node(self, repo, repo_name, cs, user, author, message, content,
333 352 f_path):
334 353 if repo.alias == 'hg':
335 354 from vcs.backends.hg import MercurialInMemoryChangeset as IMC
336 355 elif repo.alias == 'git':
337 356 from vcs.backends.git import GitInMemoryChangeset as IMC
338 357 # decoding here will force that we have proper encoded values
339 358 # in any other case this will throw exceptions and deny commit
340 359
341 360 if isinstance(content, (basestring,)):
342 361 content = safe_str(content)
343 362 elif isinstance(content, file):
344 363 content = content.read()
345 364
346 365 message = safe_str(message)
347 366 path = safe_str(f_path)
348 367 author = safe_str(author)
349 368 m = IMC(repo)
350 369
351 370 if isinstance(cs, EmptyChangeset):
352 371 # Emptychangeset means we we're editing empty repository
353 372 parents = None
354 373 else:
355 374 parents = [cs]
356 375
357 376 m.add(FileNode(path, content=content))
358 377 tip = m.commit(message=message,
359 378 author=author,
360 379 parents=parents, branch=cs.branch)
361 380 new_cs = tip.short_id
362 381 action = 'push_local:%s' % new_cs
363 382
364 383 action_logger(user, action, repo_name)
365 384
366 385 self.mark_for_invalidation(repo_name)
367 386
368 387 def get_unread_journal(self):
369 388 return self.sa.query(UserLog).count()
@@ -1,197 +1,224
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3
4 4 <%def name="title()">
5 5 ${_('Edit repository')} ${c.repo_info.repo_name} - ${c.rhodecode_name}
6 6 </%def>
7 7
8 8 <%def name="breadcrumbs_links()">
9 9 ${h.link_to(_('Admin'),h.url('admin_home'))}
10 10 &raquo;
11 11 ${h.link_to(_('Repositories'),h.url('repos'))}
12 12 &raquo;
13 13 ${_('edit')} &raquo; ${h.link_to(c.repo_info.just_name,h.url('summary_home',repo_name=c.repo_name))}
14 14 </%def>
15 15
16 16 <%def name="page_nav()">
17 17 ${self.menu('admin')}
18 18 </%def>
19 19
20 20 <%def name="main()">
21 21 <div class="box box-left">
22 22 <!-- box / title -->
23 23 <div class="title">
24 24 ${self.breadcrumbs()}
25 25 </div>
26 26 ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='put')}
27 27 <div class="form">
28 28 <!-- fields -->
29 29 <div class="fields">
30 30 <div class="field">
31 31 <div class="label">
32 32 <label for="repo_name">${_('Name')}:</label>
33 33 </div>
34 34 <div class="input">
35 35 ${h.text('repo_name',class_="medium")}
36 36 </div>
37 37 </div>
38 38 <div class="field">
39 39 <div class="label">
40 40 <label for="clone_uri">${_('Clone uri')}:</label>
41 41 </div>
42 42 <div class="input">
43 43 ${h.text('clone_uri',class_="medium")}
44 44 </div>
45 45 </div>
46 46 <div class="field">
47 47 <div class="label">
48 48 <label for="repo_group">${_('Repository group')}:</label>
49 49 </div>
50 50 <div class="input">
51 51 ${h.select('repo_group','',c.repo_groups,class_="medium")}
52 52 </div>
53 53 </div>
54 54 <div class="field">
55 55 <div class="label">
56 56 <label for="repo_type">${_('Type')}:</label>
57 57 </div>
58 58 <div class="input">
59 59 ${h.select('repo_type','hg',c.backends,class_="medium")}
60 60 </div>
61 61 </div>
62 62 <div class="field">
63 63 <div class="label label-textarea">
64 64 <label for="description">${_('Description')}:</label>
65 65 </div>
66 66 <div class="textarea text-area editor">
67 67 ${h.textarea('description',cols=23,rows=5)}
68 68 </div>
69 69 </div>
70 70
71 71 <div class="field">
72 72 <div class="label label-checkbox">
73 73 <label for="private">${_('Private')}:</label>
74 74 </div>
75 75 <div class="checkboxes">
76 76 ${h.checkbox('private',value="True")}
77 77 </div>
78 78 </div>
79 79 <div class="field">
80 80 <div class="label label-checkbox">
81 81 <label for="enable_statistics">${_('Enable statistics')}:</label>
82 82 </div>
83 83 <div class="checkboxes">
84 84 ${h.checkbox('enable_statistics',value="True")}
85 85 </div>
86 86 </div>
87 87 <div class="field">
88 88 <div class="label label-checkbox">
89 89 <label for="enable_downloads">${_('Enable downloads')}:</label>
90 90 </div>
91 91 <div class="checkboxes">
92 92 ${h.checkbox('enable_downloads',value="True")}
93 93 </div>
94 94 </div>
95 95 <div class="field">
96 96 <div class="label">
97 97 <label for="user">${_('Owner')}:</label>
98 98 </div>
99 99 <div class="input input-small ac">
100 100 <div class="perm_ac">
101 101 ${h.text('user',class_='yui-ac-input')}
102 102 <div id="owner_container"></div>
103 103 </div>
104 104 </div>
105 105 </div>
106 106
107 107 <div class="field">
108 108 <div class="label">
109 109 <label for="input">${_('Permissions')}:</label>
110 110 </div>
111 111 <div class="input">
112 112 <%include file="repo_edit_perms.html"/>
113 113 </div>
114 114
115 115 <div class="buttons">
116 116 ${h.submit('save','Save',class_="ui-button")}
117 117 ${h.reset('reset','Reset',class_="ui-button")}
118 118 </div>
119 119 </div>
120 120 </div>
121 121 </div>
122 122 ${h.end_form()}
123 123 </div>
124 124
125 125 <div class="box box-right">
126 126 <div class="title">
127 127 <h5>${_('Administration')}</h5>
128 128 </div>
129 129
130 130 <h3>${_('Statistics')}</h3>
131 131 ${h.form(url('repo_stats', repo_name=c.repo_info.repo_name),method='delete')}
132 132 <div class="form">
133 133 <div class="fields">
134 ${h.submit('reset_stats_%s' % c.repo_info.repo_name,_('Reset current statistics'),class_="refresh_icon action_button",onclick="return confirm('"+_('Confirm to remove current statistics')+"');")}
135 <div class="field" style="border:none">
134 ${h.submit('reset_stats_%s' % c.repo_info.repo_name,_('Reset current statistics'),class_="ui-btn",onclick="return confirm('"+_('Confirm to remove current statistics')+"');")}
135 <div class="field" style="border:none;color:#888">
136 136 <ul>
137 137 <li>${_('Fetched to rev')}: ${c.stats_revision}/${c.repo_last_rev}</li>
138 138 <li>${_('Percentage of stats gathered')}: ${c.stats_percentage} %</li>
139 139 </ul>
140 140 </div>
141
142 141 </div>
143 142 </div>
144 143 ${h.end_form()}
145 144
146 145 %if c.repo_info.clone_uri:
147 146 <h3>${_('Remote')}</h3>
148 147 ${h.form(url('repo_pull', repo_name=c.repo_info.repo_name),method='put')}
149 148 <div class="form">
150 149 <div class="fields">
151 ${h.submit('remote_pull_%s' % c.repo_info.repo_name,_('Pull changes from remote location'),class_="pull_icon action_button",onclick="return confirm('"+_('Confirm to pull changes from remote side')+"');")}
150 ${h.submit('remote_pull_%s' % c.repo_info.repo_name,_('Pull changes from remote location'),class_="ui-btn",onclick="return confirm('"+_('Confirm to pull changes from remote side')+"');")}
152 151 <div class="field" style="border:none">
153 152 <ul>
154 153 <li><a href="${c.repo_info.clone_uri}">${c.repo_info.clone_uri}</a></li>
155 154 </ul>
156 155 </div>
157 156 </div>
158 157 </div>
159 158 ${h.end_form()}
160 159 %endif
161 160
162 161 <h3>${_('Cache')}</h3>
163 162 ${h.form(url('repo_cache', repo_name=c.repo_info.repo_name),method='delete')}
164 163 <div class="form">
165 164 <div class="fields">
166 ${h.submit('reset_cache_%s' % c.repo_info.repo_name,_('Invalidate repository cache'),class_="refresh_icon action_button",onclick="return confirm('"+_('Confirm to invalidate repository cache')+"');")}
165 ${h.submit('reset_cache_%s' % c.repo_info.repo_name,_('Invalidate repository cache'),class_="ui-btn",onclick="return confirm('"+_('Confirm to invalidate repository cache')+"');")}
167 166 </div>
168 167 </div>
169 168 ${h.end_form()}
170 169
171 170 <h3>${_('Public journal')}</h3>
172 171 ${h.form(url('repo_public_journal', repo_name=c.repo_info.repo_name),method='put')}
173 172 <div class="form">
174 <div class="fields">
175 173 ${h.hidden('auth_token',str(h.get_token()))}
174 <div class="field">
176 175 %if c.in_public_journal:
177 ${h.submit('set_public_%s' % c.repo_info.repo_name,_('Remove from public journal'),class_="stop_following_icon action_button")}
176 ${h.submit('set_public_%s' % c.repo_info.repo_name,_('Remove from public journal'),class_="ui-btn")}
178 177 %else:
179 ${h.submit('set_public_%s' % c.repo_info.repo_name,_('Add to public journal'),class_="start_following_icon action_button")}
178 ${h.submit('set_public_%s' % c.repo_info.repo_name,_('Add to public journal'),class_="ui-btn")}
180 179 %endif
181 </div>
180 </div>
181 <div class="field" style="border:none;color:#888">
182 <ul>
183 <li>${_('''All actions made on this repository will be accessible to everyone in public journal''')}
184 </li>
185 </ul>
186 </div>
182 187 </div>
183 188 ${h.end_form()}
184 189
185 190 <h3>${_('Delete')}</h3>
186 191 ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='delete')}
187 192 <div class="form">
188 193 <div class="fields">
189 ${h.submit('remove_%s' % c.repo_info.repo_name,_('Remove this repository'),class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this repository')+"');")}
194 ${h.submit('remove_%s' % c.repo_info.repo_name,_('Remove this repository'),class_="ui-btn red",onclick="return confirm('"+_('Confirm to delete this repository')+"');")}
190 195 </div>
196 <div class="field" style="border:none;color:#888">
197 <ul>
198 <li>${_('''This repository will be renamed in a special way in order to be unaccesible for RhodeCode and VCS systems.
199 If you need fully delete it from filesystem please do it manually''')}
200 </li>
201 </ul>
202 </div>
203 </div>
204 ${h.end_form()}
205
206 <h3>${_('Set as fork')}</h3>
207 ${h.form(url('repo_as_fork', repo_name=c.repo_info.repo_name),method='put')}
208 <div class="form">
209 <div class="fields">
210 ${h.select('id_fork_of','',c.repos_list,class_="medium")}
211 ${h.submit('set_as_fork_%s' % c.repo_info.repo_name,_('set'),class_="ui-btn",)}
212 </div>
213 <div class="field" style="border:none;color:#888">
214 <ul>
215 <li>${_('''Manually set this repository as a fork of another''')}</li>
216 </ul>
217 </div>
191 218 </div>
192 219 ${h.end_form()}
193 220
194 221 </div>
195 222
196 223
197 224 </%def>
General Comments 0
You need to be logged in to leave comments. Login now