##// END OF EJS Templates
Authors of pull-requests can now delete them
marcink -
r2746:49a4864b beta
parent child Browse files
Show More
@@ -1,575 +1,580
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 m.connect('repo_locking', "/repo_locking/{repo_name:.*?}",
142 142 action="repo_locking", conditions=dict(method=["PUT"],
143 143 function=check_repo))
144 144 with rmap.submapper(path_prefix=ADMIN_PREFIX,
145 145 controller='admin/repos_groups') as m:
146 146 m.connect("repos_groups", "/repos_groups",
147 147 action="create", conditions=dict(method=["POST"]))
148 148 m.connect("repos_groups", "/repos_groups",
149 149 action="index", conditions=dict(method=["GET"]))
150 150 m.connect("formatted_repos_groups", "/repos_groups.{format}",
151 151 action="index", conditions=dict(method=["GET"]))
152 152 m.connect("new_repos_group", "/repos_groups/new",
153 153 action="new", conditions=dict(method=["GET"]))
154 154 m.connect("formatted_new_repos_group", "/repos_groups/new.{format}",
155 155 action="new", conditions=dict(method=["GET"]))
156 156 m.connect("update_repos_group", "/repos_groups/{id}",
157 157 action="update", conditions=dict(method=["PUT"],
158 158 function=check_int))
159 159 m.connect("delete_repos_group", "/repos_groups/{id}",
160 160 action="delete", conditions=dict(method=["DELETE"],
161 161 function=check_int))
162 162 m.connect("edit_repos_group", "/repos_groups/{id}/edit",
163 163 action="edit", conditions=dict(method=["GET"],))
164 164 m.connect("formatted_edit_repos_group",
165 165 "/repos_groups/{id}.{format}/edit",
166 166 action="edit", conditions=dict(method=["GET"],
167 167 function=check_int))
168 168 m.connect("repos_group", "/repos_groups/{id}",
169 169 action="show", conditions=dict(method=["GET"],
170 170 function=check_int))
171 171 m.connect("formatted_repos_group", "/repos_groups/{id}.{format}",
172 172 action="show", conditions=dict(method=["GET"],
173 173 function=check_int))
174 174 # ajax delete repos group perm user
175 175 m.connect('delete_repos_group_user_perm',
176 176 "/delete_repos_group_user_perm/{group_name:.*}",
177 177 action="delete_repos_group_user_perm",
178 178 conditions=dict(method=["DELETE"], function=check_group))
179 179
180 180 # ajax delete repos group perm users_group
181 181 m.connect('delete_repos_group_users_group_perm',
182 182 "/delete_repos_group_users_group_perm/{group_name:.*}",
183 183 action="delete_repos_group_users_group_perm",
184 184 conditions=dict(method=["DELETE"], function=check_group))
185 185
186 186 #ADMIN USER REST ROUTES
187 187 with rmap.submapper(path_prefix=ADMIN_PREFIX,
188 188 controller='admin/users') as m:
189 189 m.connect("users", "/users",
190 190 action="create", conditions=dict(method=["POST"]))
191 191 m.connect("users", "/users",
192 192 action="index", conditions=dict(method=["GET"]))
193 193 m.connect("formatted_users", "/users.{format}",
194 194 action="index", conditions=dict(method=["GET"]))
195 195 m.connect("new_user", "/users/new",
196 196 action="new", conditions=dict(method=["GET"]))
197 197 m.connect("formatted_new_user", "/users/new.{format}",
198 198 action="new", conditions=dict(method=["GET"]))
199 199 m.connect("update_user", "/users/{id}",
200 200 action="update", conditions=dict(method=["PUT"]))
201 201 m.connect("delete_user", "/users/{id}",
202 202 action="delete", conditions=dict(method=["DELETE"]))
203 203 m.connect("edit_user", "/users/{id}/edit",
204 204 action="edit", conditions=dict(method=["GET"]))
205 205 m.connect("formatted_edit_user",
206 206 "/users/{id}.{format}/edit",
207 207 action="edit", conditions=dict(method=["GET"]))
208 208 m.connect("user", "/users/{id}",
209 209 action="show", conditions=dict(method=["GET"]))
210 210 m.connect("formatted_user", "/users/{id}.{format}",
211 211 action="show", conditions=dict(method=["GET"]))
212 212
213 213 #EXTRAS USER ROUTES
214 214 m.connect("user_perm", "/users_perm/{id}",
215 215 action="update_perm", conditions=dict(method=["PUT"]))
216 216 m.connect("user_emails", "/users_emails/{id}",
217 217 action="add_email", conditions=dict(method=["PUT"]))
218 218 m.connect("user_emails_delete", "/users_emails/{id}",
219 219 action="delete_email", conditions=dict(method=["DELETE"]))
220 220
221 221 #ADMIN USERS GROUPS REST ROUTES
222 222 with rmap.submapper(path_prefix=ADMIN_PREFIX,
223 223 controller='admin/users_groups') as m:
224 224 m.connect("users_groups", "/users_groups",
225 225 action="create", conditions=dict(method=["POST"]))
226 226 m.connect("users_groups", "/users_groups",
227 227 action="index", conditions=dict(method=["GET"]))
228 228 m.connect("formatted_users_groups", "/users_groups.{format}",
229 229 action="index", conditions=dict(method=["GET"]))
230 230 m.connect("new_users_group", "/users_groups/new",
231 231 action="new", conditions=dict(method=["GET"]))
232 232 m.connect("formatted_new_users_group", "/users_groups/new.{format}",
233 233 action="new", conditions=dict(method=["GET"]))
234 234 m.connect("update_users_group", "/users_groups/{id}",
235 235 action="update", conditions=dict(method=["PUT"]))
236 236 m.connect("delete_users_group", "/users_groups/{id}",
237 237 action="delete", conditions=dict(method=["DELETE"]))
238 238 m.connect("edit_users_group", "/users_groups/{id}/edit",
239 239 action="edit", conditions=dict(method=["GET"]))
240 240 m.connect("formatted_edit_users_group",
241 241 "/users_groups/{id}.{format}/edit",
242 242 action="edit", conditions=dict(method=["GET"]))
243 243 m.connect("users_group", "/users_groups/{id}",
244 244 action="show", conditions=dict(method=["GET"]))
245 245 m.connect("formatted_users_group", "/users_groups/{id}.{format}",
246 246 action="show", conditions=dict(method=["GET"]))
247 247
248 248 #EXTRAS USER ROUTES
249 249 m.connect("users_group_perm", "/users_groups_perm/{id}",
250 250 action="update_perm", conditions=dict(method=["PUT"]))
251 251
252 252 #ADMIN GROUP REST ROUTES
253 253 rmap.resource('group', 'groups',
254 254 controller='admin/groups', path_prefix=ADMIN_PREFIX)
255 255
256 256 #ADMIN PERMISSIONS REST ROUTES
257 257 rmap.resource('permission', 'permissions',
258 258 controller='admin/permissions', path_prefix=ADMIN_PREFIX)
259 259
260 260 ##ADMIN LDAP SETTINGS
261 261 rmap.connect('ldap_settings', '%s/ldap' % ADMIN_PREFIX,
262 262 controller='admin/ldap_settings', action='ldap_settings',
263 263 conditions=dict(method=["POST"]))
264 264
265 265 rmap.connect('ldap_home', '%s/ldap' % ADMIN_PREFIX,
266 266 controller='admin/ldap_settings')
267 267
268 268 #ADMIN SETTINGS REST ROUTES
269 269 with rmap.submapper(path_prefix=ADMIN_PREFIX,
270 270 controller='admin/settings') as m:
271 271 m.connect("admin_settings", "/settings",
272 272 action="create", conditions=dict(method=["POST"]))
273 273 m.connect("admin_settings", "/settings",
274 274 action="index", conditions=dict(method=["GET"]))
275 275 m.connect("formatted_admin_settings", "/settings.{format}",
276 276 action="index", conditions=dict(method=["GET"]))
277 277 m.connect("admin_new_setting", "/settings/new",
278 278 action="new", conditions=dict(method=["GET"]))
279 279 m.connect("formatted_admin_new_setting", "/settings/new.{format}",
280 280 action="new", conditions=dict(method=["GET"]))
281 281 m.connect("/settings/{setting_id}",
282 282 action="update", conditions=dict(method=["PUT"]))
283 283 m.connect("/settings/{setting_id}",
284 284 action="delete", conditions=dict(method=["DELETE"]))
285 285 m.connect("admin_edit_setting", "/settings/{setting_id}/edit",
286 286 action="edit", conditions=dict(method=["GET"]))
287 287 m.connect("formatted_admin_edit_setting",
288 288 "/settings/{setting_id}.{format}/edit",
289 289 action="edit", conditions=dict(method=["GET"]))
290 290 m.connect("admin_setting", "/settings/{setting_id}",
291 291 action="show", conditions=dict(method=["GET"]))
292 292 m.connect("formatted_admin_setting", "/settings/{setting_id}.{format}",
293 293 action="show", conditions=dict(method=["GET"]))
294 294 m.connect("admin_settings_my_account", "/my_account",
295 295 action="my_account", conditions=dict(method=["GET"]))
296 296 m.connect("admin_settings_my_account_update", "/my_account_update",
297 297 action="my_account_update", conditions=dict(method=["PUT"]))
298 298 m.connect("admin_settings_create_repository", "/create_repository",
299 299 action="create_repository", conditions=dict(method=["GET"]))
300 300 m.connect("admin_settings_my_repos", "/my_account/repos",
301 301 action="my_account_my_repos", conditions=dict(method=["GET"]))
302 302 m.connect("admin_settings_my_pullrequests", "/my_account/pull_requests",
303 303 action="my_account_my_pullrequests", conditions=dict(method=["GET"]))
304 304
305 305 #NOTIFICATION REST ROUTES
306 306 with rmap.submapper(path_prefix=ADMIN_PREFIX,
307 307 controller='admin/notifications') as m:
308 308 m.connect("notifications", "/notifications",
309 309 action="create", conditions=dict(method=["POST"]))
310 310 m.connect("notifications", "/notifications",
311 311 action="index", conditions=dict(method=["GET"]))
312 312 m.connect("notifications_mark_all_read", "/notifications/mark_all_read",
313 313 action="mark_all_read", conditions=dict(method=["GET"]))
314 314 m.connect("formatted_notifications", "/notifications.{format}",
315 315 action="index", conditions=dict(method=["GET"]))
316 316 m.connect("new_notification", "/notifications/new",
317 317 action="new", conditions=dict(method=["GET"]))
318 318 m.connect("formatted_new_notification", "/notifications/new.{format}",
319 319 action="new", conditions=dict(method=["GET"]))
320 320 m.connect("/notification/{notification_id}",
321 321 action="update", conditions=dict(method=["PUT"]))
322 322 m.connect("/notification/{notification_id}",
323 323 action="delete", conditions=dict(method=["DELETE"]))
324 324 m.connect("edit_notification", "/notification/{notification_id}/edit",
325 325 action="edit", conditions=dict(method=["GET"]))
326 326 m.connect("formatted_edit_notification",
327 327 "/notification/{notification_id}.{format}/edit",
328 328 action="edit", conditions=dict(method=["GET"]))
329 329 m.connect("notification", "/notification/{notification_id}",
330 330 action="show", conditions=dict(method=["GET"]))
331 331 m.connect("formatted_notification", "/notifications/{notification_id}.{format}",
332 332 action="show", conditions=dict(method=["GET"]))
333 333
334 334 #ADMIN MAIN PAGES
335 335 with rmap.submapper(path_prefix=ADMIN_PREFIX,
336 336 controller='admin/admin') as m:
337 337 m.connect('admin_home', '', action='index')
338 338 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
339 339 action='add_repo')
340 340
341 341 #==========================================================================
342 342 # API V2
343 343 #==========================================================================
344 344 with rmap.submapper(path_prefix=ADMIN_PREFIX,
345 345 controller='api/api') as m:
346 346 m.connect('api', '/api')
347 347
348 348 #USER JOURNAL
349 349 rmap.connect('journal', '%s/journal' % ADMIN_PREFIX,
350 350 controller='journal', action='index')
351 351 rmap.connect('journal_rss', '%s/journal/rss' % ADMIN_PREFIX,
352 352 controller='journal', action='journal_rss')
353 353 rmap.connect('journal_atom', '%s/journal/atom' % ADMIN_PREFIX,
354 354 controller='journal', action='journal_atom')
355 355
356 356 rmap.connect('public_journal', '%s/public_journal' % ADMIN_PREFIX,
357 357 controller='journal', action="public_journal")
358 358
359 359 rmap.connect('public_journal_rss', '%s/public_journal/rss' % ADMIN_PREFIX,
360 360 controller='journal', action="public_journal_rss")
361 361
362 362 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % ADMIN_PREFIX,
363 363 controller='journal', action="public_journal_rss")
364 364
365 365 rmap.connect('public_journal_atom',
366 366 '%s/public_journal/atom' % ADMIN_PREFIX, controller='journal',
367 367 action="public_journal_atom")
368 368
369 369 rmap.connect('public_journal_atom_old',
370 370 '%s/public_journal_atom' % ADMIN_PREFIX, controller='journal',
371 371 action="public_journal_atom")
372 372
373 373 rmap.connect('toggle_following', '%s/toggle_following' % ADMIN_PREFIX,
374 374 controller='journal', action='toggle_following',
375 375 conditions=dict(method=["POST"]))
376 376
377 377 #SEARCH
378 378 rmap.connect('search', '%s/search' % ADMIN_PREFIX, controller='search',)
379 379 rmap.connect('search_repo', '%s/search/{search_repo:.*}' % ADMIN_PREFIX,
380 380 controller='search')
381 381
382 382 #LOGIN/LOGOUT/REGISTER/SIGN IN
383 383 rmap.connect('login_home', '%s/login' % ADMIN_PREFIX, controller='login')
384 384 rmap.connect('logout_home', '%s/logout' % ADMIN_PREFIX, controller='login',
385 385 action='logout')
386 386
387 387 rmap.connect('register', '%s/register' % ADMIN_PREFIX, controller='login',
388 388 action='register')
389 389
390 390 rmap.connect('reset_password', '%s/password_reset' % ADMIN_PREFIX,
391 391 controller='login', action='password_reset')
392 392
393 393 rmap.connect('reset_password_confirmation',
394 394 '%s/password_reset_confirmation' % ADMIN_PREFIX,
395 395 controller='login', action='password_reset_confirmation')
396 396
397 397 #FEEDS
398 398 rmap.connect('rss_feed_home', '/{repo_name:.*?}/feed/rss',
399 399 controller='feed', action='rss',
400 400 conditions=dict(function=check_repo))
401 401
402 402 rmap.connect('atom_feed_home', '/{repo_name:.*?}/feed/atom',
403 403 controller='feed', action='atom',
404 404 conditions=dict(function=check_repo))
405 405
406 406 #==========================================================================
407 407 # REPOSITORY ROUTES
408 408 #==========================================================================
409 409 rmap.connect('summary_home', '/{repo_name:.*?}',
410 410 controller='summary',
411 411 conditions=dict(function=check_repo))
412 412
413 413 rmap.connect('repos_group_home', '/{group_name:.*}',
414 414 controller='admin/repos_groups', action="show_by_name",
415 415 conditions=dict(function=check_group))
416 416
417 417 rmap.connect('changeset_home', '/{repo_name:.*?}/changeset/{revision}',
418 418 controller='changeset', revision='tip',
419 419 conditions=dict(function=check_repo))
420 420
421 421 rmap.connect('changeset_comment',
422 422 '/{repo_name:.*?}/changeset/{revision}/comment',
423 423 controller='changeset', revision='tip', action='comment',
424 424 conditions=dict(function=check_repo))
425 425
426 426 rmap.connect('changeset_comment_delete',
427 427 '/{repo_name:.*?}/changeset/comment/{comment_id}/delete',
428 428 controller='changeset', action='delete_comment',
429 429 conditions=dict(function=check_repo, method=["DELETE"]))
430 430
431 431 rmap.connect('raw_changeset_home',
432 432 '/{repo_name:.*?}/raw-changeset/{revision}',
433 433 controller='changeset', action='raw_changeset',
434 434 revision='tip', conditions=dict(function=check_repo))
435 435
436 436 rmap.connect('compare_url',
437 437 '/{repo_name:.*?}/compare/{org_ref_type}@{org_ref}...{other_ref_type}@{other_ref}',
438 438 controller='compare', action='index',
439 439 conditions=dict(function=check_repo),
440 440 requirements=dict(
441 441 org_ref_type='(branch|book|tag|rev|org_ref_type)',
442 442 other_ref_type='(branch|book|tag|rev|other_ref_type)')
443 443 )
444 444
445 445 rmap.connect('pullrequest_home',
446 446 '/{repo_name:.*?}/pull-request/new', controller='pullrequests',
447 447 action='index', conditions=dict(function=check_repo,
448 448 method=["GET"]))
449 449
450 450 rmap.connect('pullrequest',
451 451 '/{repo_name:.*?}/pull-request/new', controller='pullrequests',
452 452 action='create', conditions=dict(function=check_repo,
453 453 method=["POST"]))
454 454
455 455 rmap.connect('pullrequest_show',
456 456 '/{repo_name:.*?}/pull-request/{pull_request_id}',
457 457 controller='pullrequests',
458 458 action='show', conditions=dict(function=check_repo,
459 459 method=["GET"]))
460 460 rmap.connect('pullrequest_update',
461 461 '/{repo_name:.*?}/pull-request/{pull_request_id}',
462 462 controller='pullrequests',
463 463 action='update', conditions=dict(function=check_repo,
464 464 method=["PUT"]))
465 rmap.connect('pullrequest_delete',
466 '/{repo_name:.*?}/pull-request/{pull_request_id}',
467 controller='pullrequests',
468 action='delete', conditions=dict(function=check_repo,
469 method=["DELETE"]))
465 470
466 471 rmap.connect('pullrequest_show_all',
467 472 '/{repo_name:.*?}/pull-request',
468 473 controller='pullrequests',
469 474 action='show_all', conditions=dict(function=check_repo,
470 475 method=["GET"]))
471 476
472 477 rmap.connect('pullrequest_comment',
473 478 '/{repo_name:.*?}/pull-request-comment/{pull_request_id}',
474 479 controller='pullrequests',
475 480 action='comment', conditions=dict(function=check_repo,
476 481 method=["POST"]))
477 482
478 483 rmap.connect('pullrequest_comment_delete',
479 484 '/{repo_name:.*?}/pull-request-comment/{comment_id}/delete',
480 485 controller='pullrequests', action='delete_comment',
481 486 conditions=dict(function=check_repo, method=["DELETE"]))
482 487
483 488 rmap.connect('summary_home', '/{repo_name:.*?}/summary',
484 489 controller='summary', conditions=dict(function=check_repo))
485 490
486 491 rmap.connect('shortlog_home', '/{repo_name:.*?}/shortlog',
487 492 controller='shortlog', conditions=dict(function=check_repo))
488 493
489 494 rmap.connect('branches_home', '/{repo_name:.*?}/branches',
490 495 controller='branches', conditions=dict(function=check_repo))
491 496
492 497 rmap.connect('tags_home', '/{repo_name:.*?}/tags',
493 498 controller='tags', conditions=dict(function=check_repo))
494 499
495 500 rmap.connect('bookmarks_home', '/{repo_name:.*?}/bookmarks',
496 501 controller='bookmarks', conditions=dict(function=check_repo))
497 502
498 503 rmap.connect('changelog_home', '/{repo_name:.*?}/changelog',
499 504 controller='changelog', conditions=dict(function=check_repo))
500 505
501 506 rmap.connect('changelog_details', '/{repo_name:.*?}/changelog_details/{cs}',
502 507 controller='changelog', action='changelog_details',
503 508 conditions=dict(function=check_repo))
504 509
505 510 rmap.connect('files_home', '/{repo_name:.*?}/files/{revision}/{f_path:.*}',
506 511 controller='files', revision='tip', f_path='',
507 512 conditions=dict(function=check_repo))
508 513
509 514 rmap.connect('files_diff_home', '/{repo_name:.*?}/diff/{f_path:.*}',
510 515 controller='files', action='diff', revision='tip', f_path='',
511 516 conditions=dict(function=check_repo))
512 517
513 518 rmap.connect('files_rawfile_home',
514 519 '/{repo_name:.*?}/rawfile/{revision}/{f_path:.*}',
515 520 controller='files', action='rawfile', revision='tip',
516 521 f_path='', conditions=dict(function=check_repo))
517 522
518 523 rmap.connect('files_raw_home',
519 524 '/{repo_name:.*?}/raw/{revision}/{f_path:.*}',
520 525 controller='files', action='raw', revision='tip', f_path='',
521 526 conditions=dict(function=check_repo))
522 527
523 528 rmap.connect('files_annotate_home',
524 529 '/{repo_name:.*?}/annotate/{revision}/{f_path:.*}',
525 530 controller='files', action='index', revision='tip',
526 531 f_path='', annotate=True, conditions=dict(function=check_repo))
527 532
528 533 rmap.connect('files_edit_home',
529 534 '/{repo_name:.*?}/edit/{revision}/{f_path:.*}',
530 535 controller='files', action='edit', revision='tip',
531 536 f_path='', conditions=dict(function=check_repo))
532 537
533 538 rmap.connect('files_add_home',
534 539 '/{repo_name:.*?}/add/{revision}/{f_path:.*}',
535 540 controller='files', action='add', revision='tip',
536 541 f_path='', conditions=dict(function=check_repo))
537 542
538 543 rmap.connect('files_archive_home', '/{repo_name:.*?}/archive/{fname}',
539 544 controller='files', action='archivefile',
540 545 conditions=dict(function=check_repo))
541 546
542 547 rmap.connect('files_nodelist_home',
543 548 '/{repo_name:.*?}/nodelist/{revision}/{f_path:.*}',
544 549 controller='files', action='nodelist',
545 550 conditions=dict(function=check_repo))
546 551
547 552 rmap.connect('repo_settings_delete', '/{repo_name:.*?}/settings',
548 553 controller='settings', action="delete",
549 554 conditions=dict(method=["DELETE"], function=check_repo))
550 555
551 556 rmap.connect('repo_settings_update', '/{repo_name:.*?}/settings',
552 557 controller='settings', action="update",
553 558 conditions=dict(method=["PUT"], function=check_repo))
554 559
555 560 rmap.connect('repo_settings_home', '/{repo_name:.*?}/settings',
556 561 controller='settings', action='index',
557 562 conditions=dict(function=check_repo))
558 563
559 564 rmap.connect('repo_fork_create_home', '/{repo_name:.*?}/fork',
560 565 controller='forks', action='fork_create',
561 566 conditions=dict(function=check_repo, method=["POST"]))
562 567
563 568 rmap.connect('repo_fork_home', '/{repo_name:.*?}/fork',
564 569 controller='forks', action='fork',
565 570 conditions=dict(function=check_repo))
566 571
567 572 rmap.connect('repo_forks_home', '/{repo_name:.*?}/forks',
568 573 controller='forks', action='forks',
569 574 conditions=dict(function=check_repo))
570 575
571 576 rmap.connect('repo_followers_home', '/{repo_name:.*?}/followers',
572 577 controller='followers', action='followers',
573 578 conditions=dict(function=check_repo))
574 579
575 580 return rmap
@@ -1,388 +1,402
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.pullrequests
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 pull requests controller for rhodecode for initializing pull requests
7 7
8 8 :created_on: May 7, 2012
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 import logging
26 26 import traceback
27 27 import formencode
28 28
29 29 from webob.exc import HTTPNotFound, HTTPForbidden
30 30 from collections import defaultdict
31 31 from itertools import groupby
32 32
33 33 from pylons import request, response, session, tmpl_context as c, url
34 34 from pylons.controllers.util import abort, redirect
35 35 from pylons.i18n.translation import _
36 36 from pylons.decorators import jsonify
37 37
38 38 from rhodecode.lib.compat import json
39 39 from rhodecode.lib.base import BaseRepoController, render
40 40 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator,\
41 41 NotAnonymous
42 42 from rhodecode.lib import helpers as h
43 43 from rhodecode.lib import diffs
44 44 from rhodecode.lib.utils import action_logger
45 45 from rhodecode.model.db import User, PullRequest, ChangesetStatus,\
46 46 ChangesetComment
47 47 from rhodecode.model.pull_request import PullRequestModel
48 48 from rhodecode.model.meta import Session
49 49 from rhodecode.model.repo import RepoModel
50 50 from rhodecode.model.comment import ChangesetCommentsModel
51 51 from rhodecode.model.changeset_status import ChangesetStatusModel
52 52 from rhodecode.model.forms import PullRequestForm
53 53
54 54 log = logging.getLogger(__name__)
55 55
56 56
57 57 class PullrequestsController(BaseRepoController):
58 58
59 59 @LoginRequired()
60 60 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
61 61 'repository.admin')
62 62 def __before__(self):
63 63 super(PullrequestsController, self).__before__()
64 64 repo_model = RepoModel()
65 65 c.users_array = repo_model.get_users_js()
66 66 c.users_groups_array = repo_model.get_users_groups_js()
67 67
68 68 def _get_repo_refs(self, repo):
69 69 hist_l = []
70 70
71 71 branches_group = ([('branch:%s:%s' % (k, v), k) for
72 72 k, v in repo.branches.iteritems()], _("Branches"))
73 73 bookmarks_group = ([('book:%s:%s' % (k, v), k) for
74 74 k, v in repo.bookmarks.iteritems()], _("Bookmarks"))
75 75 tags_group = ([('tag:%s:%s' % (k, v), k) for
76 76 k, v in repo.tags.iteritems()], _("Tags"))
77 77
78 78 hist_l.append(bookmarks_group)
79 79 hist_l.append(branches_group)
80 80 hist_l.append(tags_group)
81 81
82 82 return hist_l
83 83
84 84 def show_all(self, repo_name):
85 85 c.pull_requests = PullRequestModel().get_all(repo_name)
86 86 c.repo_name = repo_name
87 87 return render('/pullrequests/pullrequest_show_all.html')
88 88
89 89 @NotAnonymous()
90 90 def index(self):
91 91 org_repo = c.rhodecode_db_repo
92 92
93 93 if org_repo.scm_instance.alias != 'hg':
94 94 log.error('Review not available for GIT REPOS')
95 95 raise HTTPNotFound
96 96
97 97 other_repos_info = {}
98 98
99 99 c.org_refs = self._get_repo_refs(c.rhodecode_repo)
100 100 c.org_repos = []
101 101 c.other_repos = []
102 102 c.org_repos.append((org_repo.repo_name, '%s/%s' % (
103 103 org_repo.user.username, c.repo_name))
104 104 )
105 105
106 106 # add org repo to other so we can open pull request agains itself
107 107 c.other_repos.extend(c.org_repos)
108 108
109 109 c.default_pull_request = org_repo.repo_name
110 110 c.default_revs = self._get_repo_refs(org_repo.scm_instance)
111 111 #add orginal repo
112 112 other_repos_info[org_repo.repo_name] = {
113 113 'gravatar': h.gravatar_url(org_repo.user.email, 24),
114 114 'description': org_repo.description,
115 115 'revs': h.select('other_ref', '', c.default_revs, class_='refs')
116 116 }
117 117
118 118 #gather forks and add to this list
119 119 for fork in org_repo.forks:
120 120 c.other_repos.append((fork.repo_name, '%s/%s' % (
121 121 fork.user.username, fork.repo_name))
122 122 )
123 123 other_repos_info[fork.repo_name] = {
124 124 'gravatar': h.gravatar_url(fork.user.email, 24),
125 125 'description': fork.description,
126 126 'revs': h.select('other_ref', '',
127 127 self._get_repo_refs(fork.scm_instance),
128 128 class_='refs')
129 129 }
130 130 #add parents of this fork also
131 131 if org_repo.parent:
132 132 c.default_pull_request = org_repo.parent.repo_name
133 133 c.other_repos.append((org_repo.parent.repo_name, '%s/%s' % (
134 134 org_repo.parent.user.username,
135 135 org_repo.parent.repo_name))
136 136 )
137 137 other_repos_info[org_repo.parent.repo_name] = {
138 138 'gravatar': h.gravatar_url(org_repo.parent.user.email, 24),
139 139 'description': org_repo.parent.description,
140 140 'revs': h.select('other_ref', '',
141 141 self._get_repo_refs(org_repo.parent.scm_instance),
142 142 class_='refs')
143 143 }
144 144
145 145 c.other_repos_info = json.dumps(other_repos_info)
146 146 c.review_members = [org_repo.user]
147 147 return render('/pullrequests/pullrequest.html')
148 148
149 149 @NotAnonymous()
150 150 def create(self, repo_name):
151 151 try:
152 152 _form = PullRequestForm()().to_python(request.POST)
153 153 except formencode.Invalid, errors:
154 154 log.error(traceback.format_exc())
155 155 if errors.error_dict.get('revisions'):
156 156 msg = 'Revisions: %s' % errors.error_dict['revisions']
157 157 elif errors.error_dict.get('pullrequest_title'):
158 158 msg = _('Pull request requires a title with min. 3 chars')
159 159 else:
160 160 msg = _('error during creation of pull request')
161 161
162 162 h.flash(msg, 'error')
163 163 return redirect(url('pullrequest_home', repo_name=repo_name))
164 164
165 165 org_repo = _form['org_repo']
166 166 org_ref = _form['org_ref']
167 167 other_repo = _form['other_repo']
168 168 other_ref = _form['other_ref']
169 169 revisions = _form['revisions']
170 170 reviewers = _form['review_members']
171 171
172 172 title = _form['pullrequest_title']
173 173 description = _form['pullrequest_desc']
174 174
175 175 try:
176 176 pull_request = PullRequestModel().create(
177 177 self.rhodecode_user.user_id, org_repo, org_ref, other_repo,
178 178 other_ref, revisions, reviewers, title, description
179 179 )
180 180 Session().commit()
181 181 h.flash(_('Successfully opened new pull request'),
182 182 category='success')
183 183 except Exception:
184 184 h.flash(_('Error occurred during sending pull request'),
185 185 category='error')
186 186 log.error(traceback.format_exc())
187 187 return redirect(url('pullrequest_home', repo_name=repo_name))
188 188
189 189 return redirect(url('pullrequest_show', repo_name=other_repo,
190 190 pull_request_id=pull_request.pull_request_id))
191 191
192 192 @NotAnonymous()
193 193 @jsonify
194 194 def update(self, repo_name, pull_request_id):
195 195 pull_request = PullRequest.get_or_404(pull_request_id)
196 196 if pull_request.is_closed():
197 197 raise HTTPForbidden()
198 198
199 199 reviewers_ids = map(int, filter(lambda v: v not in [None, ''],
200 200 request.POST.get('reviewers_ids', '').split(',')))
201 201
202 202 PullRequestModel().update_reviewers(pull_request_id, reviewers_ids)
203 203 Session.commit()
204 204 return True
205 205
206 @NotAnonymous()
207 @jsonify
208 def delete(self, repo_name, pull_request_id):
209 pull_request = PullRequest.get_or_404(pull_request_id)
210 #only owner can delete it !
211 if pull_request.author.user_id == c.rhodecode_user.user_id:
212 PullRequestModel().delete(pull_request)
213 Session().commit()
214 h.flash(_('Successfully deleted pull request'),
215 category='success')
216 return redirect(url('admin_settings_my_account'))
217 else:
218 raise HTTPForbidden()
219
206 220 def _load_compare_data(self, pull_request, enable_comments=True):
207 221 """
208 222 Load context data needed for generating compare diff
209 223
210 224 :param pull_request:
211 225 :type pull_request:
212 226 """
213 227
214 228 org_repo = pull_request.org_repo
215 229 (org_ref_type,
216 230 org_ref_name,
217 231 org_ref_rev) = pull_request.org_ref.split(':')
218 232
219 233 other_repo = pull_request.other_repo
220 234 (other_ref_type,
221 235 other_ref_name,
222 236 other_ref_rev) = pull_request.other_ref.split(':')
223 237
224 238 # despite opening revisions for bookmarks/branches/tags, we always
225 239 # convert this to rev to prevent changes after book or branch change
226 240 org_ref = ('rev', org_ref_rev)
227 241 other_ref = ('rev', other_ref_rev)
228 242
229 243 c.org_repo = org_repo
230 244 c.other_repo = other_repo
231 245
232 246 c.cs_ranges, discovery_data = PullRequestModel().get_compare_data(
233 247 org_repo, org_ref, other_repo, other_ref
234 248 )
235 249
236 250 c.statuses = c.rhodecode_db_repo.statuses([x.raw_id for x in
237 251 c.cs_ranges])
238 252 # defines that we need hidden inputs with changesets
239 253 c.as_form = request.GET.get('as_form', False)
240 254
241 255 c.org_ref = org_ref[1]
242 256 c.other_ref = other_ref[1]
243 257 # diff needs to have swapped org with other to generate proper diff
244 258 _diff = diffs.differ(other_repo, other_ref, org_repo, org_ref,
245 259 discovery_data)
246 260 diff_processor = diffs.DiffProcessor(_diff, format='gitdiff')
247 261 _parsed = diff_processor.prepare()
248 262
249 263 c.files = []
250 264 c.changes = {}
251 265
252 266 for f in _parsed:
253 267 fid = h.FID('', f['filename'])
254 268 c.files.append([fid, f['operation'], f['filename'], f['stats']])
255 269 diff = diff_processor.as_html(enable_comments=enable_comments,
256 270 diff_lines=[f])
257 271 c.changes[fid] = [f['operation'], f['filename'], diff]
258 272
259 273 def show(self, repo_name, pull_request_id):
260 274 repo_model = RepoModel()
261 275 c.users_array = repo_model.get_users_js()
262 276 c.users_groups_array = repo_model.get_users_groups_js()
263 277 c.pull_request = PullRequest.get_or_404(pull_request_id)
264 278
265 279 cc_model = ChangesetCommentsModel()
266 280 cs_model = ChangesetStatusModel()
267 281 _cs_statuses = cs_model.get_statuses(c.pull_request.org_repo,
268 282 pull_request=c.pull_request,
269 283 with_revisions=True)
270 284
271 285 cs_statuses = defaultdict(list)
272 286 for st in _cs_statuses:
273 287 cs_statuses[st.author.username] += [st]
274 288
275 289 c.pull_request_reviewers = []
276 290 c.pull_request_pending_reviewers = []
277 291 for o in c.pull_request.reviewers:
278 292 st = cs_statuses.get(o.user.username, None)
279 293 if st:
280 294 sorter = lambda k: k.version
281 295 st = [(x, list(y)[0])
282 296 for x, y in (groupby(sorted(st, key=sorter), sorter))]
283 297 else:
284 298 c.pull_request_pending_reviewers.append(o.user)
285 299 c.pull_request_reviewers.append([o.user, st])
286 300
287 301 # pull_requests repo_name we opened it against
288 302 # ie. other_repo must match
289 303 if repo_name != c.pull_request.other_repo.repo_name:
290 304 raise HTTPNotFound
291 305
292 306 # load compare data into template context
293 307 enable_comments = not c.pull_request.is_closed()
294 308 self._load_compare_data(c.pull_request, enable_comments=enable_comments)
295 309
296 310 # inline comments
297 311 c.inline_cnt = 0
298 312 c.inline_comments = cc_model.get_inline_comments(
299 313 c.rhodecode_db_repo.repo_id,
300 314 pull_request=pull_request_id)
301 315 # count inline comments
302 316 for __, lines in c.inline_comments:
303 317 for comments in lines.values():
304 318 c.inline_cnt += len(comments)
305 319 # comments
306 320 c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id,
307 321 pull_request=pull_request_id)
308 322
309 323 # changeset(pull-request) status
310 324 c.current_changeset_status = cs_model.calculate_status(
311 325 c.pull_request_reviewers
312 326 )
313 327 c.changeset_statuses = ChangesetStatus.STATUSES
314 328 c.target_repo = c.pull_request.org_repo.repo_name
315 329 return render('/pullrequests/pullrequest_show.html')
316 330
317 331 @NotAnonymous()
318 332 @jsonify
319 333 def comment(self, repo_name, pull_request_id):
320 334 pull_request = PullRequest.get_or_404(pull_request_id)
321 335 if pull_request.is_closed():
322 336 raise HTTPForbidden()
323 337
324 338 status = request.POST.get('changeset_status')
325 339 change_status = request.POST.get('change_changeset_status')
326 340
327 341 comm = ChangesetCommentsModel().create(
328 342 text=request.POST.get('text'),
329 343 repo=c.rhodecode_db_repo.repo_id,
330 344 user=c.rhodecode_user.user_id,
331 345 pull_request=pull_request_id,
332 346 f_path=request.POST.get('f_path'),
333 347 line_no=request.POST.get('line'),
334 348 status_change=(ChangesetStatus.get_status_lbl(status)
335 349 if status and change_status else None)
336 350 )
337 351
338 352 # get status if set !
339 353 if status and change_status:
340 354 ChangesetStatusModel().set_status(
341 355 c.rhodecode_db_repo.repo_id,
342 356 status,
343 357 c.rhodecode_user.user_id,
344 358 comm,
345 359 pull_request=pull_request_id
346 360 )
347 361 action_logger(self.rhodecode_user,
348 362 'user_commented_pull_request:%s' % pull_request_id,
349 363 c.rhodecode_db_repo, self.ip_addr, self.sa)
350 364
351 365 if request.POST.get('save_close'):
352 366 PullRequestModel().close_pull_request(pull_request_id)
353 367 action_logger(self.rhodecode_user,
354 368 'user_closed_pull_request:%s' % pull_request_id,
355 369 c.rhodecode_db_repo, self.ip_addr, self.sa)
356 370
357 371 Session().commit()
358 372
359 373 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
360 374 return redirect(h.url('pullrequest_show', repo_name=repo_name,
361 375 pull_request_id=pull_request_id))
362 376
363 377 data = {
364 378 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
365 379 }
366 380 if comm:
367 381 c.co = comm
368 382 data.update(comm.get_dict())
369 383 data.update({'rendered_text':
370 384 render('changeset/changeset_comment_block.html')})
371 385
372 386 return data
373 387
374 388 @NotAnonymous()
375 389 @jsonify
376 390 def delete_comment(self, repo_name, comment_id):
377 391 co = ChangesetComment.get(comment_id)
378 392 if co.pull_request.is_closed():
379 393 #don't allow deleting comments on closed pull request
380 394 raise HTTPForbidden()
381 395
382 396 owner = lambda: co.author.user_id == c.rhodecode_user.user_id
383 397 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
384 398 ChangesetCommentsModel().delete(comment=co)
385 399 Session().commit()
386 400 return True
387 401 else:
388 402 raise HTTPForbidden()
@@ -1,245 +1,249
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.pull_request
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 pull request model for RhodeCode
7 7
8 8 :created_on: Jun 6, 2012
9 9 :author: marcink
10 10 :copyright: (C) 2012-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27 import binascii
28 28 import datetime
29 29
30 30 from pylons.i18n.translation import _
31 31
32 32 from rhodecode.model.meta import Session
33 33 from rhodecode.lib import helpers as h
34 34 from rhodecode.model import BaseModel
35 35 from rhodecode.model.db import PullRequest, PullRequestReviewers, Notification
36 36 from rhodecode.model.notification import NotificationModel
37 37 from rhodecode.lib.utils2 import safe_unicode
38 38
39 39 from rhodecode.lib.vcs.utils.hgcompat import discovery, localrepo, scmutil
40 40
41 41 log = logging.getLogger(__name__)
42 42
43 43
44 44 class PullRequestModel(BaseModel):
45 45
46 46 cls = PullRequest
47 47
48 48 def __get_pull_request(self, pull_request):
49 49 return self._get_instance(PullRequest, pull_request)
50 50
51 51 def get_all(self, repo):
52 52 repo = self._get_repo(repo)
53 53 return PullRequest.query().filter(PullRequest.other_repo == repo).all()
54 54
55 55 def create(self, created_by, org_repo, org_ref, other_repo,
56 56 other_ref, revisions, reviewers, title, description=None):
57 57
58 58 created_by_user = self._get_user(created_by)
59 59 org_repo = self._get_repo(org_repo)
60 60 other_repo = self._get_repo(other_repo)
61 61
62 62 new = PullRequest()
63 63 new.org_repo = org_repo
64 64 new.org_ref = org_ref
65 65 new.other_repo = other_repo
66 66 new.other_ref = other_ref
67 67 new.revisions = revisions
68 68 new.title = title
69 69 new.description = description
70 70 new.author = created_by_user
71 71 self.sa.add(new)
72 72 Session().flush()
73 73 #members
74 74 for member in reviewers:
75 75 _usr = self._get_user(member)
76 76 reviewer = PullRequestReviewers(_usr, new)
77 77 self.sa.add(reviewer)
78 78
79 79 #notification to reviewers
80 80 notif = NotificationModel()
81 81
82 82 subject = safe_unicode(
83 83 h.link_to(
84 84 _('%(user)s wants you to review pull request #%(pr_id)s') % \
85 85 {'user': created_by_user.username,
86 86 'pr_id': new.pull_request_id},
87 87 h.url('pullrequest_show', repo_name=other_repo.repo_name,
88 88 pull_request_id=new.pull_request_id,
89 89 qualified=True,
90 90 )
91 91 )
92 92 )
93 93 body = description
94 94 notif.create(created_by=created_by_user, subject=subject, body=body,
95 95 recipients=reviewers,
96 96 type_=Notification.TYPE_PULL_REQUEST,)
97 97
98 98 return new
99 99
100 100 def update_reviewers(self, pull_request, reviewers_ids):
101 101 reviewers_ids = set(reviewers_ids)
102 102 pull_request = self.__get_pull_request(pull_request)
103 103 current_reviewers = PullRequestReviewers.query()\
104 104 .filter(PullRequestReviewers.pull_request==
105 105 pull_request)\
106 106 .all()
107 107 current_reviewers_ids = set([x.user.user_id for x in current_reviewers])
108 108
109 109 to_add = reviewers_ids.difference(current_reviewers_ids)
110 110 to_remove = current_reviewers_ids.difference(reviewers_ids)
111 111
112 112 log.debug("Adding %s reviewers" % to_add)
113 113 log.debug("Removing %s reviewers" % to_remove)
114 114
115 115 for uid in to_add:
116 116 _usr = self._get_user(uid)
117 117 reviewer = PullRequestReviewers(_usr, pull_request)
118 118 self.sa.add(reviewer)
119 119
120 120 for uid in to_remove:
121 121 reviewer = PullRequestReviewers.query()\
122 122 .filter(PullRequestReviewers.user_id==uid,
123 123 PullRequestReviewers.pull_request==pull_request)\
124 124 .scalar()
125 125 if reviewer:
126 126 self.sa.delete(reviewer)
127 127
128 def delete(self, pull_request):
129 pull_request = self.__get_pull_request(pull_request)
130 Session().delete(pull_request)
131
128 132 def close_pull_request(self, pull_request):
129 133 pull_request = self.__get_pull_request(pull_request)
130 134 pull_request.status = PullRequest.STATUS_CLOSED
131 135 pull_request.updated_on = datetime.datetime.now()
132 136 self.sa.add(pull_request)
133 137
134 138 def _get_changesets(self, org_repo, org_ref, other_repo, other_ref,
135 139 discovery_data):
136 140 """
137 141 Returns a list of changesets that are incoming from org_repo@org_ref
138 142 to other_repo@other_ref
139 143
140 144 :param org_repo:
141 145 :type org_repo:
142 146 :param org_ref:
143 147 :type org_ref:
144 148 :param other_repo:
145 149 :type other_repo:
146 150 :param other_ref:
147 151 :type other_ref:
148 152 :param tmp:
149 153 :type tmp:
150 154 """
151 155 changesets = []
152 156 #case two independent repos
153 157 common, incoming, rheads = discovery_data
154 158 if org_repo != other_repo and incoming:
155 159 revs = org_repo._repo.changelog.findmissing(common, rheads)
156 160
157 161 for cs in reversed(map(binascii.hexlify, revs)):
158 162 changesets.append(org_repo.get_changeset(cs))
159 163 else:
160 164 _revset_predicates = {
161 165 'branch': 'branch',
162 166 'book': 'bookmark',
163 167 'tag': 'tag',
164 168 'rev': 'id',
165 169 }
166 170
167 171 revs = [
168 172 "ancestors(%s('%s')) and not ancestors(%s('%s'))" % (
169 173 _revset_predicates[org_ref[0]], org_ref[1],
170 174 _revset_predicates[other_ref[0]], other_ref[1]
171 175 )
172 176 ]
173 177
174 178 out = scmutil.revrange(org_repo._repo, revs)
175 179 for cs in reversed(out):
176 180 changesets.append(org_repo.get_changeset(cs))
177 181
178 182 return changesets
179 183
180 184 def _get_discovery(self, org_repo, org_ref, other_repo, other_ref):
181 185 """
182 186 Get's mercurial discovery data used to calculate difference between
183 187 repos and refs
184 188
185 189 :param org_repo:
186 190 :type org_repo:
187 191 :param org_ref:
188 192 :type org_ref:
189 193 :param other_repo:
190 194 :type other_repo:
191 195 :param other_ref:
192 196 :type other_ref:
193 197 """
194 198
195 199 _org_repo = org_repo._repo
196 200 org_rev_type, org_rev = org_ref
197 201
198 202 _other_repo = other_repo._repo
199 203 other_rev_type, other_rev = other_ref
200 204
201 205 log.debug('Doing discovery for %s@%s vs %s@%s' % (
202 206 org_repo, org_ref, other_repo, other_ref)
203 207 )
204 208 #log.debug('Filter heads are %s[%s]' % ('', org_ref[1]))
205 209 org_peer = localrepo.locallegacypeer(_org_repo.local())
206 210 tmp = discovery.findcommonincoming(
207 211 repo=_other_repo, # other_repo we check for incoming
208 212 remote=org_peer, # org_repo source for incoming
209 213 heads=[_other_repo[other_rev].node(),
210 214 _org_repo[org_rev].node()],
211 215 force=False
212 216 )
213 217 return tmp
214 218
215 219 def get_compare_data(self, org_repo, org_ref, other_repo, other_ref):
216 220 """
217 221 Returns a tuple of incomming changesets, and discoverydata cache
218 222
219 223 :param org_repo:
220 224 :type org_repo:
221 225 :param org_ref:
222 226 :type org_ref:
223 227 :param other_repo:
224 228 :type other_repo:
225 229 :param other_ref:
226 230 :type other_ref:
227 231 """
228 232
229 233 if len(org_ref) != 2 or not isinstance(org_ref, (list, tuple)):
230 234 raise Exception('org_ref must be a two element list/tuple')
231 235
232 236 if len(other_ref) != 2 or not isinstance(org_ref, (list, tuple)):
233 237 raise Exception('other_ref must be a two element list/tuple')
234 238
235 239 discovery_data = self._get_discovery(org_repo.scm_instance,
236 240 org_ref,
237 241 other_repo.scm_instance,
238 242 other_ref)
239 243 cs_ranges = self._get_changesets(org_repo.scm_instance,
240 244 org_ref,
241 245 other_repo.scm_instance,
242 246 other_ref,
243 247 discovery_data)
244 248
245 249 return cs_ranges, discovery_data
@@ -1,30 +1,37
1 1
2 2 <div class="pullrequests_section_head">${_('Opened by me')}</div>
3 3 <ul>
4 4 %if c.my_pull_requests:
5 5 %for pull_request in c.my_pull_requests:
6 6 <li>
7 <div style="float:left">
7 8 <a href="${h.url('pullrequest_show',repo_name=pull_request.other_repo.repo_name,pull_request_id=pull_request.pull_request_id)}">
8 9 ${_('Pull request #%s opened on %s') % (pull_request.pull_request_id, h.fmt_date(pull_request.created_on))}
9 10 </a>
11 </div>
12 <div style="float:left;margin-top: -5px">
13 ${h.form(url('pullrequest_delete', repo_name=pull_request.other_repo.repo_name, pull_request_id=pull_request.pull_request_id),method='delete')}
14 ${h.submit('remove_%s' % pull_request.pull_request_id,'',class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this pull request')+"');")}
15 ${h.end_form()}
16 </div>
10 17 </li>
11 18 %endfor
12 19 %else:
13 20 <li><span class="empty_data">${_('Nothing here yet')}</span></li>
14 21 %endif
15 22 </ul>
16 23
17 <div class="pullrequests_section_head">${_('I participate in')}</div>
24 <div class="pullrequests_section_head" style="clear:both">${_('I participate in')}</div>
18 25 <ul>
19 26 %if c.my_pull_requests:
20 27 %for pull_request in c.participate_in_pull_requests:
21 28 <li>
22 29 <a href="${h.url('pullrequest_show',repo_name=pull_request.other_repo.repo_name,pull_request_id=pull_request.pull_request_id)}">
23 30 ${_('Pull request #%s opened by %s on %s') % (pull_request.pull_request_id, pull_request.author.full_name, h.fmt_date(pull_request.created_on))}
24 31 </a>
25 32 </li>
26 33 %endfor
27 34 %else:
28 35 <li><span class="empty_data">${_('Nothing here yet')}</span></li>
29 36 %endif
30 37 </ul>
General Comments 0
You need to be logged in to leave comments. Login now