##// END OF EJS Templates
channelstream: added testing panel for live-notifications.
marcink -
r1274:2537ec98 default
parent child Browse files
Show More
@@ -1,1169 +1,1173 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 Routes configuration
23 23
24 24 The more specific and detailed routes should be defined first so they
25 25 may take precedent over the more generic routes. For more information
26 26 refer to the routes manual at http://routes.groovie.org/docs/
27 27
28 28 IMPORTANT: if you change any routing here, make sure to take a look at lib/base.py
29 29 and _route_name variable which uses some of stored naming here to do redirects.
30 30 """
31 31 import os
32 32 import re
33 33 from routes import Mapper
34 34
35 35 from rhodecode.config import routing_links
36 36
37 37 # prefix for non repository related links needs to be prefixed with `/`
38 38 ADMIN_PREFIX = '/_admin'
39 39 STATIC_FILE_PREFIX = '/_static'
40 40
41 41 # Default requirements for URL parts
42 42 URL_NAME_REQUIREMENTS = {
43 43 # group name can have a slash in them, but they must not end with a slash
44 44 'group_name': r'.*?[^/]',
45 45 'repo_group_name': r'.*?[^/]',
46 46 # repo names can have a slash in them, but they must not end with a slash
47 47 'repo_name': r'.*?[^/]',
48 48 # file path eats up everything at the end
49 49 'f_path': r'.*',
50 50 # reference types
51 51 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
52 52 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
53 53 }
54 54
55 55
56 56 def add_route_requirements(route_path, requirements):
57 57 """
58 58 Adds regex requirements to pyramid routes using a mapping dict
59 59
60 60 >>> add_route_requirements('/{action}/{id}', {'id': r'\d+'})
61 61 '/{action}/{id:\d+}'
62 62
63 63 """
64 64 for key, regex in requirements.items():
65 65 route_path = route_path.replace('{%s}' % key, '{%s:%s}' % (key, regex))
66 66 return route_path
67 67
68 68
69 69 class JSRoutesMapper(Mapper):
70 70 """
71 71 Wrapper for routes.Mapper to make pyroutes compatible url definitions
72 72 """
73 73 _named_route_regex = re.compile(r'^[a-z-_0-9A-Z]+$')
74 74 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
75 75 def __init__(self, *args, **kw):
76 76 super(JSRoutesMapper, self).__init__(*args, **kw)
77 77 self._jsroutes = []
78 78
79 79 def connect(self, *args, **kw):
80 80 """
81 81 Wrapper for connect to take an extra argument jsroute=True
82 82
83 83 :param jsroute: boolean, if True will add the route to the pyroutes list
84 84 """
85 85 if kw.pop('jsroute', False):
86 86 if not self._named_route_regex.match(args[0]):
87 87 raise Exception('only named routes can be added to pyroutes')
88 88 self._jsroutes.append(args[0])
89 89
90 90 super(JSRoutesMapper, self).connect(*args, **kw)
91 91
92 92 def _extract_route_information(self, route):
93 93 """
94 94 Convert a route into tuple(name, path, args), eg:
95 95 ('user_profile', '/profile/%(username)s', ['username'])
96 96 """
97 97 routepath = route.routepath
98 98 def replace(matchobj):
99 99 if matchobj.group(1):
100 100 return "%%(%s)s" % matchobj.group(1).split(':')[0]
101 101 else:
102 102 return "%%(%s)s" % matchobj.group(2)
103 103
104 104 routepath = self._argument_prog.sub(replace, routepath)
105 105 return (
106 106 route.name,
107 107 routepath,
108 108 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
109 109 for arg in self._argument_prog.findall(route.routepath)]
110 110 )
111 111
112 112 def jsroutes(self):
113 113 """
114 114 Return a list of pyroutes.js compatible routes
115 115 """
116 116 for route_name in self._jsroutes:
117 117 yield self._extract_route_information(self._routenames[route_name])
118 118
119 119
120 120 def make_map(config):
121 121 """Create, configure and return the routes Mapper"""
122 122 rmap = JSRoutesMapper(directory=config['pylons.paths']['controllers'],
123 123 always_scan=config['debug'])
124 124 rmap.minimization = False
125 125 rmap.explicit = False
126 126
127 127 from rhodecode.lib.utils2 import str2bool
128 128 from rhodecode.model import repo, repo_group
129 129
130 130 def check_repo(environ, match_dict):
131 131 """
132 132 check for valid repository for proper 404 handling
133 133
134 134 :param environ:
135 135 :param match_dict:
136 136 """
137 137 repo_name = match_dict.get('repo_name')
138 138
139 139 if match_dict.get('f_path'):
140 140 # fix for multiple initial slashes that causes errors
141 141 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
142 142 repo_model = repo.RepoModel()
143 143 by_name_match = repo_model.get_by_repo_name(repo_name)
144 144 # if we match quickly from database, short circuit the operation,
145 145 # and validate repo based on the type.
146 146 if by_name_match:
147 147 return True
148 148
149 149 by_id_match = repo_model.get_repo_by_id(repo_name)
150 150 if by_id_match:
151 151 repo_name = by_id_match.repo_name
152 152 match_dict['repo_name'] = repo_name
153 153 return True
154 154
155 155 return False
156 156
157 157 def check_group(environ, match_dict):
158 158 """
159 159 check for valid repository group path for proper 404 handling
160 160
161 161 :param environ:
162 162 :param match_dict:
163 163 """
164 164 repo_group_name = match_dict.get('group_name')
165 165 repo_group_model = repo_group.RepoGroupModel()
166 166 by_name_match = repo_group_model.get_by_group_name(repo_group_name)
167 167 if by_name_match:
168 168 return True
169 169
170 170 return False
171 171
172 172 def check_user_group(environ, match_dict):
173 173 """
174 174 check for valid user group for proper 404 handling
175 175
176 176 :param environ:
177 177 :param match_dict:
178 178 """
179 179 return True
180 180
181 181 def check_int(environ, match_dict):
182 182 return match_dict.get('id').isdigit()
183 183
184 184
185 185 #==========================================================================
186 186 # CUSTOM ROUTES HERE
187 187 #==========================================================================
188 188
189 189 # MAIN PAGE
190 190 rmap.connect('home', '/', controller='home', action='index', jsroute=True)
191 191 rmap.connect('goto_switcher_data', '/_goto_data', controller='home',
192 192 action='goto_switcher_data')
193 193 rmap.connect('repo_list_data', '/_repos', controller='home',
194 194 action='repo_list_data')
195 195
196 196 rmap.connect('user_autocomplete_data', '/_users', controller='home',
197 197 action='user_autocomplete_data', jsroute=True)
198 198 rmap.connect('user_group_autocomplete_data', '/_user_groups', controller='home',
199 199 action='user_group_autocomplete_data', jsroute=True)
200 200
201 201 rmap.connect(
202 202 'user_profile', '/_profiles/{username}', controller='users',
203 203 action='user_profile')
204 204
205 205 # TODO: johbo: Static links, to be replaced by our redirection mechanism
206 206 rmap.connect('rst_help',
207 207 'http://docutils.sourceforge.net/docs/user/rst/quickref.html',
208 208 _static=True)
209 209 rmap.connect('markdown_help',
210 210 'http://daringfireball.net/projects/markdown/syntax',
211 211 _static=True)
212 212 rmap.connect('rhodecode_official', 'https://rhodecode.com', _static=True)
213 213 rmap.connect('rhodecode_support', 'https://rhodecode.com/help/', _static=True)
214 214 rmap.connect('rhodecode_translations', 'https://rhodecode.com/translate/enterprise', _static=True)
215 215 # TODO: anderson - making this a static link since redirect won't play
216 216 # nice with POST requests
217 217 rmap.connect('enterprise_license_convert_from_old',
218 218 'https://rhodecode.com/u/license-upgrade',
219 219 _static=True)
220 220
221 221 routing_links.connect_redirection_links(rmap)
222 222
223 223 rmap.connect('ping', '%s/ping' % (ADMIN_PREFIX,), controller='home', action='ping')
224 224 rmap.connect('error_test', '%s/error_test' % (ADMIN_PREFIX,), controller='home', action='error_test')
225 225
226 226 # ADMIN REPOSITORY ROUTES
227 227 with rmap.submapper(path_prefix=ADMIN_PREFIX,
228 228 controller='admin/repos') as m:
229 229 m.connect('repos', '/repos',
230 230 action='create', conditions={'method': ['POST']})
231 231 m.connect('repos', '/repos',
232 232 action='index', conditions={'method': ['GET']})
233 233 m.connect('new_repo', '/create_repository', jsroute=True,
234 234 action='create_repository', conditions={'method': ['GET']})
235 235 m.connect('/repos/{repo_name}',
236 236 action='update', conditions={'method': ['PUT'],
237 237 'function': check_repo},
238 238 requirements=URL_NAME_REQUIREMENTS)
239 239 m.connect('delete_repo', '/repos/{repo_name}',
240 240 action='delete', conditions={'method': ['DELETE']},
241 241 requirements=URL_NAME_REQUIREMENTS)
242 242 m.connect('repo', '/repos/{repo_name}',
243 243 action='show', conditions={'method': ['GET'],
244 244 'function': check_repo},
245 245 requirements=URL_NAME_REQUIREMENTS)
246 246
247 247 # ADMIN REPOSITORY GROUPS ROUTES
248 248 with rmap.submapper(path_prefix=ADMIN_PREFIX,
249 249 controller='admin/repo_groups') as m:
250 250 m.connect('repo_groups', '/repo_groups',
251 251 action='create', conditions={'method': ['POST']})
252 252 m.connect('repo_groups', '/repo_groups',
253 253 action='index', conditions={'method': ['GET']})
254 254 m.connect('new_repo_group', '/repo_groups/new',
255 255 action='new', conditions={'method': ['GET']})
256 256 m.connect('update_repo_group', '/repo_groups/{group_name}',
257 257 action='update', conditions={'method': ['PUT'],
258 258 'function': check_group},
259 259 requirements=URL_NAME_REQUIREMENTS)
260 260
261 261 # EXTRAS REPO GROUP ROUTES
262 262 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
263 263 action='edit',
264 264 conditions={'method': ['GET'], 'function': check_group},
265 265 requirements=URL_NAME_REQUIREMENTS)
266 266 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
267 267 action='edit',
268 268 conditions={'method': ['PUT'], 'function': check_group},
269 269 requirements=URL_NAME_REQUIREMENTS)
270 270
271 271 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
272 272 action='edit_repo_group_advanced',
273 273 conditions={'method': ['GET'], 'function': check_group},
274 274 requirements=URL_NAME_REQUIREMENTS)
275 275 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
276 276 action='edit_repo_group_advanced',
277 277 conditions={'method': ['PUT'], 'function': check_group},
278 278 requirements=URL_NAME_REQUIREMENTS)
279 279
280 280 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
281 281 action='edit_repo_group_perms',
282 282 conditions={'method': ['GET'], 'function': check_group},
283 283 requirements=URL_NAME_REQUIREMENTS)
284 284 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
285 285 action='update_perms',
286 286 conditions={'method': ['PUT'], 'function': check_group},
287 287 requirements=URL_NAME_REQUIREMENTS)
288 288
289 289 m.connect('delete_repo_group', '/repo_groups/{group_name}',
290 290 action='delete', conditions={'method': ['DELETE'],
291 291 'function': check_group},
292 292 requirements=URL_NAME_REQUIREMENTS)
293 293
294 294 # ADMIN USER ROUTES
295 295 with rmap.submapper(path_prefix=ADMIN_PREFIX,
296 296 controller='admin/users') as m:
297 297 m.connect('users', '/users',
298 298 action='create', conditions={'method': ['POST']})
299 299 m.connect('users', '/users',
300 300 action='index', conditions={'method': ['GET']})
301 301 m.connect('new_user', '/users/new',
302 302 action='new', conditions={'method': ['GET']})
303 303 m.connect('update_user', '/users/{user_id}',
304 304 action='update', conditions={'method': ['PUT']})
305 305 m.connect('delete_user', '/users/{user_id}',
306 306 action='delete', conditions={'method': ['DELETE']})
307 307 m.connect('edit_user', '/users/{user_id}/edit',
308 308 action='edit', conditions={'method': ['GET']}, jsroute=True)
309 309 m.connect('user', '/users/{user_id}',
310 310 action='show', conditions={'method': ['GET']})
311 311 m.connect('force_password_reset_user', '/users/{user_id}/password_reset',
312 312 action='reset_password', conditions={'method': ['POST']})
313 313 m.connect('create_personal_repo_group', '/users/{user_id}/create_repo_group',
314 314 action='create_personal_repo_group', conditions={'method': ['POST']})
315 315
316 316 # EXTRAS USER ROUTES
317 317 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
318 318 action='edit_advanced', conditions={'method': ['GET']})
319 319 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
320 320 action='update_advanced', conditions={'method': ['PUT']})
321 321
322 322 m.connect('edit_user_auth_tokens', '/users/{user_id}/edit/auth_tokens',
323 323 action='edit_auth_tokens', conditions={'method': ['GET']})
324 324 m.connect('edit_user_auth_tokens', '/users/{user_id}/edit/auth_tokens',
325 325 action='add_auth_token', conditions={'method': ['PUT']})
326 326 m.connect('edit_user_auth_tokens', '/users/{user_id}/edit/auth_tokens',
327 327 action='delete_auth_token', conditions={'method': ['DELETE']})
328 328
329 329 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
330 330 action='edit_global_perms', conditions={'method': ['GET']})
331 331 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
332 332 action='update_global_perms', conditions={'method': ['PUT']})
333 333
334 334 m.connect('edit_user_perms_summary', '/users/{user_id}/edit/permissions_summary',
335 335 action='edit_perms_summary', conditions={'method': ['GET']})
336 336
337 337 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
338 338 action='edit_emails', conditions={'method': ['GET']})
339 339 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
340 340 action='add_email', conditions={'method': ['PUT']})
341 341 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
342 342 action='delete_email', conditions={'method': ['DELETE']})
343 343
344 344 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
345 345 action='edit_ips', conditions={'method': ['GET']})
346 346 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
347 347 action='add_ip', conditions={'method': ['PUT']})
348 348 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
349 349 action='delete_ip', conditions={'method': ['DELETE']})
350 350
351 351 # ADMIN USER GROUPS REST ROUTES
352 352 with rmap.submapper(path_prefix=ADMIN_PREFIX,
353 353 controller='admin/user_groups') as m:
354 354 m.connect('users_groups', '/user_groups',
355 355 action='create', conditions={'method': ['POST']})
356 356 m.connect('users_groups', '/user_groups',
357 357 action='index', conditions={'method': ['GET']})
358 358 m.connect('new_users_group', '/user_groups/new',
359 359 action='new', conditions={'method': ['GET']})
360 360 m.connect('update_users_group', '/user_groups/{user_group_id}',
361 361 action='update', conditions={'method': ['PUT']})
362 362 m.connect('delete_users_group', '/user_groups/{user_group_id}',
363 363 action='delete', conditions={'method': ['DELETE']})
364 364 m.connect('edit_users_group', '/user_groups/{user_group_id}/edit',
365 365 action='edit', conditions={'method': ['GET']},
366 366 function=check_user_group)
367 367
368 368 # EXTRAS USER GROUP ROUTES
369 369 m.connect('edit_user_group_global_perms',
370 370 '/user_groups/{user_group_id}/edit/global_permissions',
371 371 action='edit_global_perms', conditions={'method': ['GET']})
372 372 m.connect('edit_user_group_global_perms',
373 373 '/user_groups/{user_group_id}/edit/global_permissions',
374 374 action='update_global_perms', conditions={'method': ['PUT']})
375 375 m.connect('edit_user_group_perms_summary',
376 376 '/user_groups/{user_group_id}/edit/permissions_summary',
377 377 action='edit_perms_summary', conditions={'method': ['GET']})
378 378
379 379 m.connect('edit_user_group_perms',
380 380 '/user_groups/{user_group_id}/edit/permissions',
381 381 action='edit_perms', conditions={'method': ['GET']})
382 382 m.connect('edit_user_group_perms',
383 383 '/user_groups/{user_group_id}/edit/permissions',
384 384 action='update_perms', conditions={'method': ['PUT']})
385 385
386 386 m.connect('edit_user_group_advanced',
387 387 '/user_groups/{user_group_id}/edit/advanced',
388 388 action='edit_advanced', conditions={'method': ['GET']})
389 389
390 390 m.connect('edit_user_group_members',
391 391 '/user_groups/{user_group_id}/edit/members', jsroute=True,
392 392 action='user_group_members', conditions={'method': ['GET']})
393 393
394 394 # ADMIN PERMISSIONS ROUTES
395 395 with rmap.submapper(path_prefix=ADMIN_PREFIX,
396 396 controller='admin/permissions') as m:
397 397 m.connect('admin_permissions_application', '/permissions/application',
398 398 action='permission_application_update', conditions={'method': ['POST']})
399 399 m.connect('admin_permissions_application', '/permissions/application',
400 400 action='permission_application', conditions={'method': ['GET']})
401 401
402 402 m.connect('admin_permissions_global', '/permissions/global',
403 403 action='permission_global_update', conditions={'method': ['POST']})
404 404 m.connect('admin_permissions_global', '/permissions/global',
405 405 action='permission_global', conditions={'method': ['GET']})
406 406
407 407 m.connect('admin_permissions_object', '/permissions/object',
408 408 action='permission_objects_update', conditions={'method': ['POST']})
409 409 m.connect('admin_permissions_object', '/permissions/object',
410 410 action='permission_objects', conditions={'method': ['GET']})
411 411
412 412 m.connect('admin_permissions_ips', '/permissions/ips',
413 413 action='permission_ips', conditions={'method': ['POST']})
414 414 m.connect('admin_permissions_ips', '/permissions/ips',
415 415 action='permission_ips', conditions={'method': ['GET']})
416 416
417 417 m.connect('admin_permissions_overview', '/permissions/overview',
418 418 action='permission_perms', conditions={'method': ['GET']})
419 419
420 420 # ADMIN DEFAULTS REST ROUTES
421 421 with rmap.submapper(path_prefix=ADMIN_PREFIX,
422 422 controller='admin/defaults') as m:
423 423 m.connect('admin_defaults_repositories', '/defaults/repositories',
424 424 action='update_repository_defaults', conditions={'method': ['POST']})
425 425 m.connect('admin_defaults_repositories', '/defaults/repositories',
426 426 action='index', conditions={'method': ['GET']})
427 427
428 428 # ADMIN DEBUG STYLE ROUTES
429 429 if str2bool(config.get('debug_style')):
430 430 with rmap.submapper(path_prefix=ADMIN_PREFIX + '/debug_style',
431 431 controller='debug_style') as m:
432 432 m.connect('debug_style_home', '',
433 433 action='index', conditions={'method': ['GET']})
434 434 m.connect('debug_style_template', '/t/{t_path}',
435 435 action='template', conditions={'method': ['GET']})
436 436
437 437 # ADMIN SETTINGS ROUTES
438 438 with rmap.submapper(path_prefix=ADMIN_PREFIX,
439 439 controller='admin/settings') as m:
440 440
441 441 # default
442 442 m.connect('admin_settings', '/settings',
443 443 action='settings_global_update',
444 444 conditions={'method': ['POST']})
445 445 m.connect('admin_settings', '/settings',
446 446 action='settings_global', conditions={'method': ['GET']})
447 447
448 448 m.connect('admin_settings_vcs', '/settings/vcs',
449 449 action='settings_vcs_update',
450 450 conditions={'method': ['POST']})
451 451 m.connect('admin_settings_vcs', '/settings/vcs',
452 452 action='settings_vcs',
453 453 conditions={'method': ['GET']})
454 454 m.connect('admin_settings_vcs', '/settings/vcs',
455 455 action='delete_svn_pattern',
456 456 conditions={'method': ['DELETE']})
457 457
458 458 m.connect('admin_settings_mapping', '/settings/mapping',
459 459 action='settings_mapping_update',
460 460 conditions={'method': ['POST']})
461 461 m.connect('admin_settings_mapping', '/settings/mapping',
462 462 action='settings_mapping', conditions={'method': ['GET']})
463 463
464 464 m.connect('admin_settings_global', '/settings/global',
465 465 action='settings_global_update',
466 466 conditions={'method': ['POST']})
467 467 m.connect('admin_settings_global', '/settings/global',
468 468 action='settings_global', conditions={'method': ['GET']})
469 469
470 470 m.connect('admin_settings_visual', '/settings/visual',
471 471 action='settings_visual_update',
472 472 conditions={'method': ['POST']})
473 473 m.connect('admin_settings_visual', '/settings/visual',
474 474 action='settings_visual', conditions={'method': ['GET']})
475 475
476 476 m.connect('admin_settings_issuetracker',
477 477 '/settings/issue-tracker', action='settings_issuetracker',
478 478 conditions={'method': ['GET']})
479 479 m.connect('admin_settings_issuetracker_save',
480 480 '/settings/issue-tracker/save',
481 481 action='settings_issuetracker_save',
482 482 conditions={'method': ['POST']})
483 483 m.connect('admin_issuetracker_test', '/settings/issue-tracker/test',
484 484 action='settings_issuetracker_test',
485 485 conditions={'method': ['POST']})
486 486 m.connect('admin_issuetracker_delete',
487 487 '/settings/issue-tracker/delete',
488 488 action='settings_issuetracker_delete',
489 489 conditions={'method': ['DELETE']})
490 490
491 491 m.connect('admin_settings_email', '/settings/email',
492 492 action='settings_email_update',
493 493 conditions={'method': ['POST']})
494 494 m.connect('admin_settings_email', '/settings/email',
495 495 action='settings_email', conditions={'method': ['GET']})
496 496
497 497 m.connect('admin_settings_hooks', '/settings/hooks',
498 498 action='settings_hooks_update',
499 499 conditions={'method': ['POST', 'DELETE']})
500 500 m.connect('admin_settings_hooks', '/settings/hooks',
501 501 action='settings_hooks', conditions={'method': ['GET']})
502 502
503 503 m.connect('admin_settings_search', '/settings/search',
504 504 action='settings_search', conditions={'method': ['GET']})
505 505
506 506 m.connect('admin_settings_system', '/settings/system',
507 507 action='settings_system', conditions={'method': ['GET']})
508 508
509 509 m.connect('admin_settings_system_update', '/settings/system/updates',
510 510 action='settings_system_update', conditions={'method': ['GET']})
511 511
512 512 m.connect('admin_settings_supervisor', '/settings/supervisor',
513 513 action='settings_supervisor', conditions={'method': ['GET']})
514 514 m.connect('admin_settings_supervisor_log', '/settings/supervisor/{procid}/log',
515 515 action='settings_supervisor_log', conditions={'method': ['GET']})
516 516
517 517 m.connect('admin_settings_labs', '/settings/labs',
518 518 action='settings_labs_update',
519 519 conditions={'method': ['POST']})
520 520 m.connect('admin_settings_labs', '/settings/labs',
521 521 action='settings_labs', conditions={'method': ['GET']})
522 522
523 523 # ADMIN MY ACCOUNT
524 524 with rmap.submapper(path_prefix=ADMIN_PREFIX,
525 525 controller='admin/my_account') as m:
526 526
527 527 m.connect('my_account', '/my_account',
528 528 action='my_account', conditions={'method': ['GET']})
529 529 m.connect('my_account_edit', '/my_account/edit',
530 530 action='my_account_edit', conditions={'method': ['GET']})
531 531 m.connect('my_account', '/my_account',
532 532 action='my_account_update', conditions={'method': ['POST']})
533 533
534 534 m.connect('my_account_password', '/my_account/password',
535 535 action='my_account_password', conditions={'method': ['GET', 'POST']})
536 536
537 537 m.connect('my_account_repos', '/my_account/repos',
538 538 action='my_account_repos', conditions={'method': ['GET']})
539 539
540 540 m.connect('my_account_watched', '/my_account/watched',
541 541 action='my_account_watched', conditions={'method': ['GET']})
542 542
543 543 m.connect('my_account_pullrequests', '/my_account/pull_requests',
544 544 action='my_account_pullrequests', conditions={'method': ['GET']})
545 545
546 546 m.connect('my_account_perms', '/my_account/perms',
547 547 action='my_account_perms', conditions={'method': ['GET']})
548 548
549 549 m.connect('my_account_emails', '/my_account/emails',
550 550 action='my_account_emails', conditions={'method': ['GET']})
551 551 m.connect('my_account_emails', '/my_account/emails',
552 552 action='my_account_emails_add', conditions={'method': ['POST']})
553 553 m.connect('my_account_emails', '/my_account/emails',
554 554 action='my_account_emails_delete', conditions={'method': ['DELETE']})
555 555
556 556 m.connect('my_account_auth_tokens', '/my_account/auth_tokens',
557 557 action='my_account_auth_tokens', conditions={'method': ['GET']})
558 558 m.connect('my_account_auth_tokens', '/my_account/auth_tokens',
559 559 action='my_account_auth_tokens_add', conditions={'method': ['POST']})
560 560 m.connect('my_account_auth_tokens', '/my_account/auth_tokens',
561 561 action='my_account_auth_tokens_delete', conditions={'method': ['DELETE']})
562 562 m.connect('my_account_notifications', '/my_account/notifications',
563 563 action='my_notifications',
564 564 conditions={'method': ['GET']})
565 565 m.connect('my_account_notifications_toggle_visibility',
566 566 '/my_account/toggle_visibility',
567 567 action='my_notifications_toggle_visibility',
568 568 conditions={'method': ['POST']})
569 m.connect('my_account_notifications_test_channelstream',
570 '/my_account/test_channelstream',
571 action='my_account_notifications_test_channelstream',
572 conditions={'method': ['POST']})
569 573
570 574 # NOTIFICATION REST ROUTES
571 575 with rmap.submapper(path_prefix=ADMIN_PREFIX,
572 576 controller='admin/notifications') as m:
573 577 m.connect('notifications', '/notifications',
574 578 action='index', conditions={'method': ['GET']})
575 579 m.connect('notifications_mark_all_read', '/notifications/mark_all_read',
576 580 action='mark_all_read', conditions={'method': ['POST']})
577 581 m.connect('/notifications/{notification_id}',
578 582 action='update', conditions={'method': ['PUT']})
579 583 m.connect('/notifications/{notification_id}',
580 584 action='delete', conditions={'method': ['DELETE']})
581 585 m.connect('notification', '/notifications/{notification_id}',
582 586 action='show', conditions={'method': ['GET']})
583 587
584 588 # ADMIN GIST
585 589 with rmap.submapper(path_prefix=ADMIN_PREFIX,
586 590 controller='admin/gists') as m:
587 591 m.connect('gists', '/gists',
588 592 action='create', conditions={'method': ['POST']})
589 593 m.connect('gists', '/gists', jsroute=True,
590 594 action='index', conditions={'method': ['GET']})
591 595 m.connect('new_gist', '/gists/new', jsroute=True,
592 596 action='new', conditions={'method': ['GET']})
593 597
594 598 m.connect('/gists/{gist_id}',
595 599 action='delete', conditions={'method': ['DELETE']})
596 600 m.connect('edit_gist', '/gists/{gist_id}/edit',
597 601 action='edit_form', conditions={'method': ['GET']})
598 602 m.connect('edit_gist', '/gists/{gist_id}/edit',
599 603 action='edit', conditions={'method': ['POST']})
600 604 m.connect(
601 605 'edit_gist_check_revision', '/gists/{gist_id}/edit/check_revision',
602 606 action='check_revision', conditions={'method': ['GET']})
603 607
604 608 m.connect('gist', '/gists/{gist_id}',
605 609 action='show', conditions={'method': ['GET']})
606 610 m.connect('gist_rev', '/gists/{gist_id}/{revision}',
607 611 revision='tip',
608 612 action='show', conditions={'method': ['GET']})
609 613 m.connect('formatted_gist', '/gists/{gist_id}/{revision}/{format}',
610 614 revision='tip',
611 615 action='show', conditions={'method': ['GET']})
612 616 m.connect('formatted_gist_file', '/gists/{gist_id}/{revision}/{format}/{f_path}',
613 617 revision='tip',
614 618 action='show', conditions={'method': ['GET']},
615 619 requirements=URL_NAME_REQUIREMENTS)
616 620
617 621 # ADMIN MAIN PAGES
618 622 with rmap.submapper(path_prefix=ADMIN_PREFIX,
619 623 controller='admin/admin') as m:
620 624 m.connect('admin_home', '', action='index')
621 625 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
622 626 action='add_repo')
623 627 m.connect(
624 628 'pull_requests_global_0', '/pull_requests/{pull_request_id:[0-9]+}',
625 629 action='pull_requests')
626 630 m.connect(
627 631 'pull_requests_global_1', '/pull-requests/{pull_request_id:[0-9]+}',
628 632 action='pull_requests')
629 633 m.connect(
630 634 'pull_requests_global', '/pull-request/{pull_request_id:[0-9]+}',
631 635 action='pull_requests')
632 636
633 637 # USER JOURNAL
634 638 rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,),
635 639 controller='journal', action='index')
636 640 rmap.connect('journal_rss', '%s/journal/rss' % (ADMIN_PREFIX,),
637 641 controller='journal', action='journal_rss')
638 642 rmap.connect('journal_atom', '%s/journal/atom' % (ADMIN_PREFIX,),
639 643 controller='journal', action='journal_atom')
640 644
641 645 rmap.connect('public_journal', '%s/public_journal' % (ADMIN_PREFIX,),
642 646 controller='journal', action='public_journal')
643 647
644 648 rmap.connect('public_journal_rss', '%s/public_journal/rss' % (ADMIN_PREFIX,),
645 649 controller='journal', action='public_journal_rss')
646 650
647 651 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % (ADMIN_PREFIX,),
648 652 controller='journal', action='public_journal_rss')
649 653
650 654 rmap.connect('public_journal_atom',
651 655 '%s/public_journal/atom' % (ADMIN_PREFIX,), controller='journal',
652 656 action='public_journal_atom')
653 657
654 658 rmap.connect('public_journal_atom_old',
655 659 '%s/public_journal_atom' % (ADMIN_PREFIX,), controller='journal',
656 660 action='public_journal_atom')
657 661
658 662 rmap.connect('toggle_following', '%s/toggle_following' % (ADMIN_PREFIX,),
659 663 controller='journal', action='toggle_following', jsroute=True,
660 664 conditions={'method': ['POST']})
661 665
662 666 # FULL TEXT SEARCH
663 667 rmap.connect('search', '%s/search' % (ADMIN_PREFIX,),
664 668 controller='search')
665 669 rmap.connect('search_repo_home', '/{repo_name}/search',
666 670 controller='search',
667 671 action='index',
668 672 conditions={'function': check_repo},
669 673 requirements=URL_NAME_REQUIREMENTS)
670 674
671 675 # FEEDS
672 676 rmap.connect('rss_feed_home', '/{repo_name}/feed/rss',
673 677 controller='feed', action='rss',
674 678 conditions={'function': check_repo},
675 679 requirements=URL_NAME_REQUIREMENTS)
676 680
677 681 rmap.connect('atom_feed_home', '/{repo_name}/feed/atom',
678 682 controller='feed', action='atom',
679 683 conditions={'function': check_repo},
680 684 requirements=URL_NAME_REQUIREMENTS)
681 685
682 686 #==========================================================================
683 687 # REPOSITORY ROUTES
684 688 #==========================================================================
685 689
686 690 rmap.connect('repo_creating_home', '/{repo_name}/repo_creating',
687 691 controller='admin/repos', action='repo_creating',
688 692 requirements=URL_NAME_REQUIREMENTS)
689 693 rmap.connect('repo_check_home', '/{repo_name}/crepo_check',
690 694 controller='admin/repos', action='repo_check',
691 695 requirements=URL_NAME_REQUIREMENTS)
692 696
693 697 rmap.connect('repo_stats', '/{repo_name}/repo_stats/{commit_id}',
694 698 controller='summary', action='repo_stats',
695 699 conditions={'function': check_repo},
696 700 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
697 701
698 702 rmap.connect('repo_refs_data', '/{repo_name}/refs-data',
699 703 controller='summary', action='repo_refs_data', jsroute=True,
700 704 requirements=URL_NAME_REQUIREMENTS)
701 705 rmap.connect('repo_refs_changelog_data', '/{repo_name}/refs-data-changelog',
702 706 controller='summary', action='repo_refs_changelog_data',
703 707 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
704 708 rmap.connect('repo_default_reviewers_data', '/{repo_name}/default-reviewers',
705 709 controller='summary', action='repo_default_reviewers_data',
706 710 jsroute=True, requirements=URL_NAME_REQUIREMENTS)
707 711
708 712 rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}',
709 713 controller='changeset', revision='tip', jsroute=True,
710 714 conditions={'function': check_repo},
711 715 requirements=URL_NAME_REQUIREMENTS)
712 716 rmap.connect('changeset_children', '/{repo_name}/changeset_children/{revision}',
713 717 controller='changeset', revision='tip', action='changeset_children',
714 718 conditions={'function': check_repo},
715 719 requirements=URL_NAME_REQUIREMENTS)
716 720 rmap.connect('changeset_parents', '/{repo_name}/changeset_parents/{revision}',
717 721 controller='changeset', revision='tip', action='changeset_parents',
718 722 conditions={'function': check_repo},
719 723 requirements=URL_NAME_REQUIREMENTS)
720 724
721 725 # repo edit options
722 726 rmap.connect('edit_repo', '/{repo_name}/settings', jsroute=True,
723 727 controller='admin/repos', action='edit',
724 728 conditions={'method': ['GET'], 'function': check_repo},
725 729 requirements=URL_NAME_REQUIREMENTS)
726 730
727 731 rmap.connect('edit_repo_perms', '/{repo_name}/settings/permissions',
728 732 jsroute=True,
729 733 controller='admin/repos', action='edit_permissions',
730 734 conditions={'method': ['GET'], 'function': check_repo},
731 735 requirements=URL_NAME_REQUIREMENTS)
732 736 rmap.connect('edit_repo_perms_update', '/{repo_name}/settings/permissions',
733 737 controller='admin/repos', action='edit_permissions_update',
734 738 conditions={'method': ['PUT'], 'function': check_repo},
735 739 requirements=URL_NAME_REQUIREMENTS)
736 740
737 741 rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields',
738 742 controller='admin/repos', action='edit_fields',
739 743 conditions={'method': ['GET'], 'function': check_repo},
740 744 requirements=URL_NAME_REQUIREMENTS)
741 745 rmap.connect('create_repo_fields', '/{repo_name}/settings/fields/new',
742 746 controller='admin/repos', action='create_repo_field',
743 747 conditions={'method': ['PUT'], 'function': check_repo},
744 748 requirements=URL_NAME_REQUIREMENTS)
745 749 rmap.connect('delete_repo_fields', '/{repo_name}/settings/fields/{field_id}',
746 750 controller='admin/repos', action='delete_repo_field',
747 751 conditions={'method': ['DELETE'], 'function': check_repo},
748 752 requirements=URL_NAME_REQUIREMENTS)
749 753
750 754 rmap.connect('edit_repo_advanced', '/{repo_name}/settings/advanced',
751 755 controller='admin/repos', action='edit_advanced',
752 756 conditions={'method': ['GET'], 'function': check_repo},
753 757 requirements=URL_NAME_REQUIREMENTS)
754 758
755 759 rmap.connect('edit_repo_advanced_locking', '/{repo_name}/settings/advanced/locking',
756 760 controller='admin/repos', action='edit_advanced_locking',
757 761 conditions={'method': ['PUT'], 'function': check_repo},
758 762 requirements=URL_NAME_REQUIREMENTS)
759 763 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
760 764 controller='admin/repos', action='toggle_locking',
761 765 conditions={'method': ['GET'], 'function': check_repo},
762 766 requirements=URL_NAME_REQUIREMENTS)
763 767
764 768 rmap.connect('edit_repo_advanced_journal', '/{repo_name}/settings/advanced/journal',
765 769 controller='admin/repos', action='edit_advanced_journal',
766 770 conditions={'method': ['PUT'], 'function': check_repo},
767 771 requirements=URL_NAME_REQUIREMENTS)
768 772
769 773 rmap.connect('edit_repo_advanced_fork', '/{repo_name}/settings/advanced/fork',
770 774 controller='admin/repos', action='edit_advanced_fork',
771 775 conditions={'method': ['PUT'], 'function': check_repo},
772 776 requirements=URL_NAME_REQUIREMENTS)
773 777
774 778 rmap.connect('edit_repo_caches', '/{repo_name}/settings/caches',
775 779 controller='admin/repos', action='edit_caches_form',
776 780 conditions={'method': ['GET'], 'function': check_repo},
777 781 requirements=URL_NAME_REQUIREMENTS)
778 782 rmap.connect('edit_repo_caches', '/{repo_name}/settings/caches',
779 783 controller='admin/repos', action='edit_caches',
780 784 conditions={'method': ['PUT'], 'function': check_repo},
781 785 requirements=URL_NAME_REQUIREMENTS)
782 786
783 787 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
784 788 controller='admin/repos', action='edit_remote_form',
785 789 conditions={'method': ['GET'], 'function': check_repo},
786 790 requirements=URL_NAME_REQUIREMENTS)
787 791 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
788 792 controller='admin/repos', action='edit_remote',
789 793 conditions={'method': ['PUT'], 'function': check_repo},
790 794 requirements=URL_NAME_REQUIREMENTS)
791 795
792 796 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
793 797 controller='admin/repos', action='edit_statistics_form',
794 798 conditions={'method': ['GET'], 'function': check_repo},
795 799 requirements=URL_NAME_REQUIREMENTS)
796 800 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
797 801 controller='admin/repos', action='edit_statistics',
798 802 conditions={'method': ['PUT'], 'function': check_repo},
799 803 requirements=URL_NAME_REQUIREMENTS)
800 804 rmap.connect('repo_settings_issuetracker',
801 805 '/{repo_name}/settings/issue-tracker',
802 806 controller='admin/repos', action='repo_issuetracker',
803 807 conditions={'method': ['GET'], 'function': check_repo},
804 808 requirements=URL_NAME_REQUIREMENTS)
805 809 rmap.connect('repo_issuetracker_test',
806 810 '/{repo_name}/settings/issue-tracker/test',
807 811 controller='admin/repos', action='repo_issuetracker_test',
808 812 conditions={'method': ['POST'], 'function': check_repo},
809 813 requirements=URL_NAME_REQUIREMENTS)
810 814 rmap.connect('repo_issuetracker_delete',
811 815 '/{repo_name}/settings/issue-tracker/delete',
812 816 controller='admin/repos', action='repo_issuetracker_delete',
813 817 conditions={'method': ['DELETE'], 'function': check_repo},
814 818 requirements=URL_NAME_REQUIREMENTS)
815 819 rmap.connect('repo_issuetracker_save',
816 820 '/{repo_name}/settings/issue-tracker/save',
817 821 controller='admin/repos', action='repo_issuetracker_save',
818 822 conditions={'method': ['POST'], 'function': check_repo},
819 823 requirements=URL_NAME_REQUIREMENTS)
820 824 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
821 825 controller='admin/repos', action='repo_settings_vcs_update',
822 826 conditions={'method': ['POST'], 'function': check_repo},
823 827 requirements=URL_NAME_REQUIREMENTS)
824 828 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
825 829 controller='admin/repos', action='repo_settings_vcs',
826 830 conditions={'method': ['GET'], 'function': check_repo},
827 831 requirements=URL_NAME_REQUIREMENTS)
828 832 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
829 833 controller='admin/repos', action='repo_delete_svn_pattern',
830 834 conditions={'method': ['DELETE'], 'function': check_repo},
831 835 requirements=URL_NAME_REQUIREMENTS)
832 836 rmap.connect('repo_pullrequest_settings', '/{repo_name}/settings/pullrequest',
833 837 controller='admin/repos', action='repo_settings_pullrequest',
834 838 conditions={'method': ['GET', 'POST'], 'function': check_repo},
835 839 requirements=URL_NAME_REQUIREMENTS)
836 840
837 841 # still working url for backward compat.
838 842 rmap.connect('raw_changeset_home_depraced',
839 843 '/{repo_name}/raw-changeset/{revision}',
840 844 controller='changeset', action='changeset_raw',
841 845 revision='tip', conditions={'function': check_repo},
842 846 requirements=URL_NAME_REQUIREMENTS)
843 847
844 848 # new URLs
845 849 rmap.connect('changeset_raw_home',
846 850 '/{repo_name}/changeset-diff/{revision}',
847 851 controller='changeset', action='changeset_raw',
848 852 revision='tip', conditions={'function': check_repo},
849 853 requirements=URL_NAME_REQUIREMENTS)
850 854
851 855 rmap.connect('changeset_patch_home',
852 856 '/{repo_name}/changeset-patch/{revision}',
853 857 controller='changeset', action='changeset_patch',
854 858 revision='tip', conditions={'function': check_repo},
855 859 requirements=URL_NAME_REQUIREMENTS)
856 860
857 861 rmap.connect('changeset_download_home',
858 862 '/{repo_name}/changeset-download/{revision}',
859 863 controller='changeset', action='changeset_download',
860 864 revision='tip', conditions={'function': check_repo},
861 865 requirements=URL_NAME_REQUIREMENTS)
862 866
863 867 rmap.connect('changeset_comment',
864 868 '/{repo_name}/changeset/{revision}/comment', jsroute=True,
865 869 controller='changeset', revision='tip', action='comment',
866 870 conditions={'function': check_repo},
867 871 requirements=URL_NAME_REQUIREMENTS)
868 872
869 873 rmap.connect('changeset_comment_preview',
870 874 '/{repo_name}/changeset/comment/preview', jsroute=True,
871 875 controller='changeset', action='preview_comment',
872 876 conditions={'function': check_repo, 'method': ['POST']},
873 877 requirements=URL_NAME_REQUIREMENTS)
874 878
875 879 rmap.connect('changeset_comment_delete',
876 880 '/{repo_name}/changeset/comment/{comment_id}/delete',
877 881 controller='changeset', action='delete_comment',
878 882 conditions={'function': check_repo, 'method': ['DELETE']},
879 883 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
880 884
881 885 rmap.connect('changeset_info', '/{repo_name}/changeset_info/{revision}',
882 886 controller='changeset', action='changeset_info',
883 887 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
884 888
885 889 rmap.connect('compare_home',
886 890 '/{repo_name}/compare',
887 891 controller='compare', action='index',
888 892 conditions={'function': check_repo},
889 893 requirements=URL_NAME_REQUIREMENTS)
890 894
891 895 rmap.connect('compare_url',
892 896 '/{repo_name}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}',
893 897 controller='compare', action='compare',
894 898 conditions={'function': check_repo},
895 899 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
896 900
897 901 rmap.connect('pullrequest_home',
898 902 '/{repo_name}/pull-request/new', controller='pullrequests',
899 903 action='index', conditions={'function': check_repo,
900 904 'method': ['GET']},
901 905 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
902 906
903 907 rmap.connect('pullrequest',
904 908 '/{repo_name}/pull-request/new', controller='pullrequests',
905 909 action='create', conditions={'function': check_repo,
906 910 'method': ['POST']},
907 911 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
908 912
909 913 rmap.connect('pullrequest_repo_refs',
910 914 '/{repo_name}/pull-request/refs/{target_repo_name:.*?[^/]}',
911 915 controller='pullrequests',
912 916 action='get_repo_refs',
913 917 conditions={'function': check_repo, 'method': ['GET']},
914 918 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
915 919
916 920 rmap.connect('pullrequest_repo_destinations',
917 921 '/{repo_name}/pull-request/repo-destinations',
918 922 controller='pullrequests',
919 923 action='get_repo_destinations',
920 924 conditions={'function': check_repo, 'method': ['GET']},
921 925 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
922 926
923 927 rmap.connect('pullrequest_show',
924 928 '/{repo_name}/pull-request/{pull_request_id}',
925 929 controller='pullrequests',
926 930 action='show', conditions={'function': check_repo,
927 931 'method': ['GET']},
928 932 requirements=URL_NAME_REQUIREMENTS)
929 933
930 934 rmap.connect('pullrequest_update',
931 935 '/{repo_name}/pull-request/{pull_request_id}',
932 936 controller='pullrequests',
933 937 action='update', conditions={'function': check_repo,
934 938 'method': ['PUT']},
935 939 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
936 940
937 941 rmap.connect('pullrequest_merge',
938 942 '/{repo_name}/pull-request/{pull_request_id}',
939 943 controller='pullrequests',
940 944 action='merge', conditions={'function': check_repo,
941 945 'method': ['POST']},
942 946 requirements=URL_NAME_REQUIREMENTS)
943 947
944 948 rmap.connect('pullrequest_delete',
945 949 '/{repo_name}/pull-request/{pull_request_id}',
946 950 controller='pullrequests',
947 951 action='delete', conditions={'function': check_repo,
948 952 'method': ['DELETE']},
949 953 requirements=URL_NAME_REQUIREMENTS)
950 954
951 955 rmap.connect('pullrequest_show_all',
952 956 '/{repo_name}/pull-request',
953 957 controller='pullrequests',
954 958 action='show_all', conditions={'function': check_repo,
955 959 'method': ['GET']},
956 960 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
957 961
958 962 rmap.connect('pullrequest_comment',
959 963 '/{repo_name}/pull-request-comment/{pull_request_id}',
960 964 controller='pullrequests',
961 965 action='comment', conditions={'function': check_repo,
962 966 'method': ['POST']},
963 967 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
964 968
965 969 rmap.connect('pullrequest_comment_delete',
966 970 '/{repo_name}/pull-request-comment/{comment_id}/delete',
967 971 controller='pullrequests', action='delete_comment',
968 972 conditions={'function': check_repo, 'method': ['DELETE']},
969 973 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
970 974
971 975 rmap.connect('summary_home_explicit', '/{repo_name}/summary',
972 976 controller='summary', conditions={'function': check_repo},
973 977 requirements=URL_NAME_REQUIREMENTS)
974 978
975 979 rmap.connect('branches_home', '/{repo_name}/branches',
976 980 controller='branches', conditions={'function': check_repo},
977 981 requirements=URL_NAME_REQUIREMENTS)
978 982
979 983 rmap.connect('tags_home', '/{repo_name}/tags',
980 984 controller='tags', conditions={'function': check_repo},
981 985 requirements=URL_NAME_REQUIREMENTS)
982 986
983 987 rmap.connect('bookmarks_home', '/{repo_name}/bookmarks',
984 988 controller='bookmarks', conditions={'function': check_repo},
985 989 requirements=URL_NAME_REQUIREMENTS)
986 990
987 991 rmap.connect('changelog_home', '/{repo_name}/changelog', jsroute=True,
988 992 controller='changelog', conditions={'function': check_repo},
989 993 requirements=URL_NAME_REQUIREMENTS)
990 994
991 995 rmap.connect('changelog_summary_home', '/{repo_name}/changelog_summary',
992 996 controller='changelog', action='changelog_summary',
993 997 conditions={'function': check_repo},
994 998 requirements=URL_NAME_REQUIREMENTS)
995 999
996 1000 rmap.connect('changelog_file_home',
997 1001 '/{repo_name}/changelog/{revision}/{f_path}',
998 1002 controller='changelog', f_path=None,
999 1003 conditions={'function': check_repo},
1000 1004 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1001 1005
1002 1006 rmap.connect('changelog_details', '/{repo_name}/changelog_details/{cs}',
1003 1007 controller='changelog', action='changelog_details',
1004 1008 conditions={'function': check_repo},
1005 1009 requirements=URL_NAME_REQUIREMENTS)
1006 1010
1007 1011 rmap.connect('files_home', '/{repo_name}/files/{revision}/{f_path}',
1008 1012 controller='files', revision='tip', f_path='',
1009 1013 conditions={'function': check_repo},
1010 1014 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1011 1015
1012 1016 rmap.connect('files_home_simple_catchrev',
1013 1017 '/{repo_name}/files/{revision}',
1014 1018 controller='files', revision='tip', f_path='',
1015 1019 conditions={'function': check_repo},
1016 1020 requirements=URL_NAME_REQUIREMENTS)
1017 1021
1018 1022 rmap.connect('files_home_simple_catchall',
1019 1023 '/{repo_name}/files',
1020 1024 controller='files', revision='tip', f_path='',
1021 1025 conditions={'function': check_repo},
1022 1026 requirements=URL_NAME_REQUIREMENTS)
1023 1027
1024 1028 rmap.connect('files_history_home',
1025 1029 '/{repo_name}/history/{revision}/{f_path}',
1026 1030 controller='files', action='history', revision='tip', f_path='',
1027 1031 conditions={'function': check_repo},
1028 1032 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1029 1033
1030 1034 rmap.connect('files_authors_home',
1031 1035 '/{repo_name}/authors/{revision}/{f_path}',
1032 1036 controller='files', action='authors', revision='tip', f_path='',
1033 1037 conditions={'function': check_repo},
1034 1038 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1035 1039
1036 1040 rmap.connect('files_diff_home', '/{repo_name}/diff/{f_path}',
1037 1041 controller='files', action='diff', f_path='',
1038 1042 conditions={'function': check_repo},
1039 1043 requirements=URL_NAME_REQUIREMENTS)
1040 1044
1041 1045 rmap.connect('files_diff_2way_home',
1042 1046 '/{repo_name}/diff-2way/{f_path}',
1043 1047 controller='files', action='diff_2way', f_path='',
1044 1048 conditions={'function': check_repo},
1045 1049 requirements=URL_NAME_REQUIREMENTS)
1046 1050
1047 1051 rmap.connect('files_rawfile_home',
1048 1052 '/{repo_name}/rawfile/{revision}/{f_path}',
1049 1053 controller='files', action='rawfile', revision='tip',
1050 1054 f_path='', conditions={'function': check_repo},
1051 1055 requirements=URL_NAME_REQUIREMENTS)
1052 1056
1053 1057 rmap.connect('files_raw_home',
1054 1058 '/{repo_name}/raw/{revision}/{f_path}',
1055 1059 controller='files', action='raw', revision='tip', f_path='',
1056 1060 conditions={'function': check_repo},
1057 1061 requirements=URL_NAME_REQUIREMENTS)
1058 1062
1059 1063 rmap.connect('files_render_home',
1060 1064 '/{repo_name}/render/{revision}/{f_path}',
1061 1065 controller='files', action='index', revision='tip', f_path='',
1062 1066 rendered=True, conditions={'function': check_repo},
1063 1067 requirements=URL_NAME_REQUIREMENTS)
1064 1068
1065 1069 rmap.connect('files_annotate_home',
1066 1070 '/{repo_name}/annotate/{revision}/{f_path}',
1067 1071 controller='files', action='index', revision='tip',
1068 1072 f_path='', annotate=True, conditions={'function': check_repo},
1069 1073 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1070 1074
1071 1075 rmap.connect('files_edit',
1072 1076 '/{repo_name}/edit/{revision}/{f_path}',
1073 1077 controller='files', action='edit', revision='tip',
1074 1078 f_path='',
1075 1079 conditions={'function': check_repo, 'method': ['POST']},
1076 1080 requirements=URL_NAME_REQUIREMENTS)
1077 1081
1078 1082 rmap.connect('files_edit_home',
1079 1083 '/{repo_name}/edit/{revision}/{f_path}',
1080 1084 controller='files', action='edit_home', revision='tip',
1081 1085 f_path='', conditions={'function': check_repo},
1082 1086 requirements=URL_NAME_REQUIREMENTS)
1083 1087
1084 1088 rmap.connect('files_add',
1085 1089 '/{repo_name}/add/{revision}/{f_path}',
1086 1090 controller='files', action='add', revision='tip',
1087 1091 f_path='',
1088 1092 conditions={'function': check_repo, 'method': ['POST']},
1089 1093 requirements=URL_NAME_REQUIREMENTS)
1090 1094
1091 1095 rmap.connect('files_add_home',
1092 1096 '/{repo_name}/add/{revision}/{f_path}',
1093 1097 controller='files', action='add_home', revision='tip',
1094 1098 f_path='', conditions={'function': check_repo},
1095 1099 requirements=URL_NAME_REQUIREMENTS)
1096 1100
1097 1101 rmap.connect('files_delete',
1098 1102 '/{repo_name}/delete/{revision}/{f_path}',
1099 1103 controller='files', action='delete', revision='tip',
1100 1104 f_path='',
1101 1105 conditions={'function': check_repo, 'method': ['POST']},
1102 1106 requirements=URL_NAME_REQUIREMENTS)
1103 1107
1104 1108 rmap.connect('files_delete_home',
1105 1109 '/{repo_name}/delete/{revision}/{f_path}',
1106 1110 controller='files', action='delete_home', revision='tip',
1107 1111 f_path='', conditions={'function': check_repo},
1108 1112 requirements=URL_NAME_REQUIREMENTS)
1109 1113
1110 1114 rmap.connect('files_archive_home', '/{repo_name}/archive/{fname}',
1111 1115 controller='files', action='archivefile',
1112 1116 conditions={'function': check_repo},
1113 1117 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1114 1118
1115 1119 rmap.connect('files_nodelist_home',
1116 1120 '/{repo_name}/nodelist/{revision}/{f_path}',
1117 1121 controller='files', action='nodelist',
1118 1122 conditions={'function': check_repo},
1119 1123 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1120 1124
1121 1125 rmap.connect('files_nodetree_full',
1122 1126 '/{repo_name}/nodetree_full/{commit_id}/{f_path}',
1123 1127 controller='files', action='nodetree_full',
1124 1128 conditions={'function': check_repo},
1125 1129 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1126 1130
1127 1131 rmap.connect('repo_fork_create_home', '/{repo_name}/fork',
1128 1132 controller='forks', action='fork_create',
1129 1133 conditions={'function': check_repo, 'method': ['POST']},
1130 1134 requirements=URL_NAME_REQUIREMENTS)
1131 1135
1132 1136 rmap.connect('repo_fork_home', '/{repo_name}/fork',
1133 1137 controller='forks', action='fork',
1134 1138 conditions={'function': check_repo},
1135 1139 requirements=URL_NAME_REQUIREMENTS)
1136 1140
1137 1141 rmap.connect('repo_forks_home', '/{repo_name}/forks',
1138 1142 controller='forks', action='forks',
1139 1143 conditions={'function': check_repo},
1140 1144 requirements=URL_NAME_REQUIREMENTS)
1141 1145
1142 1146 rmap.connect('repo_followers_home', '/{repo_name}/followers',
1143 1147 controller='followers', action='followers',
1144 1148 conditions={'function': check_repo},
1145 1149 requirements=URL_NAME_REQUIREMENTS)
1146 1150
1147 1151 # must be here for proper group/repo catching pattern
1148 1152 _connect_with_slash(
1149 1153 rmap, 'repo_group_home', '/{group_name}',
1150 1154 controller='home', action='index_repo_group',
1151 1155 conditions={'function': check_group},
1152 1156 requirements=URL_NAME_REQUIREMENTS)
1153 1157
1154 1158 # catch all, at the end
1155 1159 _connect_with_slash(
1156 1160 rmap, 'summary_home', '/{repo_name}', jsroute=True,
1157 1161 controller='summary', action='index',
1158 1162 conditions={'function': check_repo},
1159 1163 requirements=URL_NAME_REQUIREMENTS)
1160 1164
1161 1165 return rmap
1162 1166
1163 1167
1164 1168 def _connect_with_slash(mapper, name, path, *args, **kwargs):
1165 1169 """
1166 1170 Connect a route with an optional trailing slash in `path`.
1167 1171 """
1168 1172 mapper.connect(name + '_slash', path + '/', *args, **kwargs)
1169 1173 mapper.connect(name, path, *args, **kwargs)
@@ -1,432 +1,467 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2013-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21
22 22 """
23 23 my account controller for RhodeCode admin
24 24 """
25 25
26 26 import logging
27 import datetime
27 28
28 29 import formencode
29 30 from formencode import htmlfill
31 from pyramid.threadlocal import get_current_registry
30 32 from pylons import request, tmpl_context as c, url, session
31 33 from pylons.controllers.util import redirect
32 34 from pylons.i18n.translation import _
33 35 from sqlalchemy.orm import joinedload
36 from webob.exc import HTTPBadGateway
34 37
35 38 from rhodecode import forms
36 39 from rhodecode.lib import helpers as h
37 40 from rhodecode.lib import auth
38 41 from rhodecode.lib.auth import (
39 42 LoginRequired, NotAnonymous, AuthUser, generate_auth_token)
40 43 from rhodecode.lib.base import BaseController, render
41 44 from rhodecode.lib.utils import jsonify
42 45 from rhodecode.lib.utils2 import safe_int, md5, str2bool
43 46 from rhodecode.lib.ext_json import json
47 from rhodecode.lib.channelstream import channelstream_request, \
48 ChannelstreamException
44 49
45 50 from rhodecode.model.validation_schema.schemas import user_schema
46 51 from rhodecode.model.db import (
47 52 Repository, PullRequest, UserEmailMap, User, UserFollowing)
48 53 from rhodecode.model.forms import UserForm
49 54 from rhodecode.model.scm import RepoList
50 55 from rhodecode.model.user import UserModel
51 56 from rhodecode.model.repo import RepoModel
52 57 from rhodecode.model.auth_token import AuthTokenModel
53 58 from rhodecode.model.meta import Session
54 59 from rhodecode.model.pull_request import PullRequestModel
55 60 from rhodecode.model.comment import ChangesetCommentsModel
56 61
57 62 log = logging.getLogger(__name__)
58 63
59 64
60 65 class MyAccountController(BaseController):
61 66 """REST Controller styled on the Atom Publishing Protocol"""
62 67 # To properly map this controller, ensure your config/routing.py
63 68 # file has a resource setup:
64 69 # map.resource('setting', 'settings', controller='admin/settings',
65 70 # path_prefix='/admin', name_prefix='admin_')
66 71
67 72 @LoginRequired()
68 73 @NotAnonymous()
69 74 def __before__(self):
70 75 super(MyAccountController, self).__before__()
71 76
72 77 def __load_data(self):
73 78 c.user = User.get(c.rhodecode_user.user_id)
74 79 if c.user.username == User.DEFAULT_USER:
75 80 h.flash(_("You can't edit this user since it's"
76 81 " crucial for entire application"), category='warning')
77 82 return redirect(url('users'))
78 83
79 84 def _load_my_repos_data(self, watched=False):
80 85 if watched:
81 86 admin = False
82 87 follows_repos = Session().query(UserFollowing)\
83 88 .filter(UserFollowing.user_id == c.rhodecode_user.user_id)\
84 89 .options(joinedload(UserFollowing.follows_repository))\
85 90 .all()
86 91 repo_list = [x.follows_repository for x in follows_repos]
87 92 else:
88 93 admin = True
89 94 repo_list = Repository.get_all_repos(
90 95 user_id=c.rhodecode_user.user_id)
91 96 repo_list = RepoList(repo_list, perm_set=[
92 97 'repository.read', 'repository.write', 'repository.admin'])
93 98
94 99 repos_data = RepoModel().get_repos_as_dict(
95 100 repo_list=repo_list, admin=admin)
96 101 # json used to render the grid
97 102 return json.dumps(repos_data)
98 103
99 104 @auth.CSRFRequired()
100 105 def my_account_update(self):
101 106 """
102 107 POST /_admin/my_account Updates info of my account
103 108 """
104 109 # url('my_account')
105 110 c.active = 'profile_edit'
106 111 self.__load_data()
107 112 c.perm_user = AuthUser(user_id=c.rhodecode_user.user_id,
108 113 ip_addr=self.ip_addr)
109 114 c.extern_type = c.user.extern_type
110 115 c.extern_name = c.user.extern_name
111 116
112 117 defaults = c.user.get_dict()
113 118 update = False
114 119 _form = UserForm(edit=True,
115 120 old_data={'user_id': c.rhodecode_user.user_id,
116 121 'email': c.rhodecode_user.email})()
117 122 form_result = {}
118 123 try:
119 124 post_data = dict(request.POST)
120 125 post_data['new_password'] = ''
121 126 post_data['password_confirmation'] = ''
122 127 form_result = _form.to_python(post_data)
123 128 # skip updating those attrs for my account
124 129 skip_attrs = ['admin', 'active', 'extern_type', 'extern_name',
125 130 'new_password', 'password_confirmation']
126 131 # TODO: plugin should define if username can be updated
127 132 if c.extern_type != "rhodecode":
128 133 # forbid updating username for external accounts
129 134 skip_attrs.append('username')
130 135
131 136 UserModel().update_user(
132 137 c.rhodecode_user.user_id, skip_attrs=skip_attrs, **form_result)
133 138 h.flash(_('Your account was updated successfully'),
134 139 category='success')
135 140 Session().commit()
136 141 update = True
137 142
138 143 except formencode.Invalid as errors:
139 144 return htmlfill.render(
140 145 render('admin/my_account/my_account.html'),
141 146 defaults=errors.value,
142 147 errors=errors.error_dict or {},
143 148 prefix_error=False,
144 149 encoding="UTF-8",
145 150 force_defaults=False)
146 151 except Exception:
147 152 log.exception("Exception updating user")
148 153 h.flash(_('Error occurred during update of user %s')
149 154 % form_result.get('username'), category='error')
150 155
151 156 if update:
152 157 return redirect('my_account')
153 158
154 159 return htmlfill.render(
155 160 render('admin/my_account/my_account.html'),
156 161 defaults=defaults,
157 162 encoding="UTF-8",
158 163 force_defaults=False
159 164 )
160 165
161 166 def my_account(self):
162 167 """
163 168 GET /_admin/my_account Displays info about my account
164 169 """
165 170 # url('my_account')
166 171 c.active = 'profile'
167 172 self.__load_data()
168 173
169 174 defaults = c.user.get_dict()
170 175 return htmlfill.render(
171 176 render('admin/my_account/my_account.html'),
172 177 defaults=defaults, encoding="UTF-8", force_defaults=False)
173 178
174 179 def my_account_edit(self):
175 180 """
176 181 GET /_admin/my_account/edit Displays edit form of my account
177 182 """
178 183 c.active = 'profile_edit'
179 184 self.__load_data()
180 185 c.perm_user = AuthUser(user_id=c.rhodecode_user.user_id,
181 186 ip_addr=self.ip_addr)
182 187 c.extern_type = c.user.extern_type
183 188 c.extern_name = c.user.extern_name
184 189
185 190 defaults = c.user.get_dict()
186 191 return htmlfill.render(
187 192 render('admin/my_account/my_account.html'),
188 193 defaults=defaults,
189 194 encoding="UTF-8",
190 195 force_defaults=False
191 196 )
192 197
193 198 @auth.CSRFRequired(except_methods=['GET'])
194 199 def my_account_password(self):
195 200 c.active = 'password'
196 201 self.__load_data()
197 202
198 203 schema = user_schema.ChangePasswordSchema().bind(
199 204 username=c.rhodecode_user.username)
200 205
201 206 form = forms.Form(schema,
202 207 buttons=(forms.buttons.save, forms.buttons.reset))
203 208
204 209 if request.method == 'POST':
205 210 controls = request.POST.items()
206 211 try:
207 212 valid_data = form.validate(controls)
208 213 UserModel().update_user(c.rhodecode_user.user_id, **valid_data)
209 214 instance = c.rhodecode_user.get_instance()
210 215 instance.update_userdata(force_password_change=False)
211 216 Session().commit()
212 217 except forms.ValidationFailure as e:
213 218 request.session.flash(
214 219 _('Error occurred during update of user password'),
215 220 queue='error')
216 221 form = e
217 222 except Exception:
218 223 log.exception("Exception updating password")
219 224 request.session.flash(
220 225 _('Error occurred during update of user password'),
221 226 queue='error')
222 227 else:
223 228 session.setdefault('rhodecode_user', {}).update(
224 229 {'password': md5(instance.password)})
225 230 session.save()
226 231 request.session.flash(
227 232 _("Successfully updated password"), queue='success')
228 233 return redirect(url('my_account_password'))
229 234
230 235 c.form = form
231 236 return render('admin/my_account/my_account.html')
232 237
233 238 def my_account_repos(self):
234 239 c.active = 'repos'
235 240 self.__load_data()
236 241
237 242 # json used to render the grid
238 243 c.data = self._load_my_repos_data()
239 244 return render('admin/my_account/my_account.html')
240 245
241 246 def my_account_watched(self):
242 247 c.active = 'watched'
243 248 self.__load_data()
244 249
245 250 # json used to render the grid
246 251 c.data = self._load_my_repos_data(watched=True)
247 252 return render('admin/my_account/my_account.html')
248 253
249 254 def my_account_perms(self):
250 255 c.active = 'perms'
251 256 self.__load_data()
252 257 c.perm_user = AuthUser(user_id=c.rhodecode_user.user_id,
253 258 ip_addr=self.ip_addr)
254 259
255 260 return render('admin/my_account/my_account.html')
256 261
257 262 def my_account_emails(self):
258 263 c.active = 'emails'
259 264 self.__load_data()
260 265
261 266 c.user_email_map = UserEmailMap.query()\
262 267 .filter(UserEmailMap.user == c.user).all()
263 268 return render('admin/my_account/my_account.html')
264 269
265 270 @auth.CSRFRequired()
266 271 def my_account_emails_add(self):
267 272 email = request.POST.get('new_email')
268 273
269 274 try:
270 275 UserModel().add_extra_email(c.rhodecode_user.user_id, email)
271 276 Session().commit()
272 277 h.flash(_("Added new email address `%s` for user account") % email,
273 278 category='success')
274 279 except formencode.Invalid as error:
275 280 msg = error.error_dict['email']
276 281 h.flash(msg, category='error')
277 282 except Exception:
278 283 log.exception("Exception in my_account_emails")
279 284 h.flash(_('An error occurred during email saving'),
280 285 category='error')
281 286 return redirect(url('my_account_emails'))
282 287
283 288 @auth.CSRFRequired()
284 289 def my_account_emails_delete(self):
285 290 email_id = request.POST.get('del_email_id')
286 291 user_model = UserModel()
287 292 user_model.delete_extra_email(c.rhodecode_user.user_id, email_id)
288 293 Session().commit()
289 294 h.flash(_("Removed email address from user account"),
290 295 category='success')
291 296 return redirect(url('my_account_emails'))
292 297
293 298 def _extract_ordering(self, request):
294 299 column_index = safe_int(request.GET.get('order[0][column]'))
295 300 order_dir = request.GET.get('order[0][dir]', 'desc')
296 301 order_by = request.GET.get(
297 302 'columns[%s][data][sort]' % column_index, 'name_raw')
298 303 return order_by, order_dir
299 304
300 305 def _get_pull_requests_list(self, statuses):
301 306 start = safe_int(request.GET.get('start'), 0)
302 307 length = safe_int(request.GET.get('length'), c.visual.dashboard_items)
303 308 order_by, order_dir = self._extract_ordering(request)
304 309
305 310 pull_requests = PullRequestModel().get_im_participating_in(
306 311 user_id=c.rhodecode_user.user_id,
307 312 statuses=statuses,
308 313 offset=start, length=length, order_by=order_by,
309 314 order_dir=order_dir)
310 315
311 316 pull_requests_total_count = PullRequestModel().count_im_participating_in(
312 317 user_id=c.rhodecode_user.user_id, statuses=statuses)
313 318
314 319 from rhodecode.lib.utils import PartialRenderer
315 320 _render = PartialRenderer('data_table/_dt_elements.html')
316 321 data = []
317 322 for pr in pull_requests:
318 323 repo_id = pr.target_repo_id
319 324 comments = ChangesetCommentsModel().get_all_comments(
320 325 repo_id, pull_request=pr)
321 326 owned = pr.user_id == c.rhodecode_user.user_id
322 327 status = pr.calculated_review_status()
323 328
324 329 data.append({
325 330 'target_repo': _render('pullrequest_target_repo',
326 331 pr.target_repo.repo_name),
327 332 'name': _render('pullrequest_name',
328 333 pr.pull_request_id, pr.target_repo.repo_name,
329 334 short=True),
330 335 'name_raw': pr.pull_request_id,
331 336 'status': _render('pullrequest_status', status),
332 337 'title': _render(
333 338 'pullrequest_title', pr.title, pr.description),
334 339 'description': h.escape(pr.description),
335 340 'updated_on': _render('pullrequest_updated_on',
336 341 h.datetime_to_time(pr.updated_on)),
337 342 'updated_on_raw': h.datetime_to_time(pr.updated_on),
338 343 'created_on': _render('pullrequest_updated_on',
339 344 h.datetime_to_time(pr.created_on)),
340 345 'created_on_raw': h.datetime_to_time(pr.created_on),
341 346 'author': _render('pullrequest_author',
342 347 pr.author.full_contact, ),
343 348 'author_raw': pr.author.full_name,
344 349 'comments': _render('pullrequest_comments', len(comments)),
345 350 'comments_raw': len(comments),
346 351 'closed': pr.is_closed(),
347 352 'owned': owned
348 353 })
349 354 # json used to render the grid
350 355 data = ({
351 356 'data': data,
352 357 'recordsTotal': pull_requests_total_count,
353 358 'recordsFiltered': pull_requests_total_count,
354 359 })
355 360 return data
356 361
357 362 def my_account_pullrequests(self):
358 363 c.active = 'pullrequests'
359 364 self.__load_data()
360 365 c.show_closed = str2bool(request.GET.get('pr_show_closed'))
361 366
362 367 statuses = [PullRequest.STATUS_NEW, PullRequest.STATUS_OPEN]
363 368 if c.show_closed:
364 369 statuses += [PullRequest.STATUS_CLOSED]
365 370 data = self._get_pull_requests_list(statuses)
366 371 if not request.is_xhr:
367 372 c.data_participate = json.dumps(data['data'])
368 373 c.records_total_participate = data['recordsTotal']
369 374 return render('admin/my_account/my_account.html')
370 375 else:
371 376 return json.dumps(data)
372 377
373 378 def my_account_auth_tokens(self):
374 379 c.active = 'auth_tokens'
375 380 self.__load_data()
376 381 show_expired = True
377 382 c.lifetime_values = [
378 383 (str(-1), _('forever')),
379 384 (str(5), _('5 minutes')),
380 385 (str(60), _('1 hour')),
381 386 (str(60 * 24), _('1 day')),
382 387 (str(60 * 24 * 30), _('1 month')),
383 388 ]
384 389 c.lifetime_options = [(c.lifetime_values, _("Lifetime"))]
385 390 c.role_values = [(x, AuthTokenModel.cls._get_role_name(x))
386 391 for x in AuthTokenModel.cls.ROLES]
387 392 c.role_options = [(c.role_values, _("Role"))]
388 393 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
389 394 c.rhodecode_user.user_id, show_expired=show_expired)
390 395 return render('admin/my_account/my_account.html')
391 396
392 397 @auth.CSRFRequired()
393 398 def my_account_auth_tokens_add(self):
394 399 lifetime = safe_int(request.POST.get('lifetime'), -1)
395 400 description = request.POST.get('description')
396 401 role = request.POST.get('role')
397 402 AuthTokenModel().create(c.rhodecode_user.user_id, description, lifetime,
398 403 role)
399 404 Session().commit()
400 405 h.flash(_("Auth token successfully created"), category='success')
401 406 return redirect(url('my_account_auth_tokens'))
402 407
403 408 @auth.CSRFRequired()
404 409 def my_account_auth_tokens_delete(self):
405 410 auth_token = request.POST.get('del_auth_token')
406 411 user_id = c.rhodecode_user.user_id
407 412 if request.POST.get('del_auth_token_builtin'):
408 413 user = User.get(user_id)
409 414 if user:
410 415 user.api_key = generate_auth_token(user.username)
411 416 Session().add(user)
412 417 Session().commit()
413 418 h.flash(_("Auth token successfully reset"), category='success')
414 419 elif auth_token:
415 420 AuthTokenModel().delete(auth_token, c.rhodecode_user.user_id)
416 421 Session().commit()
417 422 h.flash(_("Auth token successfully deleted"), category='success')
418 423
419 424 return redirect(url('my_account_auth_tokens'))
420 425
421 426 def my_notifications(self):
422 427 c.active = 'notifications'
423 428 return render('admin/my_account/my_account.html')
424 429
425 430 @auth.CSRFRequired()
426 431 @jsonify
427 432 def my_notifications_toggle_visibility(self):
428 433 user = c.rhodecode_user.get_instance()
429 434 new_status = not user.user_data.get('notification_status', True)
430 435 user.update_userdata(notification_status=new_status)
431 436 Session().commit()
432 437 return user.user_data['notification_status']
438
439 @auth.CSRFRequired()
440 @jsonify
441 def my_account_notifications_test_channelstream(self):
442 message = 'Test message sent via Channelstream by user: {}, on {}'.format(
443 c.rhodecode_user.username, datetime.datetime.now())
444 payload = {
445 'type': 'message',
446 'timestamp': datetime.datetime.utcnow(),
447 'user': 'system',
448 #'channel': 'broadcast',
449 'pm_users': [c.rhodecode_user.username],
450 'message': {
451 'message': message,
452 'level': 'info',
453 'topic': '/notifications'
454 }
455 }
456
457 registry = get_current_registry()
458 rhodecode_plugins = getattr(registry, 'rhodecode_plugins', {})
459 channelstream_config = rhodecode_plugins.get('channelstream', {})
460
461 try:
462 channelstream_request(channelstream_config, [payload], '/message')
463 except ChannelstreamException as e:
464 log.exception('Failed to send channelstream data')
465 return {"response": 'ERROR: {}'.format(e.__class__.__name__)}
466 return {"response": 'Channelstream data sent. '
467 'You should see a new live message now.'}
@@ -1,70 +1,107 b''
1 1 <template is="dom-bind" id="notificationsPage">
2 2 <iron-ajax id="toggleNotifications"
3 3 method="post"
4 4 url="${url('my_account_notifications_toggle_visibility')}"
5 5 content-type="application/json"
6 6 loading="{{changeNotificationsLoading}}"
7 7 on-response="handleNotifications"
8 handle-as="json"></iron-ajax>
8 handle-as="json">
9 </iron-ajax>
10
11 <iron-ajax id="sendTestNotification"
12 method="post"
13 url="${url('my_account_notifications_test_channelstream')}"
14 content-type="application/json"
15 on-response="handleTestNotification"
16 handle-as="json">
17 </iron-ajax>
9 18
10 19 <div class="panel panel-default">
11 20 <div class="panel-heading">
12 21 <h3 class="panel-title">${_('Your Live Notification Settings')}</h3>
13 22 </div>
14 23 <div class="panel-body">
15 24
16 25 <p><strong>IMPORTANT:</strong> This feature requires enabled channelstream websocket server to function correctly.</p>
17 26
18 27 <p class="hidden">Status of browser notifications permission: <strong id="browser-notification-status"></strong></p>
19 28
20 29 <div class="form">
21 30 <div class="fields">
22 31 <div class="field">
23 32 <div class="label">
24 33 <label for="new_email">${_('Notifications Status')}:</label>
25 34 </div>
26 35 <div class="checkboxes">
27 36 <rhodecode-toggle id="live-notifications" active="[[changeNotificationsLoading]]" on-change="toggleNotifications" ${'checked' if c.rhodecode_user.get_instance().user_data.get('notification_status') else ''}></rhodecode-toggle>
28 37 </div>
29 38 </div>
30 <div class="buttons">
31 <a class="btn btn-default" id="test-notification" on-tap="testNotifications">Test notification</a>
39 </div>
32 40 </div>
33 41 </div>
34 42 </div>
35 43
44 <div class="panel panel-default">
45 <div class="panel-heading">
46 <h3 class="panel-title">${_('Test Notifications')}</h3>
36 47 </div>
48 <div class="panel-body">
49
50
51 <div style="padding: 0px 0px 20px 0px">
52 <button class="btn" id="test-notification" on-tap="testNotifications">Test flash message</button>
53 <button class="btn" id="test-notification-live" on-tap="testNotificationsLive">Test live notification</button>
54 </div>
55 <h4 id="test-response"></h4>
56 </div>
57
58 </div>
59
60
61
37 62 </div>
38 63
39 64 <script type="text/javascript">
40 65 /** because im not creating a custom element for this page
41 66 * we need to push the function onto the dom-template
42 67 * ideally we turn this into notification-settings elements
43 68 * then it will be cleaner
44 69 */
45 70 var ctrlr = $('#notificationsPage')[0];
46 71 ctrlr.toggleNotifications = function(event){
47 72 var ajax = $('#toggleNotifications')[0];
48 ajax.headers = {"X-CSRF-Token": CSRF_TOKEN}
73 ajax.headers = {"X-CSRF-Token": CSRF_TOKEN};
49 74 ajax.body = {notification_status:event.target.active};
50 75 ajax.generateRequest();
51 76 };
52 77 ctrlr.handleNotifications = function(event){
53 78 $('#live-notifications')[0].checked = event.detail.response;
54 79 };
55 80
56 81 ctrlr.testNotifications = function(event){
57 82 var levels = ['info', 'error', 'warning', 'success'];
58 83 var level = levels[Math.floor(Math.random()*levels.length)];
59 84 var payload = {
60 85 message: {
61 message: 'This is a test notification.',
86 message: 'This is a test notification. ' + new Date(),
62 87 level: level,
63 88 force: true
64 89 }
65 90 };
66 91 $.Topic('/notifications').publish(payload);
67 }
92 };
93 ctrlr.testNotificationsLive = function(event){
94 var ajax = $('#sendTestNotification')[0];
95 ajax.headers = {"X-CSRF-Token": CSRF_TOKEN};
96 ajax.body = {test_msg: 'Hello !'};
97 ajax.generateRequest();
98 };
99 ctrlr.handleTestNotification = function(event){
100 var reply = event.detail.response.response;
101 reply = reply || 'no reply form server';
102 $('#test-response').html(reply);
103 };
68 104
69 105 </script>
106
70 107 </template>
General Comments 0
You need to be logged in to leave comments. Login now