##// END OF EJS Templates
admin: ported settings controller to pyramid....
marcink -
r2333:c9f5d931 default
parent child Browse files
Show More
This diff has been collapsed as it changes many lines, (754 lines changed) Show them Hide them
@@ -0,0 +1,754 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21
22 import logging
23 import collections
24
25 import datetime
26 import formencode
27 import formencode.htmlfill
28
29 import rhodecode
30 from pyramid.view import view_config
31 from pyramid.httpexceptions import HTTPFound, HTTPNotFound
32 from pyramid.renderers import render
33 from pyramid.response import Response
34
35 from rhodecode.apps._base import BaseAppView
36 from rhodecode.apps.admin.navigation import navigation_list
37 from rhodecode.apps.svn_support.config_keys import generate_config
38 from rhodecode.lib import helpers as h
39 from rhodecode.lib.auth import (
40 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
41 from rhodecode.lib.celerylib import tasks, run_task
42 from rhodecode.lib.utils import repo2db_mapper
43 from rhodecode.lib.utils2 import str2bool, safe_unicode, AttributeDict
44 from rhodecode.lib.index import searcher_from_config
45
46 from rhodecode.model.db import RhodeCodeUi, Repository
47 from rhodecode.model.forms import (ApplicationSettingsForm,
48 ApplicationUiSettingsForm, ApplicationVisualisationForm,
49 LabsSettingsForm, IssueTrackerPatternsForm)
50 from rhodecode.model.repo_group import RepoGroupModel
51
52 from rhodecode.model.scm import ScmModel
53 from rhodecode.model.notification import EmailNotificationModel
54 from rhodecode.model.meta import Session
55 from rhodecode.model.settings import (
56 IssueTrackerSettingsModel, VcsSettingsModel, SettingNotFound,
57 SettingsModel)
58
59
60 log = logging.getLogger(__name__)
61
62
63 class AdminSettingsView(BaseAppView):
64
65 def load_default_context(self):
66 c = self._get_local_tmpl_context()
67 c.labs_active = str2bool(
68 rhodecode.CONFIG.get('labs_settings_active', 'true'))
69 c.navlist = navigation_list(self.request)
70 self._register_global_c(c)
71 return c
72
73 @classmethod
74 def _get_ui_settings(cls):
75 ret = RhodeCodeUi.query().all()
76
77 if not ret:
78 raise Exception('Could not get application ui settings !')
79 settings = {}
80 for each in ret:
81 k = each.ui_key
82 v = each.ui_value
83 if k == '/':
84 k = 'root_path'
85
86 if k in ['push_ssl', 'publish', 'enabled']:
87 v = str2bool(v)
88
89 if k.find('.') != -1:
90 k = k.replace('.', '_')
91
92 if each.ui_section in ['hooks', 'extensions']:
93 v = each.ui_active
94
95 settings[each.ui_section + '_' + k] = v
96 return settings
97
98 @classmethod
99 def _form_defaults(cls):
100 defaults = SettingsModel().get_all_settings()
101 defaults.update(cls._get_ui_settings())
102
103 defaults.update({
104 'new_svn_branch': '',
105 'new_svn_tag': '',
106 })
107 return defaults
108
109 @LoginRequired()
110 @HasPermissionAllDecorator('hg.admin')
111 @view_config(
112 route_name='admin_settings_vcs', request_method='GET',
113 renderer='rhodecode:templates/admin/settings/settings.mako')
114 def settings_vcs(self):
115 c = self.load_default_context()
116 c.active = 'vcs'
117 model = VcsSettingsModel()
118 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
119 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
120
121 settings = self.request.registry.settings
122 c.svn_proxy_generate_config = settings[generate_config]
123
124 defaults = self._form_defaults()
125
126 model.create_largeobjects_dirs_if_needed(defaults['paths_root_path'])
127
128 data = render('rhodecode:templates/admin/settings/settings.mako',
129 self._get_template_context(c), self.request)
130 html = formencode.htmlfill.render(
131 data,
132 defaults=defaults,
133 encoding="UTF-8",
134 force_defaults=False
135 )
136 return Response(html)
137
138 @LoginRequired()
139 @HasPermissionAllDecorator('hg.admin')
140 @CSRFRequired()
141 @view_config(
142 route_name='admin_settings_vcs_update', request_method='POST',
143 renderer='rhodecode:templates/admin/settings/settings.mako')
144 def settings_vcs_update(self):
145 _ = self.request.translate
146 c = self.load_default_context()
147 c.active = 'vcs'
148
149 model = VcsSettingsModel()
150 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
151 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
152
153 settings = self.request.registry.settings
154 c.svn_proxy_generate_config = settings[generate_config]
155
156 application_form = ApplicationUiSettingsForm()()
157
158 try:
159 form_result = application_form.to_python(dict(self.request.POST))
160 except formencode.Invalid as errors:
161 h.flash(
162 _("Some form inputs contain invalid data."),
163 category='error')
164 data = render('rhodecode:templates/admin/settings/settings.mako',
165 self._get_template_context(c), self.request)
166 html = formencode.htmlfill.render(
167 data,
168 defaults=errors.value,
169 errors=errors.error_dict or {},
170 prefix_error=False,
171 encoding="UTF-8",
172 force_defaults=False
173 )
174 return Response(html)
175
176 try:
177 if c.visual.allow_repo_location_change:
178 model.update_global_path_setting(
179 form_result['paths_root_path'])
180
181 model.update_global_ssl_setting(form_result['web_push_ssl'])
182 model.update_global_hook_settings(form_result)
183
184 model.create_or_update_global_svn_settings(form_result)
185 model.create_or_update_global_hg_settings(form_result)
186 model.create_or_update_global_git_settings(form_result)
187 model.create_or_update_global_pr_settings(form_result)
188 except Exception:
189 log.exception("Exception while updating settings")
190 h.flash(_('Error occurred during updating '
191 'application settings'), category='error')
192 else:
193 Session().commit()
194 h.flash(_('Updated VCS settings'), category='success')
195 raise HTTPFound(h.route_path('admin_settings_vcs'))
196
197 data = render('rhodecode:templates/admin/settings/settings.mako',
198 self._get_template_context(c), self.request)
199 html = formencode.htmlfill.render(
200 data,
201 defaults=self._form_defaults(),
202 encoding="UTF-8",
203 force_defaults=False
204 )
205 return Response(html)
206
207 @LoginRequired()
208 @HasPermissionAllDecorator('hg.admin')
209 @CSRFRequired()
210 @view_config(
211 route_name='admin_settings_vcs_svn_pattern_delete', request_method='POST',
212 renderer='json_ext', xhr=True)
213 def settings_vcs_delete_svn_pattern(self):
214 delete_pattern_id = self.request.POST.get('delete_svn_pattern')
215 model = VcsSettingsModel()
216 try:
217 model.delete_global_svn_pattern(delete_pattern_id)
218 except SettingNotFound:
219 log.exception(
220 'Failed to delete svn_pattern with id %s', delete_pattern_id)
221 raise HTTPNotFound()
222
223 Session().commit()
224 return True
225
226 @LoginRequired()
227 @HasPermissionAllDecorator('hg.admin')
228 @view_config(
229 route_name='admin_settings_mapping', request_method='GET',
230 renderer='rhodecode:templates/admin/settings/settings.mako')
231 def settings_mapping(self):
232 c = self.load_default_context()
233 c.active = 'mapping'
234
235 data = render('rhodecode:templates/admin/settings/settings.mako',
236 self._get_template_context(c), self.request)
237 html = formencode.htmlfill.render(
238 data,
239 defaults=self._form_defaults(),
240 encoding="UTF-8",
241 force_defaults=False
242 )
243 return Response(html)
244
245 @LoginRequired()
246 @HasPermissionAllDecorator('hg.admin')
247 @CSRFRequired()
248 @view_config(
249 route_name='admin_settings_mapping_update', request_method='POST',
250 renderer='rhodecode:templates/admin/settings/settings.mako')
251 def settings_mapping_update(self):
252 _ = self.request.translate
253 c = self.load_default_context()
254 c.active = 'mapping'
255 rm_obsolete = self.request.POST.get('destroy', False)
256 invalidate_cache = self.request.POST.get('invalidate', False)
257 log.debug(
258 'rescanning repo location with destroy obsolete=%s', rm_obsolete)
259
260 if invalidate_cache:
261 log.debug('invalidating all repositories cache')
262 for repo in Repository.get_all():
263 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
264
265 filesystem_repos = ScmModel().repo_scan()
266 added, removed = repo2db_mapper(filesystem_repos, rm_obsolete)
267 _repr = lambda l: ', '.join(map(safe_unicode, l)) or '-'
268 h.flash(_('Repositories successfully '
269 'rescanned added: %s ; removed: %s') %
270 (_repr(added), _repr(removed)),
271 category='success')
272 raise HTTPFound(h.route_path('admin_settings_mapping'))
273
274 @LoginRequired()
275 @HasPermissionAllDecorator('hg.admin')
276 @view_config(
277 route_name='admin_settings', request_method='GET',
278 renderer='rhodecode:templates/admin/settings/settings.mako')
279 @view_config(
280 route_name='admin_settings_global', request_method='GET',
281 renderer='rhodecode:templates/admin/settings/settings.mako')
282 def settings_global(self):
283 c = self.load_default_context()
284 c.active = 'global'
285 c.personal_repo_group_default_pattern = RepoGroupModel()\
286 .get_personal_group_name_pattern()
287
288 data = render('rhodecode:templates/admin/settings/settings.mako',
289 self._get_template_context(c), self.request)
290 html = formencode.htmlfill.render(
291 data,
292 defaults=self._form_defaults(),
293 encoding="UTF-8",
294 force_defaults=False
295 )
296 return Response(html)
297
298 @LoginRequired()
299 @HasPermissionAllDecorator('hg.admin')
300 @CSRFRequired()
301 @view_config(
302 route_name='admin_settings_update', request_method='POST',
303 renderer='rhodecode:templates/admin/settings/settings.mako')
304 @view_config(
305 route_name='admin_settings_global_update', request_method='POST',
306 renderer='rhodecode:templates/admin/settings/settings.mako')
307 def settings_global_update(self):
308 _ = self.request.translate
309 c = self.load_default_context()
310 c.active = 'global'
311 c.personal_repo_group_default_pattern = RepoGroupModel()\
312 .get_personal_group_name_pattern()
313 application_form = ApplicationSettingsForm()()
314 try:
315 form_result = application_form.to_python(dict(self.request.POST))
316 except formencode.Invalid as errors:
317 data = render('rhodecode:templates/admin/settings/settings.mako',
318 self._get_template_context(c), self.request)
319 html = formencode.htmlfill.render(
320 data,
321 defaults=errors.value,
322 errors=errors.error_dict or {},
323 prefix_error=False,
324 encoding="UTF-8",
325 force_defaults=False
326 )
327 return Response(html)
328
329 settings = [
330 ('title', 'rhodecode_title', 'unicode'),
331 ('realm', 'rhodecode_realm', 'unicode'),
332 ('pre_code', 'rhodecode_pre_code', 'unicode'),
333 ('post_code', 'rhodecode_post_code', 'unicode'),
334 ('captcha_public_key', 'rhodecode_captcha_public_key', 'unicode'),
335 ('captcha_private_key', 'rhodecode_captcha_private_key', 'unicode'),
336 ('create_personal_repo_group', 'rhodecode_create_personal_repo_group', 'bool'),
337 ('personal_repo_group_pattern', 'rhodecode_personal_repo_group_pattern', 'unicode'),
338 ]
339 try:
340 for setting, form_key, type_ in settings:
341 sett = SettingsModel().create_or_update_setting(
342 setting, form_result[form_key], type_)
343 Session().add(sett)
344
345 Session().commit()
346 SettingsModel().invalidate_settings_cache()
347 h.flash(_('Updated application settings'), category='success')
348 except Exception:
349 log.exception("Exception while updating application settings")
350 h.flash(
351 _('Error occurred during updating application settings'),
352 category='error')
353
354 raise HTTPFound(h.route_path('admin_settings_global'))
355
356 @LoginRequired()
357 @HasPermissionAllDecorator('hg.admin')
358 @view_config(
359 route_name='admin_settings_visual', request_method='GET',
360 renderer='rhodecode:templates/admin/settings/settings.mako')
361 def settings_visual(self):
362 c = self.load_default_context()
363 c.active = 'visual'
364
365 data = render('rhodecode:templates/admin/settings/settings.mako',
366 self._get_template_context(c), self.request)
367 html = formencode.htmlfill.render(
368 data,
369 defaults=self._form_defaults(),
370 encoding="UTF-8",
371 force_defaults=False
372 )
373 return Response(html)
374
375 @LoginRequired()
376 @HasPermissionAllDecorator('hg.admin')
377 @CSRFRequired()
378 @view_config(
379 route_name='admin_settings_visual_update', request_method='POST',
380 renderer='rhodecode:templates/admin/settings/settings.mako')
381 def settings_visual_update(self):
382 _ = self.request.translate
383 c = self.load_default_context()
384 c.active = 'visual'
385 application_form = ApplicationVisualisationForm()()
386 try:
387 form_result = application_form.to_python(dict(self.request.POST))
388 except formencode.Invalid as errors:
389 data = render('rhodecode:templates/admin/settings/settings.mako',
390 self._get_template_context(c), self.request)
391 html = formencode.htmlfill.render(
392 data,
393 defaults=errors.value,
394 errors=errors.error_dict or {},
395 prefix_error=False,
396 encoding="UTF-8",
397 force_defaults=False
398 )
399 return Response(html)
400
401 try:
402 settings = [
403 ('show_public_icon', 'rhodecode_show_public_icon', 'bool'),
404 ('show_private_icon', 'rhodecode_show_private_icon', 'bool'),
405 ('stylify_metatags', 'rhodecode_stylify_metatags', 'bool'),
406 ('repository_fields', 'rhodecode_repository_fields', 'bool'),
407 ('dashboard_items', 'rhodecode_dashboard_items', 'int'),
408 ('admin_grid_items', 'rhodecode_admin_grid_items', 'int'),
409 ('show_version', 'rhodecode_show_version', 'bool'),
410 ('use_gravatar', 'rhodecode_use_gravatar', 'bool'),
411 ('markup_renderer', 'rhodecode_markup_renderer', 'unicode'),
412 ('gravatar_url', 'rhodecode_gravatar_url', 'unicode'),
413 ('clone_uri_tmpl', 'rhodecode_clone_uri_tmpl', 'unicode'),
414 ('support_url', 'rhodecode_support_url', 'unicode'),
415 ('show_revision_number', 'rhodecode_show_revision_number', 'bool'),
416 ('show_sha_length', 'rhodecode_show_sha_length', 'int'),
417 ]
418 for setting, form_key, type_ in settings:
419 sett = SettingsModel().create_or_update_setting(
420 setting, form_result[form_key], type_)
421 Session().add(sett)
422
423 Session().commit()
424 SettingsModel().invalidate_settings_cache()
425 h.flash(_('Updated visualisation settings'), category='success')
426 except Exception:
427 log.exception("Exception updating visualization settings")
428 h.flash(_('Error occurred during updating '
429 'visualisation settings'),
430 category='error')
431
432 raise HTTPFound(h.route_path('admin_settings_visual'))
433
434 @LoginRequired()
435 @HasPermissionAllDecorator('hg.admin')
436 @view_config(
437 route_name='admin_settings_issuetracker', request_method='GET',
438 renderer='rhodecode:templates/admin/settings/settings.mako')
439 def settings_issuetracker(self):
440 c = self.load_default_context()
441 c.active = 'issuetracker'
442 defaults = SettingsModel().get_all_settings()
443
444 entry_key = 'rhodecode_issuetracker_pat_'
445
446 c.issuetracker_entries = {}
447 for k, v in defaults.items():
448 if k.startswith(entry_key):
449 uid = k[len(entry_key):]
450 c.issuetracker_entries[uid] = None
451
452 for uid in c.issuetracker_entries:
453 c.issuetracker_entries[uid] = AttributeDict({
454 'pat': defaults.get('rhodecode_issuetracker_pat_' + uid),
455 'url': defaults.get('rhodecode_issuetracker_url_' + uid),
456 'pref': defaults.get('rhodecode_issuetracker_pref_' + uid),
457 'desc': defaults.get('rhodecode_issuetracker_desc_' + uid),
458 })
459
460 return self._get_template_context(c)
461
462 @LoginRequired()
463 @HasPermissionAllDecorator('hg.admin')
464 @CSRFRequired()
465 @view_config(
466 route_name='admin_settings_issuetracker_test', request_method='POST',
467 renderer='string', xhr=True)
468 def settings_issuetracker_test(self):
469 return h.urlify_commit_message(
470 self.request.POST.get('test_text', ''),
471 'repo_group/test_repo1')
472
473 @LoginRequired()
474 @HasPermissionAllDecorator('hg.admin')
475 @CSRFRequired()
476 @view_config(
477 route_name='admin_settings_issuetracker_update', request_method='POST',
478 renderer='rhodecode:templates/admin/settings/settings.mako')
479 def settings_issuetracker_update(self):
480 _ = self.request.translate
481 self.load_default_context()
482 settings_model = IssueTrackerSettingsModel()
483
484 form = IssueTrackerPatternsForm()().to_python(self.request.POST)
485 if form:
486 for uid in form.get('delete_patterns', []):
487 settings_model.delete_entries(uid)
488
489 for pattern in form.get('patterns', []):
490 for setting, value, type_ in pattern:
491 sett = settings_model.create_or_update_setting(
492 setting, value, type_)
493 Session().add(sett)
494
495 Session().commit()
496
497 SettingsModel().invalidate_settings_cache()
498 h.flash(_('Updated issue tracker entries'), category='success')
499 raise HTTPFound(h.route_path('admin_settings_issuetracker'))
500
501 @LoginRequired()
502 @HasPermissionAllDecorator('hg.admin')
503 @CSRFRequired()
504 @view_config(
505 route_name='admin_settings_issuetracker_delete', request_method='POST',
506 renderer='rhodecode:templates/admin/settings/settings.mako')
507 def settings_issuetracker_delete(self):
508 _ = self.request.translate
509 self.load_default_context()
510 uid = self.request.POST.get('uid')
511 try:
512 IssueTrackerSettingsModel().delete_entries(uid)
513 except Exception:
514 log.exception('Failed to delete issue tracker setting %s', uid)
515 raise HTTPNotFound()
516 h.flash(_('Removed issue tracker entry'), category='success')
517 raise HTTPFound(h.route_path('admin_settings_issuetracker'))
518
519 @LoginRequired()
520 @HasPermissionAllDecorator('hg.admin')
521 @view_config(
522 route_name='admin_settings_email', request_method='GET',
523 renderer='rhodecode:templates/admin/settings/settings.mako')
524 def settings_email(self):
525 c = self.load_default_context()
526 c.active = 'email'
527 c.rhodecode_ini = rhodecode.CONFIG
528
529 data = render('rhodecode:templates/admin/settings/settings.mako',
530 self._get_template_context(c), self.request)
531 html = formencode.htmlfill.render(
532 data,
533 defaults=self._form_defaults(),
534 encoding="UTF-8",
535 force_defaults=False
536 )
537 return Response(html)
538
539 @LoginRequired()
540 @HasPermissionAllDecorator('hg.admin')
541 @CSRFRequired()
542 @view_config(
543 route_name='admin_settings_email_update', request_method='POST',
544 renderer='rhodecode:templates/admin/settings/settings.mako')
545 def settings_email_update(self):
546 _ = self.request.translate
547 c = self.load_default_context()
548 c.active = 'email'
549
550 test_email = self.request.POST.get('test_email')
551
552 if not test_email:
553 h.flash(_('Please enter email address'), category='error')
554 raise HTTPFound(h.route_path('admin_settings_email'))
555
556 email_kwargs = {
557 'date': datetime.datetime.now(),
558 'user': c.rhodecode_user,
559 'rhodecode_version': c.rhodecode_version
560 }
561
562 (subject, headers, email_body,
563 email_body_plaintext) = EmailNotificationModel().render_email(
564 EmailNotificationModel.TYPE_EMAIL_TEST, **email_kwargs)
565
566 recipients = [test_email] if test_email else None
567
568 run_task(tasks.send_email, recipients, subject,
569 email_body_plaintext, email_body)
570
571 h.flash(_('Send email task created'), category='success')
572 raise HTTPFound(h.route_path('admin_settings_email'))
573
574 @LoginRequired()
575 @HasPermissionAllDecorator('hg.admin')
576 @view_config(
577 route_name='admin_settings_hooks', request_method='GET',
578 renderer='rhodecode:templates/admin/settings/settings.mako')
579 def settings_hooks(self):
580 c = self.load_default_context()
581 c.active = 'hooks'
582
583 model = SettingsModel()
584 c.hooks = model.get_builtin_hooks()
585 c.custom_hooks = model.get_custom_hooks()
586
587 data = render('rhodecode:templates/admin/settings/settings.mako',
588 self._get_template_context(c), self.request)
589 html = formencode.htmlfill.render(
590 data,
591 defaults=self._form_defaults(),
592 encoding="UTF-8",
593 force_defaults=False
594 )
595 return Response(html)
596
597 @LoginRequired()
598 @HasPermissionAllDecorator('hg.admin')
599 @CSRFRequired()
600 @view_config(
601 route_name='admin_settings_hooks_update', request_method='POST',
602 renderer='rhodecode:templates/admin/settings/settings.mako')
603 @view_config(
604 route_name='admin_settings_hooks_delete', request_method='POST',
605 renderer='rhodecode:templates/admin/settings/settings.mako')
606 def settings_hooks_update(self):
607 _ = self.request.translate
608 c = self.load_default_context()
609 c.active = 'hooks'
610 if c.visual.allow_custom_hooks_settings:
611 ui_key = self.request.POST.get('new_hook_ui_key')
612 ui_value = self.request.POST.get('new_hook_ui_value')
613
614 hook_id = self.request.POST.get('hook_id')
615 new_hook = False
616
617 model = SettingsModel()
618 try:
619 if ui_value and ui_key:
620 model.create_or_update_hook(ui_key, ui_value)
621 h.flash(_('Added new hook'), category='success')
622 new_hook = True
623 elif hook_id:
624 RhodeCodeUi.delete(hook_id)
625 Session().commit()
626
627 # check for edits
628 update = False
629 _d = self.request.POST.dict_of_lists()
630 for k, v in zip(_d.get('hook_ui_key', []),
631 _d.get('hook_ui_value_new', [])):
632 model.create_or_update_hook(k, v)
633 update = True
634
635 if update and not new_hook:
636 h.flash(_('Updated hooks'), category='success')
637 Session().commit()
638 except Exception:
639 log.exception("Exception during hook creation")
640 h.flash(_('Error occurred during hook creation'),
641 category='error')
642
643 raise HTTPFound(h.route_path('admin_settings_hooks'))
644
645 @LoginRequired()
646 @HasPermissionAllDecorator('hg.admin')
647 @view_config(
648 route_name='admin_settings_search', request_method='GET',
649 renderer='rhodecode:templates/admin/settings/settings.mako')
650 def settings_search(self):
651 c = self.load_default_context()
652 c.active = 'search'
653
654 searcher = searcher_from_config(self.request.registry.settings)
655 c.statistics = searcher.statistics()
656
657 return self._get_template_context(c)
658
659 @LoginRequired()
660 @HasPermissionAllDecorator('hg.admin')
661 @view_config(
662 route_name='admin_settings_labs', request_method='GET',
663 renderer='rhodecode:templates/admin/settings/settings.mako')
664 def settings_labs(self):
665 c = self.load_default_context()
666 if not c.labs_active:
667 raise HTTPFound(h.route_path('admin_settings'))
668
669 c.active = 'labs'
670 c.lab_settings = _LAB_SETTINGS
671
672 data = render('rhodecode:templates/admin/settings/settings.mako',
673 self._get_template_context(c), self.request)
674 html = formencode.htmlfill.render(
675 data,
676 defaults=self._form_defaults(),
677 encoding="UTF-8",
678 force_defaults=False
679 )
680 return Response(html)
681
682 @LoginRequired()
683 @HasPermissionAllDecorator('hg.admin')
684 @CSRFRequired()
685 @view_config(
686 route_name='admin_settings_labs_update', request_method='POST',
687 renderer='rhodecode:templates/admin/settings/settings.mako')
688 def settings_labs_update(self):
689 _ = self.request.translate
690 c = self.load_default_context()
691 c.active = 'labs'
692
693 application_form = LabsSettingsForm()()
694 try:
695 form_result = application_form.to_python(dict(self.request.POST))
696 except formencode.Invalid as errors:
697 h.flash(
698 _('Some form inputs contain invalid data.'),
699 category='error')
700 data = render('rhodecode:templates/admin/settings/settings.mako',
701 self._get_template_context(c), self.request)
702 html = formencode.htmlfill.render(
703 data,
704 defaults=errors.value,
705 errors=errors.error_dict or {},
706 prefix_error=False,
707 encoding="UTF-8",
708 force_defaults=False
709 )
710 return Response(html)
711
712 try:
713 session = Session()
714 for setting in _LAB_SETTINGS:
715 setting_name = setting.key[len('rhodecode_'):]
716 sett = SettingsModel().create_or_update_setting(
717 setting_name, form_result[setting.key], setting.type)
718 session.add(sett)
719
720 except Exception:
721 log.exception('Exception while updating lab settings')
722 h.flash(_('Error occurred during updating labs settings'),
723 category='error')
724 else:
725 Session().commit()
726 SettingsModel().invalidate_settings_cache()
727 h.flash(_('Updated Labs settings'), category='success')
728 raise HTTPFound(h.route_path('admin_settings_labs'))
729
730 data = render('rhodecode:templates/admin/settings/settings.mako',
731 self._get_template_context(c), self.request)
732 html = formencode.htmlfill.render(
733 data,
734 defaults=self._form_defaults(),
735 encoding="UTF-8",
736 force_defaults=False
737 )
738 return Response(html)
739
740
741 # :param key: name of the setting including the 'rhodecode_' prefix
742 # :param type: the RhodeCodeSetting type to use.
743 # :param group: the i18ned group in which we should dispaly this setting
744 # :param label: the i18ned label we should display for this setting
745 # :param help: the i18ned help we should dispaly for this setting
746 LabSetting = collections.namedtuple(
747 'LabSetting', ('key', 'type', 'group', 'label', 'help'))
748
749
750 # This list has to be kept in sync with the form
751 # rhodecode.model.forms.LabsSettingsForm.
752 _LAB_SETTINGS = [
753
754 ]
@@ -1,322 +1,403 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 from rhodecode.apps._base import ADMIN_PREFIX
22 from rhodecode.apps._base import ADMIN_PREFIX
23 from rhodecode.lib.utils2 import str2bool
24
23
25
24
26 def admin_routes(config):
25 def admin_routes(config):
27 """
26 """
28 Admin prefixed routes
27 Admin prefixed routes
29 """
28 """
30
29
31 config.add_route(
30 config.add_route(
32 name='admin_audit_logs',
31 name='admin_audit_logs',
33 pattern='/audit_logs')
32 pattern='/audit_logs')
34
33
35 config.add_route(
34 config.add_route(
36 name='admin_audit_log_entry',
35 name='admin_audit_log_entry',
37 pattern='/audit_logs/{audit_log_id}')
36 pattern='/audit_logs/{audit_log_id}')
38
37
39 config.add_route(
38 config.add_route(
40 name='pull_requests_global_0', # backward compat
39 name='pull_requests_global_0', # backward compat
41 pattern='/pull_requests/{pull_request_id:\d+}')
40 pattern='/pull_requests/{pull_request_id:\d+}')
42 config.add_route(
41 config.add_route(
43 name='pull_requests_global_1', # backward compat
42 name='pull_requests_global_1', # backward compat
44 pattern='/pull-requests/{pull_request_id:\d+}')
43 pattern='/pull-requests/{pull_request_id:\d+}')
45 config.add_route(
44 config.add_route(
46 name='pull_requests_global',
45 name='pull_requests_global',
47 pattern='/pull-request/{pull_request_id:\d+}')
46 pattern='/pull-request/{pull_request_id:\d+}')
48
47
49 config.add_route(
48 config.add_route(
50 name='admin_settings_open_source',
49 name='admin_settings_open_source',
51 pattern='/settings/open_source')
50 pattern='/settings/open_source')
52 config.add_route(
51 config.add_route(
53 name='admin_settings_vcs_svn_generate_cfg',
52 name='admin_settings_vcs_svn_generate_cfg',
54 pattern='/settings/vcs/svn_generate_cfg')
53 pattern='/settings/vcs/svn_generate_cfg')
55
54
56 config.add_route(
55 config.add_route(
57 name='admin_settings_system',
56 name='admin_settings_system',
58 pattern='/settings/system')
57 pattern='/settings/system')
59 config.add_route(
58 config.add_route(
60 name='admin_settings_system_update',
59 name='admin_settings_system_update',
61 pattern='/settings/system/updates')
60 pattern='/settings/system/updates')
62
61
63 config.add_route(
62 config.add_route(
64 name='admin_settings_sessions',
63 name='admin_settings_sessions',
65 pattern='/settings/sessions')
64 pattern='/settings/sessions')
66 config.add_route(
65 config.add_route(
67 name='admin_settings_sessions_cleanup',
66 name='admin_settings_sessions_cleanup',
68 pattern='/settings/sessions/cleanup')
67 pattern='/settings/sessions/cleanup')
69
68
70 config.add_route(
69 config.add_route(
71 name='admin_settings_process_management',
70 name='admin_settings_process_management',
72 pattern='/settings/process_management')
71 pattern='/settings/process_management')
73 config.add_route(
72 config.add_route(
74 name='admin_settings_process_management_signal',
73 name='admin_settings_process_management_signal',
75 pattern='/settings/process_management/signal')
74 pattern='/settings/process_management/signal')
76
75
77 # default settings
76 # default settings
78 config.add_route(
77 config.add_route(
79 name='admin_defaults_repositories',
78 name='admin_defaults_repositories',
80 pattern='/defaults/repositories')
79 pattern='/defaults/repositories')
81 config.add_route(
80 config.add_route(
82 name='admin_defaults_repositories_update',
81 name='admin_defaults_repositories_update',
83 pattern='/defaults/repositories/update')
82 pattern='/defaults/repositories/update')
84
83
84 # admin settings
85
86 config.add_route(
87 name='admin_settings',
88 pattern='/settings')
89 config.add_route(
90 name='admin_settings_update',
91 pattern='/settings/update')
92
93 config.add_route(
94 name='admin_settings_global',
95 pattern='/settings/global')
96 config.add_route(
97 name='admin_settings_global_update',
98 pattern='/settings/global/update')
99
100 config.add_route(
101 name='admin_settings_vcs',
102 pattern='/settings/vcs')
103 config.add_route(
104 name='admin_settings_vcs_update',
105 pattern='/settings/vcs/update')
106 config.add_route(
107 name='admin_settings_vcs_svn_pattern_delete',
108 pattern='/settings/vcs/svn_pattern_delete')
109
110 config.add_route(
111 name='admin_settings_mapping',
112 pattern='/settings/mapping')
113 config.add_route(
114 name='admin_settings_mapping_update',
115 pattern='/settings/mapping/update')
116
117 config.add_route(
118 name='admin_settings_visual',
119 pattern='/settings/visual')
120 config.add_route(
121 name='admin_settings_visual_update',
122 pattern='/settings/visual/update')
123
124
125 config.add_route(
126 name='admin_settings_issuetracker',
127 pattern='/settings/issue-tracker')
128 config.add_route(
129 name='admin_settings_issuetracker_update',
130 pattern='/settings/issue-tracker/update')
131 config.add_route(
132 name='admin_settings_issuetracker_test',
133 pattern='/settings/issue-tracker/test')
134 config.add_route(
135 name='admin_settings_issuetracker_delete',
136 pattern='/settings/issue-tracker/delete')
137
138 config.add_route(
139 name='admin_settings_email',
140 pattern='/settings/email')
141 config.add_route(
142 name='admin_settings_email_update',
143 pattern='/settings/email/update')
144
145 config.add_route(
146 name='admin_settings_hooks',
147 pattern='/settings/hooks')
148 config.add_route(
149 name='admin_settings_hooks_update',
150 pattern='/settings/hooks/update')
151 config.add_route(
152 name='admin_settings_hooks_delete',
153 pattern='/settings/hooks/delete')
154
155 config.add_route(
156 name='admin_settings_search',
157 pattern='/settings/search')
158
159 config.add_route(
160 name='admin_settings_labs',
161 pattern='/settings/labs')
162 config.add_route(
163 name='admin_settings_labs_update',
164 pattern='/settings/labs/update')
165
85 # global permissions
166 # global permissions
86
167
87 config.add_route(
168 config.add_route(
88 name='admin_permissions_application',
169 name='admin_permissions_application',
89 pattern='/permissions/application')
170 pattern='/permissions/application')
90 config.add_route(
171 config.add_route(
91 name='admin_permissions_application_update',
172 name='admin_permissions_application_update',
92 pattern='/permissions/application/update')
173 pattern='/permissions/application/update')
93
174
94 config.add_route(
175 config.add_route(
95 name='admin_permissions_global',
176 name='admin_permissions_global',
96 pattern='/permissions/global')
177 pattern='/permissions/global')
97 config.add_route(
178 config.add_route(
98 name='admin_permissions_global_update',
179 name='admin_permissions_global_update',
99 pattern='/permissions/global/update')
180 pattern='/permissions/global/update')
100
181
101 config.add_route(
182 config.add_route(
102 name='admin_permissions_object',
183 name='admin_permissions_object',
103 pattern='/permissions/object')
184 pattern='/permissions/object')
104 config.add_route(
185 config.add_route(
105 name='admin_permissions_object_update',
186 name='admin_permissions_object_update',
106 pattern='/permissions/object/update')
187 pattern='/permissions/object/update')
107
188
108 config.add_route(
189 config.add_route(
109 name='admin_permissions_ips',
190 name='admin_permissions_ips',
110 pattern='/permissions/ips')
191 pattern='/permissions/ips')
111
192
112 config.add_route(
193 config.add_route(
113 name='admin_permissions_overview',
194 name='admin_permissions_overview',
114 pattern='/permissions/overview')
195 pattern='/permissions/overview')
115
196
116 config.add_route(
197 config.add_route(
117 name='admin_permissions_auth_token_access',
198 name='admin_permissions_auth_token_access',
118 pattern='/permissions/auth_token_access')
199 pattern='/permissions/auth_token_access')
119
200
120 config.add_route(
201 config.add_route(
121 name='admin_permissions_ssh_keys',
202 name='admin_permissions_ssh_keys',
122 pattern='/permissions/ssh_keys')
203 pattern='/permissions/ssh_keys')
123 config.add_route(
204 config.add_route(
124 name='admin_permissions_ssh_keys_data',
205 name='admin_permissions_ssh_keys_data',
125 pattern='/permissions/ssh_keys/data')
206 pattern='/permissions/ssh_keys/data')
126 config.add_route(
207 config.add_route(
127 name='admin_permissions_ssh_keys_update',
208 name='admin_permissions_ssh_keys_update',
128 pattern='/permissions/ssh_keys/update')
209 pattern='/permissions/ssh_keys/update')
129
210
130 # users admin
211 # users admin
131 config.add_route(
212 config.add_route(
132 name='users',
213 name='users',
133 pattern='/users')
214 pattern='/users')
134
215
135 config.add_route(
216 config.add_route(
136 name='users_data',
217 name='users_data',
137 pattern='/users_data')
218 pattern='/users_data')
138
219
139 config.add_route(
220 config.add_route(
140 name='users_create',
221 name='users_create',
141 pattern='/users/create')
222 pattern='/users/create')
142
223
143 config.add_route(
224 config.add_route(
144 name='users_new',
225 name='users_new',
145 pattern='/users/new')
226 pattern='/users/new')
146
227
147 # user management
228 # user management
148 config.add_route(
229 config.add_route(
149 name='user_edit',
230 name='user_edit',
150 pattern='/users/{user_id:\d+}/edit',
231 pattern='/users/{user_id:\d+}/edit',
151 user_route=True)
232 user_route=True)
152 config.add_route(
233 config.add_route(
153 name='user_edit_advanced',
234 name='user_edit_advanced',
154 pattern='/users/{user_id:\d+}/edit/advanced',
235 pattern='/users/{user_id:\d+}/edit/advanced',
155 user_route=True)
236 user_route=True)
156 config.add_route(
237 config.add_route(
157 name='user_edit_global_perms',
238 name='user_edit_global_perms',
158 pattern='/users/{user_id:\d+}/edit/global_permissions',
239 pattern='/users/{user_id:\d+}/edit/global_permissions',
159 user_route=True)
240 user_route=True)
160 config.add_route(
241 config.add_route(
161 name='user_edit_global_perms_update',
242 name='user_edit_global_perms_update',
162 pattern='/users/{user_id:\d+}/edit/global_permissions/update',
243 pattern='/users/{user_id:\d+}/edit/global_permissions/update',
163 user_route=True)
244 user_route=True)
164 config.add_route(
245 config.add_route(
165 name='user_update',
246 name='user_update',
166 pattern='/users/{user_id:\d+}/update',
247 pattern='/users/{user_id:\d+}/update',
167 user_route=True)
248 user_route=True)
168 config.add_route(
249 config.add_route(
169 name='user_delete',
250 name='user_delete',
170 pattern='/users/{user_id:\d+}/delete',
251 pattern='/users/{user_id:\d+}/delete',
171 user_route=True)
252 user_route=True)
172 config.add_route(
253 config.add_route(
173 name='user_force_password_reset',
254 name='user_force_password_reset',
174 pattern='/users/{user_id:\d+}/password_reset',
255 pattern='/users/{user_id:\d+}/password_reset',
175 user_route=True)
256 user_route=True)
176 config.add_route(
257 config.add_route(
177 name='user_create_personal_repo_group',
258 name='user_create_personal_repo_group',
178 pattern='/users/{user_id:\d+}/create_repo_group',
259 pattern='/users/{user_id:\d+}/create_repo_group',
179 user_route=True)
260 user_route=True)
180
261
181 # user auth tokens
262 # user auth tokens
182 config.add_route(
263 config.add_route(
183 name='edit_user_auth_tokens',
264 name='edit_user_auth_tokens',
184 pattern='/users/{user_id:\d+}/edit/auth_tokens',
265 pattern='/users/{user_id:\d+}/edit/auth_tokens',
185 user_route=True)
266 user_route=True)
186 config.add_route(
267 config.add_route(
187 name='edit_user_auth_tokens_add',
268 name='edit_user_auth_tokens_add',
188 pattern='/users/{user_id:\d+}/edit/auth_tokens/new',
269 pattern='/users/{user_id:\d+}/edit/auth_tokens/new',
189 user_route=True)
270 user_route=True)
190 config.add_route(
271 config.add_route(
191 name='edit_user_auth_tokens_delete',
272 name='edit_user_auth_tokens_delete',
192 pattern='/users/{user_id:\d+}/edit/auth_tokens/delete',
273 pattern='/users/{user_id:\d+}/edit/auth_tokens/delete',
193 user_route=True)
274 user_route=True)
194
275
195 # user ssh keys
276 # user ssh keys
196 config.add_route(
277 config.add_route(
197 name='edit_user_ssh_keys',
278 name='edit_user_ssh_keys',
198 pattern='/users/{user_id:\d+}/edit/ssh_keys',
279 pattern='/users/{user_id:\d+}/edit/ssh_keys',
199 user_route=True)
280 user_route=True)
200 config.add_route(
281 config.add_route(
201 name='edit_user_ssh_keys_generate_keypair',
282 name='edit_user_ssh_keys_generate_keypair',
202 pattern='/users/{user_id:\d+}/edit/ssh_keys/generate',
283 pattern='/users/{user_id:\d+}/edit/ssh_keys/generate',
203 user_route=True)
284 user_route=True)
204 config.add_route(
285 config.add_route(
205 name='edit_user_ssh_keys_add',
286 name='edit_user_ssh_keys_add',
206 pattern='/users/{user_id:\d+}/edit/ssh_keys/new',
287 pattern='/users/{user_id:\d+}/edit/ssh_keys/new',
207 user_route=True)
288 user_route=True)
208 config.add_route(
289 config.add_route(
209 name='edit_user_ssh_keys_delete',
290 name='edit_user_ssh_keys_delete',
210 pattern='/users/{user_id:\d+}/edit/ssh_keys/delete',
291 pattern='/users/{user_id:\d+}/edit/ssh_keys/delete',
211 user_route=True)
292 user_route=True)
212
293
213 # user emails
294 # user emails
214 config.add_route(
295 config.add_route(
215 name='edit_user_emails',
296 name='edit_user_emails',
216 pattern='/users/{user_id:\d+}/edit/emails',
297 pattern='/users/{user_id:\d+}/edit/emails',
217 user_route=True)
298 user_route=True)
218 config.add_route(
299 config.add_route(
219 name='edit_user_emails_add',
300 name='edit_user_emails_add',
220 pattern='/users/{user_id:\d+}/edit/emails/new',
301 pattern='/users/{user_id:\d+}/edit/emails/new',
221 user_route=True)
302 user_route=True)
222 config.add_route(
303 config.add_route(
223 name='edit_user_emails_delete',
304 name='edit_user_emails_delete',
224 pattern='/users/{user_id:\d+}/edit/emails/delete',
305 pattern='/users/{user_id:\d+}/edit/emails/delete',
225 user_route=True)
306 user_route=True)
226
307
227 # user IPs
308 # user IPs
228 config.add_route(
309 config.add_route(
229 name='edit_user_ips',
310 name='edit_user_ips',
230 pattern='/users/{user_id:\d+}/edit/ips',
311 pattern='/users/{user_id:\d+}/edit/ips',
231 user_route=True)
312 user_route=True)
232 config.add_route(
313 config.add_route(
233 name='edit_user_ips_add',
314 name='edit_user_ips_add',
234 pattern='/users/{user_id:\d+}/edit/ips/new',
315 pattern='/users/{user_id:\d+}/edit/ips/new',
235 user_route_with_default=True) # enabled for default user too
316 user_route_with_default=True) # enabled for default user too
236 config.add_route(
317 config.add_route(
237 name='edit_user_ips_delete',
318 name='edit_user_ips_delete',
238 pattern='/users/{user_id:\d+}/edit/ips/delete',
319 pattern='/users/{user_id:\d+}/edit/ips/delete',
239 user_route_with_default=True) # enabled for default user too
320 user_route_with_default=True) # enabled for default user too
240
321
241 # user perms
322 # user perms
242 config.add_route(
323 config.add_route(
243 name='edit_user_perms_summary',
324 name='edit_user_perms_summary',
244 pattern='/users/{user_id:\d+}/edit/permissions_summary',
325 pattern='/users/{user_id:\d+}/edit/permissions_summary',
245 user_route=True)
326 user_route=True)
246 config.add_route(
327 config.add_route(
247 name='edit_user_perms_summary_json',
328 name='edit_user_perms_summary_json',
248 pattern='/users/{user_id:\d+}/edit/permissions_summary/json',
329 pattern='/users/{user_id:\d+}/edit/permissions_summary/json',
249 user_route=True)
330 user_route=True)
250
331
251 # user user groups management
332 # user user groups management
252 config.add_route(
333 config.add_route(
253 name='edit_user_groups_management',
334 name='edit_user_groups_management',
254 pattern='/users/{user_id:\d+}/edit/groups_management',
335 pattern='/users/{user_id:\d+}/edit/groups_management',
255 user_route=True)
336 user_route=True)
256
337
257 config.add_route(
338 config.add_route(
258 name='edit_user_groups_management_updates',
339 name='edit_user_groups_management_updates',
259 pattern='/users/{user_id:\d+}/edit/edit_user_groups_management/updates',
340 pattern='/users/{user_id:\d+}/edit/edit_user_groups_management/updates',
260 user_route=True)
341 user_route=True)
261
342
262 # user audit logs
343 # user audit logs
263 config.add_route(
344 config.add_route(
264 name='edit_user_audit_logs',
345 name='edit_user_audit_logs',
265 pattern='/users/{user_id:\d+}/edit/audit', user_route=True)
346 pattern='/users/{user_id:\d+}/edit/audit', user_route=True)
266
347
267 # user-groups admin
348 # user-groups admin
268 config.add_route(
349 config.add_route(
269 name='user_groups',
350 name='user_groups',
270 pattern='/user_groups')
351 pattern='/user_groups')
271
352
272 config.add_route(
353 config.add_route(
273 name='user_groups_data',
354 name='user_groups_data',
274 pattern='/user_groups_data')
355 pattern='/user_groups_data')
275
356
276 config.add_route(
357 config.add_route(
277 name='user_groups_new',
358 name='user_groups_new',
278 pattern='/user_groups/new')
359 pattern='/user_groups/new')
279
360
280 config.add_route(
361 config.add_route(
281 name='user_groups_create',
362 name='user_groups_create',
282 pattern='/user_groups/create')
363 pattern='/user_groups/create')
283
364
284 # repos admin
365 # repos admin
285 config.add_route(
366 config.add_route(
286 name='repos',
367 name='repos',
287 pattern='/repos')
368 pattern='/repos')
288
369
289 config.add_route(
370 config.add_route(
290 name='repo_new',
371 name='repo_new',
291 pattern='/repos/new')
372 pattern='/repos/new')
292
373
293 config.add_route(
374 config.add_route(
294 name='repo_create',
375 name='repo_create',
295 pattern='/repos/create')
376 pattern='/repos/create')
296
377
297 # repo groups admin
378 # repo groups admin
298 config.add_route(
379 config.add_route(
299 name='repo_groups',
380 name='repo_groups',
300 pattern='/repo_groups')
381 pattern='/repo_groups')
301
382
302 config.add_route(
383 config.add_route(
303 name='repo_group_new',
384 name='repo_group_new',
304 pattern='/repo_group/new')
385 pattern='/repo_group/new')
305
386
306 config.add_route(
387 config.add_route(
307 name='repo_group_create',
388 name='repo_group_create',
308 pattern='/repo_group/create')
389 pattern='/repo_group/create')
309
390
310
391
311 def includeme(config):
392 def includeme(config):
312 from rhodecode.apps.admin.navigation import includeme as nav_includeme
393 from rhodecode.apps.admin.navigation import includeme as nav_includeme
313
394
314 # Create admin navigation registry and add it to the pyramid registry.
395 # Create admin navigation registry and add it to the pyramid registry.
315 nav_includeme(config)
396 nav_includeme(config)
316
397
317 # main admin routes
398 # main admin routes
318 config.add_route(name='admin_home', pattern=ADMIN_PREFIX)
399 config.add_route(name='admin_home', pattern=ADMIN_PREFIX)
319 config.include(admin_routes, route_prefix=ADMIN_PREFIX)
400 config.include(admin_routes, route_prefix=ADMIN_PREFIX)
320
401
321 # Scan module for configuration decorators.
402 # Scan module for configuration decorators.
322 config.scan('.views', ignore='.tests')
403 config.scan('.views', ignore='.tests')
@@ -1,148 +1,136 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 import logging
22 import logging
23 import collections
23 import collections
24
24
25 from zope.interface import implementer
25 from zope.interface import implementer
26
26
27 from rhodecode.apps.admin.interfaces import IAdminNavigationRegistry
27 from rhodecode.apps.admin.interfaces import IAdminNavigationRegistry
28 from rhodecode.lib.utils import get_registry
28 from rhodecode.lib.utils import get_registry
29 from rhodecode.lib.utils2 import str2bool
29 from rhodecode.lib.utils2 import str2bool
30 from rhodecode.translation import _
30 from rhodecode.translation import _
31
31
32
32
33 log = logging.getLogger(__name__)
33 log = logging.getLogger(__name__)
34
34
35 NavListEntry = collections.namedtuple('NavListEntry', ['key', 'name', 'url'])
35 NavListEntry = collections.namedtuple('NavListEntry', ['key', 'name', 'url'])
36
36
37
37
38 class NavEntry(object):
38 class NavEntry(object):
39 """
39 """
40 Represents an entry in the admin navigation.
40 Represents an entry in the admin navigation.
41
41
42 :param key: Unique identifier used to store reference in an OrderedDict.
42 :param key: Unique identifier used to store reference in an OrderedDict.
43 :param name: Display name, usually a translation string.
43 :param name: Display name, usually a translation string.
44 :param view_name: Name of the view, used generate the URL.
44 :param view_name: Name of the view, used generate the URL.
45 :param pyramid: Indicator to use pyramid for URL generation. This should
45 :param pyramid: Indicator to use pyramid for URL generation. This should
46 be removed as soon as we are fully migrated to pyramid.
46 be removed as soon as we are fully migrated to pyramid.
47 """
47 """
48
48
49 def __init__(self, key, name, view_name, pyramid=False):
49 def __init__(self, key, name, view_name):
50 self.key = key
50 self.key = key
51 self.name = name
51 self.name = name
52 self.view_name = view_name
52 self.view_name = view_name
53 self.pyramid = pyramid
54
53
55 def generate_url(self, request):
54 def generate_url(self, request):
56 if self.pyramid:
55 return request.route_path(self.view_name)
57 if hasattr(request, 'route_path'):
58 return request.route_path(self.view_name)
59 else:
60 # TODO: johbo: Remove this after migrating to pyramid.
61 # We need the pyramid request here to generate URLs to pyramid
62 # views from within pylons views.
63 from pyramid.threadlocal import get_current_request
64 pyramid_request = get_current_request()
65 return pyramid_request.route_path(self.view_name)
66 else:
67 from pylons import url
68 return url(self.view_name)
69
56
70 def get_localized_name(self, request):
57 def get_localized_name(self, request):
71 if hasattr(request, 'translate'):
58 return request.translate(self.name)
72 return request.translate(self.name)
73 else:
74 # TODO(marcink): Remove this after migrating to pyramid
75 from pyramid.threadlocal import get_current_request
76 pyramid_request = get_current_request()
77 return pyramid_request.translate(self.name)
78
59
79
60
80 @implementer(IAdminNavigationRegistry)
61 @implementer(IAdminNavigationRegistry)
81 class NavigationRegistry(object):
62 class NavigationRegistry(object):
82
63
83 _base_entries = [
64 _base_entries = [
84 NavEntry('global', _('Global'), 'admin_settings_global'),
65 NavEntry('global', _('Global'),
85 NavEntry('vcs', _('VCS'), 'admin_settings_vcs'),
66 'admin_settings_global'),
86 NavEntry('visual', _('Visual'), 'admin_settings_visual'),
67 NavEntry('vcs', _('VCS'),
87 NavEntry('mapping', _('Remap and Rescan'), 'admin_settings_mapping'),
68 'admin_settings_vcs'),
69 NavEntry('visual', _('Visual'),
70 'admin_settings_visual'),
71 NavEntry('mapping', _('Remap and Rescan'),
72 'admin_settings_mapping'),
88 NavEntry('issuetracker', _('Issue Tracker'),
73 NavEntry('issuetracker', _('Issue Tracker'),
89 'admin_settings_issuetracker'),
74 'admin_settings_issuetracker'),
90 NavEntry('email', _('Email'), 'admin_settings_email'),
75 NavEntry('email', _('Email'),
91 NavEntry('hooks', _('Hooks'), 'admin_settings_hooks'),
76 'admin_settings_email'),
92 NavEntry('search', _('Full Text Search'), 'admin_settings_search'),
77 NavEntry('hooks', _('Hooks'),
93
78 'admin_settings_hooks'),
79 NavEntry('search', _('Full Text Search'),
80 'admin_settings_search'),
94 NavEntry('integrations', _('Integrations'),
81 NavEntry('integrations', _('Integrations'),
95 'global_integrations_home', pyramid=True),
82 'global_integrations_home'),
96 NavEntry('system', _('System Info'),
83 NavEntry('system', _('System Info'),
97 'admin_settings_system', pyramid=True),
84 'admin_settings_system'),
98 NavEntry('process_management', _('Processes'),
85 NavEntry('process_management', _('Processes'),
99 'admin_settings_process_management', pyramid=True),
86 'admin_settings_process_management'),
100 NavEntry('sessions', _('User Sessions'),
87 NavEntry('sessions', _('User Sessions'),
101 'admin_settings_sessions', pyramid=True),
88 'admin_settings_sessions'),
102 NavEntry('open_source', _('Open Source Licenses'),
89 NavEntry('open_source', _('Open Source Licenses'),
103 'admin_settings_open_source', pyramid=True),
90 'admin_settings_open_source'),
104
91
105 ]
92 ]
106
93
107 _labs_entry = NavEntry('labs', _('Labs'), 'admin_settings_labs')
94 _labs_entry = NavEntry('labs', _('Labs'),
95 'admin_settings_labs')
108
96
109 def __init__(self, labs_active=False):
97 def __init__(self, labs_active=False):
110 self._registered_entries = collections.OrderedDict()
98 self._registered_entries = collections.OrderedDict()
111 for item in self.__class__._base_entries:
99 for item in self.__class__._base_entries:
112 self._registered_entries[item.key] = item
100 self._registered_entries[item.key] = item
113
101
114 if labs_active:
102 if labs_active:
115 self.add_entry(self._labs_entry)
103 self.add_entry(self._labs_entry)
116
104
117 def add_entry(self, entry):
105 def add_entry(self, entry):
118 self._registered_entries[entry.key] = entry
106 self._registered_entries[entry.key] = entry
119
107
120 def get_navlist(self, request):
108 def get_navlist(self, request):
121 navlist = [NavListEntry(i.key, i.get_localized_name(request),
109 navlist = [NavListEntry(i.key, i.get_localized_name(request),
122 i.generate_url(request))
110 i.generate_url(request))
123 for i in self._registered_entries.values()]
111 for i in self._registered_entries.values()]
124 return navlist
112 return navlist
125
113
126
114
127 def navigation_registry(request, registry=None):
115 def navigation_registry(request, registry=None):
128 """
116 """
129 Helper that returns the admin navigation registry.
117 Helper that returns the admin navigation registry.
130 """
118 """
131 pyramid_registry = registry or get_registry(request)
119 pyramid_registry = registry or get_registry(request)
132 nav_registry = pyramid_registry.queryUtility(IAdminNavigationRegistry)
120 nav_registry = pyramid_registry.queryUtility(IAdminNavigationRegistry)
133 return nav_registry
121 return nav_registry
134
122
135
123
136 def navigation_list(request):
124 def navigation_list(request):
137 """
125 """
138 Helper that returns the admin navigation as list of NavListEntry objects.
126 Helper that returns the admin navigation as list of NavListEntry objects.
139 """
127 """
140 return navigation_registry(request).get_navlist(request)
128 return navigation_registry(request).get_navlist(request)
141
129
142
130
143 def includeme(config):
131 def includeme(config):
144 # Create admin navigation registry and add it to the pyramid registry.
132 # Create admin navigation registry and add it to the pyramid registry.
145 settings = config.get_settings()
133 settings = config.get_settings()
146 labs_active = str2bool(settings.get('labs_settings_active', False))
134 labs_active = str2bool(settings.get('labs_settings_active', False))
147 navigation_registry = NavigationRegistry(labs_active=labs_active)
135 navigation_registry = NavigationRegistry(labs_active=labs_active)
148 config.registry.registerUtility(navigation_registry) No newline at end of file
136 config.registry.registerUtility(navigation_registry)
@@ -1,670 +1,730 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import mock
21 import mock
22 import pytest
22 import pytest
23
23
24 import rhodecode
24 import rhodecode
25 from rhodecode.apps._base import ADMIN_PREFIX
25 from rhodecode.apps._base import ADMIN_PREFIX
26 from rhodecode.lib.utils2 import md5
26 from rhodecode.lib.utils2 import md5
27 from rhodecode.model.db import RhodeCodeUi
27 from rhodecode.model.db import RhodeCodeUi
28 from rhodecode.model.meta import Session
28 from rhodecode.model.meta import Session
29 from rhodecode.model.settings import SettingsModel, IssueTrackerSettingsModel
29 from rhodecode.model.settings import SettingsModel, IssueTrackerSettingsModel
30 from rhodecode.tests import url, assert_session_flash
30 from rhodecode.tests import assert_session_flash
31 from rhodecode.tests.utils import AssertResponse
31 from rhodecode.tests.utils import AssertResponse
32
32
33
33
34 UPDATE_DATA_QUALNAME = (
34 UPDATE_DATA_QUALNAME = (
35 'rhodecode.apps.admin.views.system_info.AdminSystemInfoSettingsView.get_update_data')
35 'rhodecode.apps.admin.views.system_info.AdminSystemInfoSettingsView.get_update_data')
36
36
37
37
38 def route_path(name, params=None, **kwargs):
39 import urllib
40 from rhodecode.apps._base import ADMIN_PREFIX
41
42 base_url = {
43
44 'admin_settings':
45 ADMIN_PREFIX +'/settings',
46 'admin_settings_update':
47 ADMIN_PREFIX + '/settings/update',
48 'admin_settings_global':
49 ADMIN_PREFIX + '/settings/global',
50 'admin_settings_global_update':
51 ADMIN_PREFIX + '/settings/global/update',
52 'admin_settings_vcs':
53 ADMIN_PREFIX + '/settings/vcs',
54 'admin_settings_vcs_update':
55 ADMIN_PREFIX + '/settings/vcs/update',
56 'admin_settings_vcs_svn_pattern_delete':
57 ADMIN_PREFIX + '/settings/vcs/svn_pattern_delete',
58 'admin_settings_mapping':
59 ADMIN_PREFIX + '/settings/mapping',
60 'admin_settings_mapping_update':
61 ADMIN_PREFIX + '/settings/mapping/update',
62 'admin_settings_visual':
63 ADMIN_PREFIX + '/settings/visual',
64 'admin_settings_visual_update':
65 ADMIN_PREFIX + '/settings/visual/update',
66 'admin_settings_issuetracker':
67 ADMIN_PREFIX + '/settings/issue-tracker',
68 'admin_settings_issuetracker_update':
69 ADMIN_PREFIX + '/settings/issue-tracker/update',
70 'admin_settings_issuetracker_test':
71 ADMIN_PREFIX + '/settings/issue-tracker/test',
72 'admin_settings_issuetracker_delete':
73 ADMIN_PREFIX + '/settings/issue-tracker/delete',
74 'admin_settings_email':
75 ADMIN_PREFIX + '/settings/email',
76 'admin_settings_email_update':
77 ADMIN_PREFIX + '/settings/email/update',
78 'admin_settings_hooks':
79 ADMIN_PREFIX + '/settings/hooks',
80 'admin_settings_hooks_update':
81 ADMIN_PREFIX + '/settings/hooks/update',
82 'admin_settings_hooks_delete':
83 ADMIN_PREFIX + '/settings/hooks/delete',
84 'admin_settings_search':
85 ADMIN_PREFIX + '/settings/search',
86 'admin_settings_labs':
87 ADMIN_PREFIX + '/settings/labs',
88 'admin_settings_labs_update':
89 ADMIN_PREFIX + '/settings/labs/update',
90
91 'admin_settings_sessions':
92 ADMIN_PREFIX + '/settings/sessions',
93 'admin_settings_sessions_cleanup':
94 ADMIN_PREFIX + '/settings/sessions/cleanup',
95 'admin_settings_system':
96 ADMIN_PREFIX + '/settings/system',
97 'admin_settings_system_update':
98 ADMIN_PREFIX + '/settings/system/updates',
99 'admin_settings_open_source':
100 ADMIN_PREFIX + '/settings/open_source',
101
102
103 }[name].format(**kwargs)
104
105 if params:
106 base_url = '{}?{}'.format(base_url, urllib.urlencode(params))
107 return base_url
108
109
38 @pytest.mark.usefixtures('autologin_user', 'app')
110 @pytest.mark.usefixtures('autologin_user', 'app')
39 class TestAdminSettingsController(object):
111 class TestAdminSettingsController(object):
40
112
41 @pytest.mark.parametrize('urlname', [
113 @pytest.mark.parametrize('urlname', [
42 'admin_settings_vcs',
114 'admin_settings_vcs',
43 'admin_settings_mapping',
115 'admin_settings_mapping',
44 'admin_settings_global',
116 'admin_settings_global',
45 'admin_settings_visual',
117 'admin_settings_visual',
46 'admin_settings_email',
118 'admin_settings_email',
47 'admin_settings_hooks',
119 'admin_settings_hooks',
48 'admin_settings_search',
120 'admin_settings_search',
49 ])
121 ])
50 def test_simple_get(self, urlname, app):
122 def test_simple_get(self, urlname):
51 app.get(url(urlname))
123 self.app.get(route_path(urlname))
52
124
53 def test_create_custom_hook(self, csrf_token):
125 def test_create_custom_hook(self, csrf_token):
54 response = self.app.post(
126 response = self.app.post(
55 url('admin_settings_hooks'),
127 route_path('admin_settings_hooks_update'),
56 params={
128 params={
57 'new_hook_ui_key': 'test_hooks_1',
129 'new_hook_ui_key': 'test_hooks_1',
58 'new_hook_ui_value': 'cd /tmp',
130 'new_hook_ui_value': 'cd /tmp',
59 'csrf_token': csrf_token})
131 'csrf_token': csrf_token})
60
132
61 response = response.follow()
133 response = response.follow()
62 response.mustcontain('test_hooks_1')
134 response.mustcontain('test_hooks_1')
63 response.mustcontain('cd /tmp')
135 response.mustcontain('cd /tmp')
64
136
65 def test_create_custom_hook_delete(self, csrf_token):
137 def test_create_custom_hook_delete(self, csrf_token):
66 response = self.app.post(
138 response = self.app.post(
67 url('admin_settings_hooks'),
139 route_path('admin_settings_hooks_update'),
68 params={
140 params={
69 'new_hook_ui_key': 'test_hooks_2',
141 'new_hook_ui_key': 'test_hooks_2',
70 'new_hook_ui_value': 'cd /tmp2',
142 'new_hook_ui_value': 'cd /tmp2',
71 'csrf_token': csrf_token})
143 'csrf_token': csrf_token})
72
144
73 response = response.follow()
145 response = response.follow()
74 response.mustcontain('test_hooks_2')
146 response.mustcontain('test_hooks_2')
75 response.mustcontain('cd /tmp2')
147 response.mustcontain('cd /tmp2')
76
148
77 hook_id = SettingsModel().get_ui_by_key('test_hooks_2').ui_id
149 hook_id = SettingsModel().get_ui_by_key('test_hooks_2').ui_id
78
150
79 # delete
151 # delete
80 self.app.post(
152 self.app.post(
81 url('admin_settings_hooks'),
153 route_path('admin_settings_hooks_delete'),
82 params={'hook_id': hook_id, 'csrf_token': csrf_token})
154 params={'hook_id': hook_id, 'csrf_token': csrf_token})
83 response = self.app.get(url('admin_settings_hooks'))
155 response = self.app.get(route_path('admin_settings_hooks'))
84 response.mustcontain(no=['test_hooks_2'])
156 response.mustcontain(no=['test_hooks_2'])
85 response.mustcontain(no=['cd /tmp2'])
157 response.mustcontain(no=['cd /tmp2'])
86
158
87
159
88 @pytest.mark.usefixtures('autologin_user', 'app')
160 @pytest.mark.usefixtures('autologin_user', 'app')
89 class TestAdminSettingsGlobal(object):
161 class TestAdminSettingsGlobal(object):
90
162
91 def test_pre_post_code_code_active(self, csrf_token):
163 def test_pre_post_code_code_active(self, csrf_token):
92 pre_code = 'rc-pre-code-187652122'
164 pre_code = 'rc-pre-code-187652122'
93 post_code = 'rc-postcode-98165231'
165 post_code = 'rc-postcode-98165231'
94
166
95 response = self.post_and_verify_settings({
167 response = self.post_and_verify_settings({
96 'rhodecode_pre_code': pre_code,
168 'rhodecode_pre_code': pre_code,
97 'rhodecode_post_code': post_code,
169 'rhodecode_post_code': post_code,
98 'csrf_token': csrf_token,
170 'csrf_token': csrf_token,
99 })
171 })
100
172
101 response = response.follow()
173 response = response.follow()
102 response.mustcontain(pre_code, post_code)
174 response.mustcontain(pre_code, post_code)
103
175
104 def test_pre_post_code_code_inactive(self, csrf_token):
176 def test_pre_post_code_code_inactive(self, csrf_token):
105 pre_code = 'rc-pre-code-187652122'
177 pre_code = 'rc-pre-code-187652122'
106 post_code = 'rc-postcode-98165231'
178 post_code = 'rc-postcode-98165231'
107 response = self.post_and_verify_settings({
179 response = self.post_and_verify_settings({
108 'rhodecode_pre_code': '',
180 'rhodecode_pre_code': '',
109 'rhodecode_post_code': '',
181 'rhodecode_post_code': '',
110 'csrf_token': csrf_token,
182 'csrf_token': csrf_token,
111 })
183 })
112
184
113 response = response.follow()
185 response = response.follow()
114 response.mustcontain(no=[pre_code, post_code])
186 response.mustcontain(no=[pre_code, post_code])
115
187
116 def test_captcha_activate(self, csrf_token):
188 def test_captcha_activate(self, csrf_token):
117 self.post_and_verify_settings({
189 self.post_and_verify_settings({
118 'rhodecode_captcha_private_key': '1234567890',
190 'rhodecode_captcha_private_key': '1234567890',
119 'rhodecode_captcha_public_key': '1234567890',
191 'rhodecode_captcha_public_key': '1234567890',
120 'csrf_token': csrf_token,
192 'csrf_token': csrf_token,
121 })
193 })
122
194
123 response = self.app.get(ADMIN_PREFIX + '/register')
195 response = self.app.get(ADMIN_PREFIX + '/register')
124 response.mustcontain('captcha')
196 response.mustcontain('captcha')
125
197
126 def test_captcha_deactivate(self, csrf_token):
198 def test_captcha_deactivate(self, csrf_token):
127 self.post_and_verify_settings({
199 self.post_and_verify_settings({
128 'rhodecode_captcha_private_key': '',
200 'rhodecode_captcha_private_key': '',
129 'rhodecode_captcha_public_key': '1234567890',
201 'rhodecode_captcha_public_key': '1234567890',
130 'csrf_token': csrf_token,
202 'csrf_token': csrf_token,
131 })
203 })
132
204
133 response = self.app.get(ADMIN_PREFIX + '/register')
205 response = self.app.get(ADMIN_PREFIX + '/register')
134 response.mustcontain(no=['captcha'])
206 response.mustcontain(no=['captcha'])
135
207
136 def test_title_change(self, csrf_token):
208 def test_title_change(self, csrf_token):
137 old_title = 'RhodeCode'
209 old_title = 'RhodeCode'
138 new_title = old_title + '_changed'
139
210
140 for new_title in ['Changed', 'Żółwik', old_title]:
211 for new_title in ['Changed', 'Żółwik', old_title]:
141 response = self.post_and_verify_settings({
212 response = self.post_and_verify_settings({
142 'rhodecode_title': new_title,
213 'rhodecode_title': new_title,
143 'csrf_token': csrf_token,
214 'csrf_token': csrf_token,
144 })
215 })
145
216
146 response = response.follow()
217 response = response.follow()
147 response.mustcontain(
218 response.mustcontain(
148 """<div class="branding">- %s</div>""" % new_title)
219 """<div class="branding">- %s</div>""" % new_title)
149
220
150 def post_and_verify_settings(self, settings):
221 def post_and_verify_settings(self, settings):
151 old_title = 'RhodeCode'
222 old_title = 'RhodeCode'
152 old_realm = 'RhodeCode authentication'
223 old_realm = 'RhodeCode authentication'
153 params = {
224 params = {
154 'rhodecode_title': old_title,
225 'rhodecode_title': old_title,
155 'rhodecode_realm': old_realm,
226 'rhodecode_realm': old_realm,
156 'rhodecode_pre_code': '',
227 'rhodecode_pre_code': '',
157 'rhodecode_post_code': '',
228 'rhodecode_post_code': '',
158 'rhodecode_captcha_private_key': '',
229 'rhodecode_captcha_private_key': '',
159 'rhodecode_captcha_public_key': '',
230 'rhodecode_captcha_public_key': '',
160 'rhodecode_create_personal_repo_group': False,
231 'rhodecode_create_personal_repo_group': False,
161 'rhodecode_personal_repo_group_pattern': '${username}',
232 'rhodecode_personal_repo_group_pattern': '${username}',
162 }
233 }
163 params.update(settings)
234 params.update(settings)
164 response = self.app.post(url('admin_settings_global'), params=params)
235 response = self.app.post(
236 route_path('admin_settings_global_update'), params=params)
165
237
166 assert_session_flash(response, 'Updated application settings')
238 assert_session_flash(response, 'Updated application settings')
167 app_settings = SettingsModel().get_all_settings()
239 app_settings = SettingsModel().get_all_settings()
168 del settings['csrf_token']
240 del settings['csrf_token']
169 for key, value in settings.iteritems():
241 for key, value in settings.iteritems():
170 assert app_settings[key] == value.decode('utf-8')
242 assert app_settings[key] == value.decode('utf-8')
171
243
172 return response
244 return response
173
245
174
246
175 @pytest.mark.usefixtures('autologin_user', 'app')
247 @pytest.mark.usefixtures('autologin_user', 'app')
176 class TestAdminSettingsVcs(object):
248 class TestAdminSettingsVcs(object):
177
249
178 def test_contains_svn_default_patterns(self, app):
250 def test_contains_svn_default_patterns(self):
179 response = app.get(url('admin_settings_vcs'))
251 response = self.app.get(route_path('admin_settings_vcs'))
180 expected_patterns = [
252 expected_patterns = [
181 '/trunk',
253 '/trunk',
182 '/branches/*',
254 '/branches/*',
183 '/tags/*',
255 '/tags/*',
184 ]
256 ]
185 for pattern in expected_patterns:
257 for pattern in expected_patterns:
186 response.mustcontain(pattern)
258 response.mustcontain(pattern)
187
259
188 def test_add_new_svn_branch_and_tag_pattern(
260 def test_add_new_svn_branch_and_tag_pattern(
189 self, app, backend_svn, form_defaults, disable_sql_cache,
261 self, backend_svn, form_defaults, disable_sql_cache,
190 csrf_token):
262 csrf_token):
191 form_defaults.update({
263 form_defaults.update({
192 'new_svn_branch': '/exp/branches/*',
264 'new_svn_branch': '/exp/branches/*',
193 'new_svn_tag': '/important_tags/*',
265 'new_svn_tag': '/important_tags/*',
194 'csrf_token': csrf_token,
266 'csrf_token': csrf_token,
195 })
267 })
196
268
197 response = app.post(
269 response = self.app.post(
198 url('admin_settings_vcs'), params=form_defaults, status=302)
270 route_path('admin_settings_vcs_update'),
271 params=form_defaults, status=302)
199 response = response.follow()
272 response = response.follow()
200
273
201 # Expect to find the new values on the page
274 # Expect to find the new values on the page
202 response.mustcontain('/exp/branches/*')
275 response.mustcontain('/exp/branches/*')
203 response.mustcontain('/important_tags/*')
276 response.mustcontain('/important_tags/*')
204
277
205 # Expect that those patterns are used to match branches and tags now
278 # Expect that those patterns are used to match branches and tags now
206 repo = backend_svn['svn-simple-layout'].scm_instance()
279 repo = backend_svn['svn-simple-layout'].scm_instance()
207 assert 'exp/branches/exp-sphinx-docs' in repo.branches
280 assert 'exp/branches/exp-sphinx-docs' in repo.branches
208 assert 'important_tags/v0.5' in repo.tags
281 assert 'important_tags/v0.5' in repo.tags
209
282
210 def test_add_same_svn_value_twice_shows_an_error_message(
283 def test_add_same_svn_value_twice_shows_an_error_message(
211 self, app, form_defaults, csrf_token, settings_util):
284 self, form_defaults, csrf_token, settings_util):
212 settings_util.create_rhodecode_ui('vcs_svn_branch', '/test')
285 settings_util.create_rhodecode_ui('vcs_svn_branch', '/test')
213 settings_util.create_rhodecode_ui('vcs_svn_tag', '/test')
286 settings_util.create_rhodecode_ui('vcs_svn_tag', '/test')
214
287
215 response = app.post(
288 response = self.app.post(
216 url('admin_settings_vcs'),
289 route_path('admin_settings_vcs_update'),
217 params={
290 params={
218 'paths_root_path': form_defaults['paths_root_path'],
291 'paths_root_path': form_defaults['paths_root_path'],
219 'new_svn_branch': '/test',
292 'new_svn_branch': '/test',
220 'new_svn_tag': '/test',
293 'new_svn_tag': '/test',
221 'csrf_token': csrf_token,
294 'csrf_token': csrf_token,
222 },
295 },
223 status=200)
296 status=200)
224
297
225 response.mustcontain("Pattern already exists")
298 response.mustcontain("Pattern already exists")
226 response.mustcontain("Some form inputs contain invalid data.")
299 response.mustcontain("Some form inputs contain invalid data.")
227
300
228 @pytest.mark.parametrize('section', [
301 @pytest.mark.parametrize('section', [
229 'vcs_svn_branch',
302 'vcs_svn_branch',
230 'vcs_svn_tag',
303 'vcs_svn_tag',
231 ])
304 ])
232 def test_delete_svn_patterns(
305 def test_delete_svn_patterns(
233 self, section, app, csrf_token, settings_util):
306 self, section, csrf_token, settings_util):
234 setting = settings_util.create_rhodecode_ui(
307 setting = settings_util.create_rhodecode_ui(
235 section, '/test_delete', cleanup=False)
308 section, '/test_delete', cleanup=False)
236
309
237 app.post(
310 self.app.post(
238 url('admin_settings_vcs'),
311 route_path('admin_settings_vcs_svn_pattern_delete'),
239 params={
312 params={
240 '_method': 'delete',
241 'delete_svn_pattern': setting.ui_id,
313 'delete_svn_pattern': setting.ui_id,
242 'csrf_token': csrf_token},
314 'csrf_token': csrf_token},
243 headers={'X-REQUESTED-WITH': 'XMLHttpRequest'})
315 headers={'X-REQUESTED-WITH': 'XMLHttpRequest'})
244
316
245 @pytest.mark.parametrize('section', [
317 @pytest.mark.parametrize('section', [
246 'vcs_svn_branch',
318 'vcs_svn_branch',
247 'vcs_svn_tag',
319 'vcs_svn_tag',
248 ])
320 ])
249 def test_delete_svn_patterns_raises_400_when_no_xhr(
321 def test_delete_svn_patterns_raises_404_when_no_xhr(
250 self, section, app, csrf_token, settings_util):
322 self, section, csrf_token, settings_util):
251 setting = settings_util.create_rhodecode_ui(section, '/test_delete')
323 setting = settings_util.create_rhodecode_ui(section, '/test_delete')
252
324
253 app.post(
325 self.app.post(
254 url('admin_settings_vcs'),
326 route_path('admin_settings_vcs_svn_pattern_delete'),
255 params={
327 params={
256 '_method': 'delete',
257 'delete_svn_pattern': setting.ui_id,
328 'delete_svn_pattern': setting.ui_id,
258 'csrf_token': csrf_token},
329 'csrf_token': csrf_token},
259 status=400)
330 status=404)
260
331
261 def test_extensions_hgsubversion(self, app, form_defaults, csrf_token):
332 def test_extensions_hgsubversion(self, form_defaults, csrf_token):
262 form_defaults.update({
333 form_defaults.update({
263 'csrf_token': csrf_token,
334 'csrf_token': csrf_token,
264 'extensions_hgsubversion': 'True',
335 'extensions_hgsubversion': 'True',
265 })
336 })
266 response = app.post(
337 response = self.app.post(
267 url('admin_settings_vcs'),
338 route_path('admin_settings_vcs_update'),
268 params=form_defaults,
339 params=form_defaults,
269 status=302)
340 status=302)
270
341
271 response = response.follow()
342 response = response.follow()
272 extensions_input = (
343 extensions_input = (
273 '<input id="extensions_hgsubversion" '
344 '<input id="extensions_hgsubversion" '
274 'name="extensions_hgsubversion" type="checkbox" '
345 'name="extensions_hgsubversion" type="checkbox" '
275 'value="True" checked="checked" />')
346 'value="True" checked="checked" />')
276 response.mustcontain(extensions_input)
347 response.mustcontain(extensions_input)
277
348
278 def test_extensions_hgevolve(self, app, form_defaults, csrf_token):
349 def test_extensions_hgevolve(self, form_defaults, csrf_token):
279 form_defaults.update({
350 form_defaults.update({
280 'csrf_token': csrf_token,
351 'csrf_token': csrf_token,
281 'extensions_evolve': 'True',
352 'extensions_evolve': 'True',
282 })
353 })
283 response = app.post(
354 response = self.app.post(
284 url('admin_settings_vcs'),
355 route_path('admin_settings_vcs_update'),
285 params=form_defaults,
356 params=form_defaults,
286 status=302)
357 status=302)
287
358
288 response = response.follow()
359 response = response.follow()
289 extensions_input = (
360 extensions_input = (
290 '<input id="extensions_evolve" '
361 '<input id="extensions_evolve" '
291 'name="extensions_evolve" type="checkbox" '
362 'name="extensions_evolve" type="checkbox" '
292 'value="True" checked="checked" />')
363 'value="True" checked="checked" />')
293 response.mustcontain(extensions_input)
364 response.mustcontain(extensions_input)
294
365
295 def test_has_a_section_for_pull_request_settings(self, app):
366 def test_has_a_section_for_pull_request_settings(self):
296 response = app.get(url('admin_settings_vcs'))
367 response = self.app.get(route_path('admin_settings_vcs'))
297 response.mustcontain('Pull Request Settings')
368 response.mustcontain('Pull Request Settings')
298
369
299 def test_has_an_input_for_invalidation_of_inline_comments(
370 def test_has_an_input_for_invalidation_of_inline_comments(self):
300 self, app):
371 response = self.app.get(route_path('admin_settings_vcs'))
301 response = app.get(url('admin_settings_vcs'))
302 assert_response = AssertResponse(response)
372 assert_response = AssertResponse(response)
303 assert_response.one_element_exists(
373 assert_response.one_element_exists(
304 '[name=rhodecode_use_outdated_comments]')
374 '[name=rhodecode_use_outdated_comments]')
305
375
306 @pytest.mark.parametrize('new_value', [True, False])
376 @pytest.mark.parametrize('new_value', [True, False])
307 def test_allows_to_change_invalidation_of_inline_comments(
377 def test_allows_to_change_invalidation_of_inline_comments(
308 self, app, form_defaults, csrf_token, new_value):
378 self, form_defaults, csrf_token, new_value):
309 setting_key = 'use_outdated_comments'
379 setting_key = 'use_outdated_comments'
310 setting = SettingsModel().create_or_update_setting(
380 setting = SettingsModel().create_or_update_setting(
311 setting_key, not new_value, 'bool')
381 setting_key, not new_value, 'bool')
312 Session().add(setting)
382 Session().add(setting)
313 Session().commit()
383 Session().commit()
314
384
315 form_defaults.update({
385 form_defaults.update({
316 'csrf_token': csrf_token,
386 'csrf_token': csrf_token,
317 'rhodecode_use_outdated_comments': str(new_value),
387 'rhodecode_use_outdated_comments': str(new_value),
318 })
388 })
319 response = app.post(
389 response = self.app.post(
320 url('admin_settings_vcs'),
390 route_path('admin_settings_vcs_update'),
321 params=form_defaults,
391 params=form_defaults,
322 status=302)
392 status=302)
323 response = response.follow()
393 response = response.follow()
324 setting = SettingsModel().get_setting_by_name(setting_key)
394 setting = SettingsModel().get_setting_by_name(setting_key)
325 assert setting.app_settings_value is new_value
395 assert setting.app_settings_value is new_value
326
396
327 @pytest.mark.parametrize('new_value', [True, False])
397 @pytest.mark.parametrize('new_value', [True, False])
328 def test_allows_to_change_hg_rebase_merge_strategy(
398 def test_allows_to_change_hg_rebase_merge_strategy(
329 self, app, form_defaults, csrf_token, new_value):
399 self, form_defaults, csrf_token, new_value):
330 setting_key = 'hg_use_rebase_for_merging'
400 setting_key = 'hg_use_rebase_for_merging'
331
401
332 form_defaults.update({
402 form_defaults.update({
333 'csrf_token': csrf_token,
403 'csrf_token': csrf_token,
334 'rhodecode_' + setting_key: str(new_value),
404 'rhodecode_' + setting_key: str(new_value),
335 })
405 })
336
406
337 with mock.patch.dict(
407 with mock.patch.dict(
338 rhodecode.CONFIG, {'labs_settings_active': 'true'}):
408 rhodecode.CONFIG, {'labs_settings_active': 'true'}):
339 app.post(
409 self.app.post(
340 url('admin_settings_vcs'),
410 route_path('admin_settings_vcs_update'),
341 params=form_defaults,
411 params=form_defaults,
342 status=302)
412 status=302)
343
413
344 setting = SettingsModel().get_setting_by_name(setting_key)
414 setting = SettingsModel().get_setting_by_name(setting_key)
345 assert setting.app_settings_value is new_value
415 assert setting.app_settings_value is new_value
346
416
347 @pytest.fixture
417 @pytest.fixture
348 def disable_sql_cache(self, request):
418 def disable_sql_cache(self, request):
349 patcher = mock.patch(
419 patcher = mock.patch(
350 'rhodecode.lib.caching_query.FromCache.process_query')
420 'rhodecode.lib.caching_query.FromCache.process_query')
351 request.addfinalizer(patcher.stop)
421 request.addfinalizer(patcher.stop)
352 patcher.start()
422 patcher.start()
353
423
354 @pytest.fixture
424 @pytest.fixture
355 def form_defaults(self):
425 def form_defaults(self):
356 from rhodecode.controllers.admin.settings import SettingsController
426 from rhodecode.apps.admin.views.settings import AdminSettingsView
357 controller = SettingsController()
427 return AdminSettingsView._form_defaults()
358 return controller._form_defaults()
359
428
360 # TODO: johbo: What we really want is to checkpoint before a test run and
429 # TODO: johbo: What we really want is to checkpoint before a test run and
361 # reset the session afterwards.
430 # reset the session afterwards.
362 @pytest.fixture(scope='class', autouse=True)
431 @pytest.fixture(scope='class', autouse=True)
363 def cleanup_settings(self, request, pylonsapp):
432 def cleanup_settings(self, request, pylonsapp):
364 ui_id = RhodeCodeUi.ui_id
433 ui_id = RhodeCodeUi.ui_id
365 original_ids = list(
434 original_ids = list(
366 r.ui_id for r in RhodeCodeUi.query().values(ui_id))
435 r.ui_id for r in RhodeCodeUi.query().values(ui_id))
367
436
368 @request.addfinalizer
437 @request.addfinalizer
369 def cleanup():
438 def cleanup():
370 RhodeCodeUi.query().filter(
439 RhodeCodeUi.query().filter(
371 ui_id.notin_(original_ids)).delete(False)
440 ui_id.notin_(original_ids)).delete(False)
372
441
373
442
374 @pytest.mark.usefixtures('autologin_user', 'app')
443 @pytest.mark.usefixtures('autologin_user', 'app')
375 class TestLabsSettings(object):
444 class TestLabsSettings(object):
376 def test_get_settings_page_disabled(self):
445 def test_get_settings_page_disabled(self):
377 with mock.patch.dict(rhodecode.CONFIG,
446 with mock.patch.dict(
378 {'labs_settings_active': 'false'}):
447 rhodecode.CONFIG, {'labs_settings_active': 'false'}):
379 response = self.app.get(url('admin_settings_labs'), status=302)
380
448
381 assert response.location.endswith(url('admin_settings'))
449 response = self.app.get(
450 route_path('admin_settings_labs'), status=302)
451
452 assert response.location.endswith(route_path('admin_settings'))
382
453
383 def test_get_settings_page_enabled(self):
454 def test_get_settings_page_enabled(self):
384 from rhodecode.controllers.admin import settings
455 from rhodecode.apps.admin.views import settings
385 lab_settings = [
456 lab_settings = [
386 settings.LabSetting(
457 settings.LabSetting(
387 key='rhodecode_bool',
458 key='rhodecode_bool',
388 type='bool',
459 type='bool',
389 group='bool group',
460 group='bool group',
390 label='bool label',
461 label='bool label',
391 help='bool help'
462 help='bool help'
392 ),
463 ),
393 settings.LabSetting(
464 settings.LabSetting(
394 key='rhodecode_text',
465 key='rhodecode_text',
395 type='unicode',
466 type='unicode',
396 group='text group',
467 group='text group',
397 label='text label',
468 label='text label',
398 help='text help'
469 help='text help'
399 ),
470 ),
400 ]
471 ]
401 with mock.patch.dict(rhodecode.CONFIG,
472 with mock.patch.dict(rhodecode.CONFIG,
402 {'labs_settings_active': 'true'}):
473 {'labs_settings_active': 'true'}):
403 with mock.patch.object(settings, '_LAB_SETTINGS', lab_settings):
474 with mock.patch.object(settings, '_LAB_SETTINGS', lab_settings):
404 response = self.app.get(url('admin_settings_labs'))
475 response = self.app.get(route_path('admin_settings_labs'))
405
476
406 assert '<label>bool group:</label>' in response
477 assert '<label>bool group:</label>' in response
407 assert '<label for="rhodecode_bool">bool label</label>' in response
478 assert '<label for="rhodecode_bool">bool label</label>' in response
408 assert '<p class="help-block">bool help</p>' in response
479 assert '<p class="help-block">bool help</p>' in response
409 assert 'name="rhodecode_bool" type="checkbox"' in response
480 assert 'name="rhodecode_bool" type="checkbox"' in response
410
481
411 assert '<label>text group:</label>' in response
482 assert '<label>text group:</label>' in response
412 assert '<label for="rhodecode_text">text label</label>' in response
483 assert '<label for="rhodecode_text">text label</label>' in response
413 assert '<p class="help-block">text help</p>' in response
484 assert '<p class="help-block">text help</p>' in response
414 assert 'name="rhodecode_text" size="60" type="text"' in response
485 assert 'name="rhodecode_text" size="60" type="text"' in response
415
486
416
487
417 @pytest.mark.usefixtures('app')
488 @pytest.mark.usefixtures('app')
418 class TestOpenSourceLicenses(object):
489 class TestOpenSourceLicenses(object):
419
490
420 def _get_url(self):
421 return ADMIN_PREFIX + '/settings/open_source'
422
423 def test_records_are_displayed(self, autologin_user):
491 def test_records_are_displayed(self, autologin_user):
424 sample_licenses = {
492 sample_licenses = {
425 "python2.7-pytest-2.7.1": {
493 "python2.7-pytest-2.7.1": {
426 "UNKNOWN": None
494 "UNKNOWN": None
427 },
495 },
428 "python2.7-Markdown-2.6.2": {
496 "python2.7-Markdown-2.6.2": {
429 "BSD-3-Clause": "http://spdx.org/licenses/BSD-3-Clause"
497 "BSD-3-Clause": "http://spdx.org/licenses/BSD-3-Clause"
430 }
498 }
431 }
499 }
432 read_licenses_patch = mock.patch(
500 read_licenses_patch = mock.patch(
433 'rhodecode.apps.admin.views.open_source_licenses.read_opensource_licenses',
501 'rhodecode.apps.admin.views.open_source_licenses.read_opensource_licenses',
434 return_value=sample_licenses)
502 return_value=sample_licenses)
435 with read_licenses_patch:
503 with read_licenses_patch:
436 response = self.app.get(self._get_url(), status=200)
504 response = self.app.get(
505 route_path('admin_settings_open_source'), status=200)
437
506
438 assert_response = AssertResponse(response)
507 assert_response = AssertResponse(response)
439 assert_response.element_contains(
508 assert_response.element_contains(
440 '.panel-heading', 'Licenses of Third Party Packages')
509 '.panel-heading', 'Licenses of Third Party Packages')
441 for name in sample_licenses:
510 for name in sample_licenses:
442 response.mustcontain(name)
511 response.mustcontain(name)
443 for license in sample_licenses[name]:
512 for license in sample_licenses[name]:
444 assert_response.element_contains('.panel-body', license)
513 assert_response.element_contains('.panel-body', license)
445
514
446 def test_records_can_be_read(self, autologin_user):
515 def test_records_can_be_read(self, autologin_user):
447 response = self.app.get(self._get_url(), status=200)
516 response = self.app.get(
517 route_path('admin_settings_open_source'), status=200)
448 assert_response = AssertResponse(response)
518 assert_response = AssertResponse(response)
449 assert_response.element_contains(
519 assert_response.element_contains(
450 '.panel-heading', 'Licenses of Third Party Packages')
520 '.panel-heading', 'Licenses of Third Party Packages')
451
521
452 def test_forbidden_when_normal_user(self, autologin_regular_user):
522 def test_forbidden_when_normal_user(self, autologin_regular_user):
453 self.app.get(self._get_url(), status=404)
523 self.app.get(
524 route_path('admin_settings_open_source'), status=404)
454
525
455
526
456 @pytest.mark.usefixtures('app')
527 @pytest.mark.usefixtures('app')
457 class TestUserSessions(object):
528 class TestUserSessions(object):
458
529
459 def _get_url(self, name='admin_settings_sessions'):
460 return {
461 'admin_settings_sessions': ADMIN_PREFIX + '/settings/sessions',
462 'admin_settings_sessions_cleanup': ADMIN_PREFIX + '/settings/sessions/cleanup'
463 }[name]
464
465 def test_forbidden_when_normal_user(self, autologin_regular_user):
530 def test_forbidden_when_normal_user(self, autologin_regular_user):
466 self.app.get(self._get_url(), status=404)
531 self.app.get(route_path('admin_settings_sessions'), status=404)
467
532
468 def test_show_sessions_page(self, autologin_user):
533 def test_show_sessions_page(self, autologin_user):
469 response = self.app.get(self._get_url(), status=200)
534 response = self.app.get(route_path('admin_settings_sessions'), status=200)
470 response.mustcontain('file')
535 response.mustcontain('file')
471
536
472 def test_cleanup_old_sessions(self, autologin_user, csrf_token):
537 def test_cleanup_old_sessions(self, autologin_user, csrf_token):
473
538
474 post_data = {
539 post_data = {
475 'csrf_token': csrf_token,
540 'csrf_token': csrf_token,
476 'expire_days': '60'
541 'expire_days': '60'
477 }
542 }
478 response = self.app.post(
543 response = self.app.post(
479 self._get_url('admin_settings_sessions_cleanup'), params=post_data,
544 route_path('admin_settings_sessions_cleanup'), params=post_data,
480 status=302)
545 status=302)
481 assert_session_flash(response, 'Cleaned up old sessions')
546 assert_session_flash(response, 'Cleaned up old sessions')
482
547
483
548
484 @pytest.mark.usefixtures('app')
549 @pytest.mark.usefixtures('app')
485 class TestAdminSystemInfo(object):
550 class TestAdminSystemInfo(object):
486 def _get_url(self, name='admin_settings_system'):
487 return {
488 'admin_settings_system': ADMIN_PREFIX + '/settings/system',
489 'admin_settings_system_update': ADMIN_PREFIX + '/settings/system/updates',
490 }[name]
491
551
492 def test_forbidden_when_normal_user(self, autologin_regular_user):
552 def test_forbidden_when_normal_user(self, autologin_regular_user):
493 self.app.get(self._get_url(), status=404)
553 self.app.get(route_path('admin_settings_system'), status=404)
494
554
495 def test_system_info_page(self, autologin_user):
555 def test_system_info_page(self, autologin_user):
496 response = self.app.get(self._get_url())
556 response = self.app.get(route_path('admin_settings_system'))
497 response.mustcontain('RhodeCode Community Edition, version {}'.format(
557 response.mustcontain('RhodeCode Community Edition, version {}'.format(
498 rhodecode.__version__))
558 rhodecode.__version__))
499
559
500 def test_system_update_new_version(self, autologin_user):
560 def test_system_update_new_version(self, autologin_user):
501 update_data = {
561 update_data = {
502 'versions': [
562 'versions': [
503 {
563 {
504 'version': '100.3.1415926535',
564 'version': '100.3.1415926535',
505 'general': 'The latest version we are ever going to ship'
565 'general': 'The latest version we are ever going to ship'
506 },
566 },
507 {
567 {
508 'version': '0.0.0',
568 'version': '0.0.0',
509 'general': 'The first version we ever shipped'
569 'general': 'The first version we ever shipped'
510 }
570 }
511 ]
571 ]
512 }
572 }
513 with mock.patch(UPDATE_DATA_QUALNAME, return_value=update_data):
573 with mock.patch(UPDATE_DATA_QUALNAME, return_value=update_data):
514 response = self.app.get(self._get_url('admin_settings_system_update'))
574 response = self.app.get(route_path('admin_settings_system_update'))
515 response.mustcontain('A <b>new version</b> is available')
575 response.mustcontain('A <b>new version</b> is available')
516
576
517 def test_system_update_nothing_new(self, autologin_user):
577 def test_system_update_nothing_new(self, autologin_user):
518 update_data = {
578 update_data = {
519 'versions': [
579 'versions': [
520 {
580 {
521 'version': '0.0.0',
581 'version': '0.0.0',
522 'general': 'The first version we ever shipped'
582 'general': 'The first version we ever shipped'
523 }
583 }
524 ]
584 ]
525 }
585 }
526 with mock.patch(UPDATE_DATA_QUALNAME, return_value=update_data):
586 with mock.patch(UPDATE_DATA_QUALNAME, return_value=update_data):
527 response = self.app.get(self._get_url('admin_settings_system_update'))
587 response = self.app.get(route_path('admin_settings_system_update'))
528 response.mustcontain(
588 response.mustcontain(
529 'You already have the <b>latest</b> stable version.')
589 'You already have the <b>latest</b> stable version.')
530
590
531 def test_system_update_bad_response(self, autologin_user):
591 def test_system_update_bad_response(self, autologin_user):
532 with mock.patch(UPDATE_DATA_QUALNAME, side_effect=ValueError('foo')):
592 with mock.patch(UPDATE_DATA_QUALNAME, side_effect=ValueError('foo')):
533 response = self.app.get(self._get_url('admin_settings_system_update'))
593 response = self.app.get(route_path('admin_settings_system_update'))
534 response.mustcontain(
594 response.mustcontain(
535 'Bad data sent from update server')
595 'Bad data sent from update server')
536
596
537
597
538 @pytest.mark.usefixtures("app")
598 @pytest.mark.usefixtures("app")
539 class TestAdminSettingsIssueTracker(object):
599 class TestAdminSettingsIssueTracker(object):
540 RC_PREFIX = 'rhodecode_'
600 RC_PREFIX = 'rhodecode_'
541 SHORT_PATTERN_KEY = 'issuetracker_pat_'
601 SHORT_PATTERN_KEY = 'issuetracker_pat_'
542 PATTERN_KEY = RC_PREFIX + SHORT_PATTERN_KEY
602 PATTERN_KEY = RC_PREFIX + SHORT_PATTERN_KEY
543
603
544 def test_issuetracker_index(self, autologin_user):
604 def test_issuetracker_index(self, autologin_user):
545 response = self.app.get(url('admin_settings_issuetracker'))
605 response = self.app.get(route_path('admin_settings_issuetracker'))
546 assert response.status_code == 200
606 assert response.status_code == 200
547
607
548 def test_add_empty_issuetracker_pattern(
608 def test_add_empty_issuetracker_pattern(
549 self, request, autologin_user, csrf_token):
609 self, request, autologin_user, csrf_token):
550 post_url = url('admin_settings_issuetracker_save')
610 post_url = route_path('admin_settings_issuetracker_update')
551 post_data = {
611 post_data = {
552 'csrf_token': csrf_token
612 'csrf_token': csrf_token
553 }
613 }
554 self.app.post(post_url, post_data, status=302)
614 self.app.post(post_url, post_data, status=302)
555
615
556 def test_add_issuetracker_pattern(
616 def test_add_issuetracker_pattern(
557 self, request, autologin_user, csrf_token):
617 self, request, autologin_user, csrf_token):
558 pattern = 'issuetracker_pat'
618 pattern = 'issuetracker_pat'
559 another_pattern = pattern+'1'
619 another_pattern = pattern+'1'
560 post_url = url('admin_settings_issuetracker_save')
620 post_url = route_path('admin_settings_issuetracker_update')
561 post_data = {
621 post_data = {
562 'new_pattern_pattern_0': pattern,
622 'new_pattern_pattern_0': pattern,
563 'new_pattern_url_0': 'url',
623 'new_pattern_url_0': 'url',
564 'new_pattern_prefix_0': 'prefix',
624 'new_pattern_prefix_0': 'prefix',
565 'new_pattern_description_0': 'description',
625 'new_pattern_description_0': 'description',
566 'new_pattern_pattern_1': another_pattern,
626 'new_pattern_pattern_1': another_pattern,
567 'new_pattern_url_1': 'url1',
627 'new_pattern_url_1': 'url1',
568 'new_pattern_prefix_1': 'prefix1',
628 'new_pattern_prefix_1': 'prefix1',
569 'new_pattern_description_1': 'description1',
629 'new_pattern_description_1': 'description1',
570 'csrf_token': csrf_token
630 'csrf_token': csrf_token
571 }
631 }
572 self.app.post(post_url, post_data, status=302)
632 self.app.post(post_url, post_data, status=302)
573 settings = SettingsModel().get_all_settings()
633 settings = SettingsModel().get_all_settings()
574 self.uid = md5(pattern)
634 self.uid = md5(pattern)
575 assert settings[self.PATTERN_KEY+self.uid] == pattern
635 assert settings[self.PATTERN_KEY+self.uid] == pattern
576 self.another_uid = md5(another_pattern)
636 self.another_uid = md5(another_pattern)
577 assert settings[self.PATTERN_KEY+self.another_uid] == another_pattern
637 assert settings[self.PATTERN_KEY+self.another_uid] == another_pattern
578
638
579 @request.addfinalizer
639 @request.addfinalizer
580 def cleanup():
640 def cleanup():
581 defaults = SettingsModel().get_all_settings()
641 defaults = SettingsModel().get_all_settings()
582
642
583 entries = [name for name in defaults if (
643 entries = [name for name in defaults if (
584 (self.uid in name) or (self.another_uid) in name)]
644 (self.uid in name) or (self.another_uid) in name)]
585 start = len(self.RC_PREFIX)
645 start = len(self.RC_PREFIX)
586 for del_key in entries:
646 for del_key in entries:
587 # TODO: anderson: get_by_name needs name without prefix
647 # TODO: anderson: get_by_name needs name without prefix
588 entry = SettingsModel().get_setting_by_name(del_key[start:])
648 entry = SettingsModel().get_setting_by_name(del_key[start:])
589 Session().delete(entry)
649 Session().delete(entry)
590
650
591 Session().commit()
651 Session().commit()
592
652
593 def test_edit_issuetracker_pattern(
653 def test_edit_issuetracker_pattern(
594 self, autologin_user, backend, csrf_token, request):
654 self, autologin_user, backend, csrf_token, request):
595 old_pattern = 'issuetracker_pat'
655 old_pattern = 'issuetracker_pat'
596 old_uid = md5(old_pattern)
656 old_uid = md5(old_pattern)
597 pattern = 'issuetracker_pat_new'
657 pattern = 'issuetracker_pat_new'
598 self.new_uid = md5(pattern)
658 self.new_uid = md5(pattern)
599
659
600 SettingsModel().create_or_update_setting(
660 SettingsModel().create_or_update_setting(
601 self.SHORT_PATTERN_KEY+old_uid, old_pattern, 'unicode')
661 self.SHORT_PATTERN_KEY+old_uid, old_pattern, 'unicode')
602
662
603 post_url = url('admin_settings_issuetracker_save')
663 post_url = route_path('admin_settings_issuetracker_update')
604 post_data = {
664 post_data = {
605 'new_pattern_pattern_0': pattern,
665 'new_pattern_pattern_0': pattern,
606 'new_pattern_url_0': 'url',
666 'new_pattern_url_0': 'url',
607 'new_pattern_prefix_0': 'prefix',
667 'new_pattern_prefix_0': 'prefix',
608 'new_pattern_description_0': 'description',
668 'new_pattern_description_0': 'description',
609 'uid': old_uid,
669 'uid': old_uid,
610 'csrf_token': csrf_token
670 'csrf_token': csrf_token
611 }
671 }
612 self.app.post(post_url, post_data, status=302)
672 self.app.post(post_url, post_data, status=302)
613 settings = SettingsModel().get_all_settings()
673 settings = SettingsModel().get_all_settings()
614 assert settings[self.PATTERN_KEY+self.new_uid] == pattern
674 assert settings[self.PATTERN_KEY+self.new_uid] == pattern
615 assert self.PATTERN_KEY+old_uid not in settings
675 assert self.PATTERN_KEY+old_uid not in settings
616
676
617 @request.addfinalizer
677 @request.addfinalizer
618 def cleanup():
678 def cleanup():
619 IssueTrackerSettingsModel().delete_entries(self.new_uid)
679 IssueTrackerSettingsModel().delete_entries(self.new_uid)
620
680
621 def test_replace_issuetracker_pattern_description(
681 def test_replace_issuetracker_pattern_description(
622 self, autologin_user, csrf_token, request, settings_util):
682 self, autologin_user, csrf_token, request, settings_util):
623 prefix = 'issuetracker'
683 prefix = 'issuetracker'
624 pattern = 'issuetracker_pat'
684 pattern = 'issuetracker_pat'
625 self.uid = md5(pattern)
685 self.uid = md5(pattern)
626 pattern_key = '_'.join([prefix, 'pat', self.uid])
686 pattern_key = '_'.join([prefix, 'pat', self.uid])
627 rc_pattern_key = '_'.join(['rhodecode', pattern_key])
687 rc_pattern_key = '_'.join(['rhodecode', pattern_key])
628 desc_key = '_'.join([prefix, 'desc', self.uid])
688 desc_key = '_'.join([prefix, 'desc', self.uid])
629 rc_desc_key = '_'.join(['rhodecode', desc_key])
689 rc_desc_key = '_'.join(['rhodecode', desc_key])
630 new_description = 'new_description'
690 new_description = 'new_description'
631
691
632 settings_util.create_rhodecode_setting(
692 settings_util.create_rhodecode_setting(
633 pattern_key, pattern, 'unicode', cleanup=False)
693 pattern_key, pattern, 'unicode', cleanup=False)
634 settings_util.create_rhodecode_setting(
694 settings_util.create_rhodecode_setting(
635 desc_key, 'old description', 'unicode', cleanup=False)
695 desc_key, 'old description', 'unicode', cleanup=False)
636
696
637 post_url = url('admin_settings_issuetracker_save')
697 post_url = route_path('admin_settings_issuetracker_update')
638 post_data = {
698 post_data = {
639 'new_pattern_pattern_0': pattern,
699 'new_pattern_pattern_0': pattern,
640 'new_pattern_url_0': 'url',
700 'new_pattern_url_0': 'url',
641 'new_pattern_prefix_0': 'prefix',
701 'new_pattern_prefix_0': 'prefix',
642 'new_pattern_description_0': new_description,
702 'new_pattern_description_0': new_description,
643 'uid': self.uid,
703 'uid': self.uid,
644 'csrf_token': csrf_token
704 'csrf_token': csrf_token
645 }
705 }
646 self.app.post(post_url, post_data, status=302)
706 self.app.post(post_url, post_data, status=302)
647 settings = SettingsModel().get_all_settings()
707 settings = SettingsModel().get_all_settings()
648 assert settings[rc_pattern_key] == pattern
708 assert settings[rc_pattern_key] == pattern
649 assert settings[rc_desc_key] == new_description
709 assert settings[rc_desc_key] == new_description
650
710
651 @request.addfinalizer
711 @request.addfinalizer
652 def cleanup():
712 def cleanup():
653 IssueTrackerSettingsModel().delete_entries(self.uid)
713 IssueTrackerSettingsModel().delete_entries(self.uid)
654
714
655 def test_delete_issuetracker_pattern(
715 def test_delete_issuetracker_pattern(
656 self, autologin_user, backend, csrf_token, settings_util):
716 self, autologin_user, backend, csrf_token, settings_util):
657 pattern = 'issuetracker_pat'
717 pattern = 'issuetracker_pat'
658 uid = md5(pattern)
718 uid = md5(pattern)
659 settings_util.create_rhodecode_setting(
719 settings_util.create_rhodecode_setting(
660 self.SHORT_PATTERN_KEY+uid, pattern, 'unicode', cleanup=False)
720 self.SHORT_PATTERN_KEY+uid, pattern, 'unicode', cleanup=False)
661
721
662 post_url = url('admin_issuetracker_delete')
722 post_url = route_path('admin_settings_issuetracker_delete')
663 post_data = {
723 post_data = {
664 '_method': 'delete',
724 '_method': 'delete',
665 'uid': uid,
725 'uid': uid,
666 'csrf_token': csrf_token
726 'csrf_token': csrf_token
667 }
727 }
668 self.app.post(post_url, post_data, status=302)
728 self.app.post(post_url, post_data, status=302)
669 settings = SettingsModel().get_all_settings()
729 settings = SettingsModel().get_all_settings()
670 assert 'rhodecode_%s%s' % (self.SHORT_PATTERN_KEY, uid) not in settings
730 assert 'rhodecode_%s%s' % (self.SHORT_PATTERN_KEY, uid) not in settings
@@ -1,259 +1,184 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 Routes configuration
22 Routes configuration
23
23
24 The more specific and detailed routes should be defined first so they
24 The more specific and detailed routes should be defined first so they
25 may take precedent over the more generic routes. For more information
25 may take precedent over the more generic routes. For more information
26 refer to the routes manual at http://routes.groovie.org/docs/
26 refer to the routes manual at http://routes.groovie.org/docs/
27
27
28 IMPORTANT: if you change any routing here, make sure to take a look at lib/base.py
28 IMPORTANT: if you change any routing here, make sure to take a look at lib/base.py
29 and _route_name variable which uses some of stored naming here to do redirects.
29 and _route_name variable which uses some of stored naming here to do redirects.
30 """
30 """
31 import os
31 import os
32 import re
32 import re
33 from routes import Mapper
33 from routes import Mapper
34
34
35 # prefix for non repository related links needs to be prefixed with `/`
35 # prefix for non repository related links needs to be prefixed with `/`
36 ADMIN_PREFIX = '/_admin'
36 ADMIN_PREFIX = '/_admin'
37 STATIC_FILE_PREFIX = '/_static'
37 STATIC_FILE_PREFIX = '/_static'
38
38
39 # Default requirements for URL parts
39 # Default requirements for URL parts
40 URL_NAME_REQUIREMENTS = {
40 URL_NAME_REQUIREMENTS = {
41 # group name can have a slash in them, but they must not end with a slash
41 # group name can have a slash in them, but they must not end with a slash
42 'group_name': r'.*?[^/]',
42 'group_name': r'.*?[^/]',
43 'repo_group_name': r'.*?[^/]',
43 'repo_group_name': r'.*?[^/]',
44 # repo names can have a slash in them, but they must not end with a slash
44 # repo names can have a slash in them, but they must not end with a slash
45 'repo_name': r'.*?[^/]',
45 'repo_name': r'.*?[^/]',
46 # file path eats up everything at the end
46 # file path eats up everything at the end
47 'f_path': r'.*',
47 'f_path': r'.*',
48 # reference types
48 # reference types
49 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
49 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
50 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
50 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
51 }
51 }
52
52
53
53
54 class JSRoutesMapper(Mapper):
54 class JSRoutesMapper(Mapper):
55 """
55 """
56 Wrapper for routes.Mapper to make pyroutes compatible url definitions
56 Wrapper for routes.Mapper to make pyroutes compatible url definitions
57 """
57 """
58 _named_route_regex = re.compile(r'^[a-z-_0-9A-Z]+$')
58 _named_route_regex = re.compile(r'^[a-z-_0-9A-Z]+$')
59 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
59 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
60 def __init__(self, *args, **kw):
60 def __init__(self, *args, **kw):
61 super(JSRoutesMapper, self).__init__(*args, **kw)
61 super(JSRoutesMapper, self).__init__(*args, **kw)
62 self._jsroutes = []
62 self._jsroutes = []
63
63
64 def connect(self, *args, **kw):
64 def connect(self, *args, **kw):
65 """
65 """
66 Wrapper for connect to take an extra argument jsroute=True
66 Wrapper for connect to take an extra argument jsroute=True
67
67
68 :param jsroute: boolean, if True will add the route to the pyroutes list
68 :param jsroute: boolean, if True will add the route to the pyroutes list
69 """
69 """
70 if kw.pop('jsroute', False):
70 if kw.pop('jsroute', False):
71 if not self._named_route_regex.match(args[0]):
71 if not self._named_route_regex.match(args[0]):
72 raise Exception('only named routes can be added to pyroutes')
72 raise Exception('only named routes can be added to pyroutes')
73 self._jsroutes.append(args[0])
73 self._jsroutes.append(args[0])
74
74
75 super(JSRoutesMapper, self).connect(*args, **kw)
75 super(JSRoutesMapper, self).connect(*args, **kw)
76
76
77 def _extract_route_information(self, route):
77 def _extract_route_information(self, route):
78 """
78 """
79 Convert a route into tuple(name, path, args), eg:
79 Convert a route into tuple(name, path, args), eg:
80 ('show_user', '/profile/%(username)s', ['username'])
80 ('show_user', '/profile/%(username)s', ['username'])
81 """
81 """
82 routepath = route.routepath
82 routepath = route.routepath
83 def replace(matchobj):
83 def replace(matchobj):
84 if matchobj.group(1):
84 if matchobj.group(1):
85 return "%%(%s)s" % matchobj.group(1).split(':')[0]
85 return "%%(%s)s" % matchobj.group(1).split(':')[0]
86 else:
86 else:
87 return "%%(%s)s" % matchobj.group(2)
87 return "%%(%s)s" % matchobj.group(2)
88
88
89 routepath = self._argument_prog.sub(replace, routepath)
89 routepath = self._argument_prog.sub(replace, routepath)
90 return (
90 return (
91 route.name,
91 route.name,
92 routepath,
92 routepath,
93 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
93 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
94 for arg in self._argument_prog.findall(route.routepath)]
94 for arg in self._argument_prog.findall(route.routepath)]
95 )
95 )
96
96
97 def jsroutes(self):
97 def jsroutes(self):
98 """
98 """
99 Return a list of pyroutes.js compatible routes
99 Return a list of pyroutes.js compatible routes
100 """
100 """
101 for route_name in self._jsroutes:
101 for route_name in self._jsroutes:
102 yield self._extract_route_information(self._routenames[route_name])
102 yield self._extract_route_information(self._routenames[route_name])
103
103
104
104
105 def make_map(config):
105 def make_map(config):
106 """Create, configure and return the routes Mapper"""
106 """Create, configure and return the routes Mapper"""
107 rmap = JSRoutesMapper(
107 rmap = JSRoutesMapper(
108 directory=config['pylons.paths']['controllers'],
108 directory=config['pylons.paths']['controllers'],
109 always_scan=config['debug'])
109 always_scan=config['debug'])
110 rmap.minimization = False
110 rmap.minimization = False
111 rmap.explicit = False
111 rmap.explicit = False
112
112
113 from rhodecode.lib.utils2 import str2bool
113 from rhodecode.lib.utils2 import str2bool
114 from rhodecode.model import repo, repo_group
114 from rhodecode.model import repo, repo_group
115
115
116 def check_repo(environ, match_dict):
116 def check_repo(environ, match_dict):
117 """
117 """
118 check for valid repository for proper 404 handling
118 check for valid repository for proper 404 handling
119
119
120 :param environ:
120 :param environ:
121 :param match_dict:
121 :param match_dict:
122 """
122 """
123 repo_name = match_dict.get('repo_name')
123 repo_name = match_dict.get('repo_name')
124
124
125 if match_dict.get('f_path'):
125 if match_dict.get('f_path'):
126 # fix for multiple initial slashes that causes errors
126 # fix for multiple initial slashes that causes errors
127 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
127 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
128 repo_model = repo.RepoModel()
128 repo_model = repo.RepoModel()
129 by_name_match = repo_model.get_by_repo_name(repo_name)
129 by_name_match = repo_model.get_by_repo_name(repo_name)
130 # if we match quickly from database, short circuit the operation,
130 # if we match quickly from database, short circuit the operation,
131 # and validate repo based on the type.
131 # and validate repo based on the type.
132 if by_name_match:
132 if by_name_match:
133 return True
133 return True
134
134
135 by_id_match = repo_model.get_repo_by_id(repo_name)
135 by_id_match = repo_model.get_repo_by_id(repo_name)
136 if by_id_match:
136 if by_id_match:
137 repo_name = by_id_match.repo_name
137 repo_name = by_id_match.repo_name
138 match_dict['repo_name'] = repo_name
138 match_dict['repo_name'] = repo_name
139 return True
139 return True
140
140
141 return False
141 return False
142
142
143 def check_group(environ, match_dict):
143 def check_group(environ, match_dict):
144 """
144 """
145 check for valid repository group path for proper 404 handling
145 check for valid repository group path for proper 404 handling
146
146
147 :param environ:
147 :param environ:
148 :param match_dict:
148 :param match_dict:
149 """
149 """
150 repo_group_name = match_dict.get('group_name')
150 repo_group_name = match_dict.get('group_name')
151 repo_group_model = repo_group.RepoGroupModel()
151 repo_group_model = repo_group.RepoGroupModel()
152 by_name_match = repo_group_model.get_by_group_name(repo_group_name)
152 by_name_match = repo_group_model.get_by_group_name(repo_group_name)
153 if by_name_match:
153 if by_name_match:
154 return True
154 return True
155
155
156 return False
156 return False
157
157
158 def check_user_group(environ, match_dict):
158 def check_user_group(environ, match_dict):
159 """
159 """
160 check for valid user group for proper 404 handling
160 check for valid user group for proper 404 handling
161
161
162 :param environ:
162 :param environ:
163 :param match_dict:
163 :param match_dict:
164 """
164 """
165 return True
165 return True
166
166
167 def check_int(environ, match_dict):
167 def check_int(environ, match_dict):
168 return match_dict.get('id').isdigit()
168 return match_dict.get('id').isdigit()
169
169
170
170
171 #==========================================================================
171 #==========================================================================
172 # CUSTOM ROUTES HERE
172 # CUSTOM ROUTES HERE
173 #==========================================================================
173 #==========================================================================
174
174
175 # ADMIN SETTINGS ROUTES
176 with rmap.submapper(path_prefix=ADMIN_PREFIX,
177 controller='admin/settings') as m:
178
179 # default
180 m.connect('admin_settings', '/settings',
181 action='settings_global_update',
182 conditions={'method': ['POST']})
183 m.connect('admin_settings', '/settings',
184 action='settings_global', conditions={'method': ['GET']})
185
186 m.connect('admin_settings_vcs', '/settings/vcs',
187 action='settings_vcs_update',
188 conditions={'method': ['POST']})
189 m.connect('admin_settings_vcs', '/settings/vcs',
190 action='settings_vcs',
191 conditions={'method': ['GET']})
192 m.connect('admin_settings_vcs', '/settings/vcs',
193 action='delete_svn_pattern',
194 conditions={'method': ['DELETE']})
195
196 m.connect('admin_settings_mapping', '/settings/mapping',
197 action='settings_mapping_update',
198 conditions={'method': ['POST']})
199 m.connect('admin_settings_mapping', '/settings/mapping',
200 action='settings_mapping', conditions={'method': ['GET']})
201
202 m.connect('admin_settings_global', '/settings/global',
203 action='settings_global_update',
204 conditions={'method': ['POST']})
205 m.connect('admin_settings_global', '/settings/global',
206 action='settings_global', conditions={'method': ['GET']})
207
208 m.connect('admin_settings_visual', '/settings/visual',
209 action='settings_visual_update',
210 conditions={'method': ['POST']})
211 m.connect('admin_settings_visual', '/settings/visual',
212 action='settings_visual', conditions={'method': ['GET']})
213
214 m.connect('admin_settings_issuetracker',
215 '/settings/issue-tracker', action='settings_issuetracker',
216 conditions={'method': ['GET']})
217 m.connect('admin_settings_issuetracker_save',
218 '/settings/issue-tracker/save',
219 action='settings_issuetracker_save',
220 conditions={'method': ['POST']})
221 m.connect('admin_issuetracker_test', '/settings/issue-tracker/test',
222 action='settings_issuetracker_test',
223 conditions={'method': ['POST']})
224 m.connect('admin_issuetracker_delete',
225 '/settings/issue-tracker/delete',
226 action='settings_issuetracker_delete',
227 conditions={'method': ['DELETE']})
228
229 m.connect('admin_settings_email', '/settings/email',
230 action='settings_email_update',
231 conditions={'method': ['POST']})
232 m.connect('admin_settings_email', '/settings/email',
233 action='settings_email', conditions={'method': ['GET']})
234
235 m.connect('admin_settings_hooks', '/settings/hooks',
236 action='settings_hooks_update',
237 conditions={'method': ['POST', 'DELETE']})
238 m.connect('admin_settings_hooks', '/settings/hooks',
239 action='settings_hooks', conditions={'method': ['GET']})
240
241 m.connect('admin_settings_search', '/settings/search',
242 action='settings_search', conditions={'method': ['GET']})
243
244 m.connect('admin_settings_labs', '/settings/labs',
245 action='settings_labs_update',
246 conditions={'method': ['POST']})
247 m.connect('admin_settings_labs', '/settings/labs',
248 action='settings_labs', conditions={'method': ['GET']})
249
250 # ADMIN MY ACCOUNT
175 # ADMIN MY ACCOUNT
251 with rmap.submapper(path_prefix=ADMIN_PREFIX,
176 with rmap.submapper(path_prefix=ADMIN_PREFIX,
252 controller='admin/my_account') as m:
177 controller='admin/my_account') as m:
253
178
254 # NOTE(marcink): this needs to be kept for password force flag to be
179 # NOTE(marcink): this needs to be kept for password force flag to be
255 # handled in pylons controllers, remove after full migration to pyramid
180 # handled in pylons controllers, remove after full migration to pyramid
256 m.connect('my_account_password', '/my_account/password',
181 m.connect('my_account_password', '/my_account/password',
257 action='my_account_password', conditions={'method': ['GET']})
182 action='my_account_password', conditions={'method': ['GET']})
258
183
259 return rmap
184 return rmap
@@ -1,293 +1,316 b''
1
1
2 /******************************************************************************
2 /******************************************************************************
3 * *
3 * *
4 * DO NOT CHANGE THIS FILE MANUALLY *
4 * DO NOT CHANGE THIS FILE MANUALLY *
5 * *
5 * *
6 * *
6 * *
7 * This file is automatically generated when the app starts up with *
7 * This file is automatically generated when the app starts up with *
8 * generate_js_files = true *
8 * generate_js_files = true *
9 * *
9 * *
10 * To add a route here pass jsroute=True to the route definition in the app *
10 * To add a route here pass jsroute=True to the route definition in the app *
11 * *
11 * *
12 ******************************************************************************/
12 ******************************************************************************/
13 function registerRCRoutes() {
13 function registerRCRoutes() {
14 // routes registration
14 // routes registration
15 pyroutes.register('favicon', '/favicon.ico', []);
15 pyroutes.register('favicon', '/favicon.ico', []);
16 pyroutes.register('robots', '/robots.txt', []);
16 pyroutes.register('robots', '/robots.txt', []);
17 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
17 pyroutes.register('auth_home', '/_admin/auth*traverse', []);
18 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
18 pyroutes.register('global_integrations_new', '/_admin/integrations/new', []);
19 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
19 pyroutes.register('global_integrations_home', '/_admin/integrations', []);
20 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
20 pyroutes.register('global_integrations_list', '/_admin/integrations/%(integration)s', ['integration']);
21 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
21 pyroutes.register('global_integrations_create', '/_admin/integrations/%(integration)s/new', ['integration']);
22 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
22 pyroutes.register('global_integrations_edit', '/_admin/integrations/%(integration)s/%(integration_id)s', ['integration', 'integration_id']);
23 pyroutes.register('repo_group_integrations_home', '/%(repo_group_name)s/_settings/integrations', ['repo_group_name']);
23 pyroutes.register('repo_group_integrations_home', '/%(repo_group_name)s/_settings/integrations', ['repo_group_name']);
24 pyroutes.register('repo_group_integrations_new', '/%(repo_group_name)s/_settings/integrations/new', ['repo_group_name']);
24 pyroutes.register('repo_group_integrations_new', '/%(repo_group_name)s/_settings/integrations/new', ['repo_group_name']);
25 pyroutes.register('repo_group_integrations_list', '/%(repo_group_name)s/_settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
25 pyroutes.register('repo_group_integrations_list', '/%(repo_group_name)s/_settings/integrations/%(integration)s', ['repo_group_name', 'integration']);
26 pyroutes.register('repo_group_integrations_create', '/%(repo_group_name)s/_settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
26 pyroutes.register('repo_group_integrations_create', '/%(repo_group_name)s/_settings/integrations/%(integration)s/new', ['repo_group_name', 'integration']);
27 pyroutes.register('repo_group_integrations_edit', '/%(repo_group_name)s/_settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
27 pyroutes.register('repo_group_integrations_edit', '/%(repo_group_name)s/_settings/integrations/%(integration)s/%(integration_id)s', ['repo_group_name', 'integration', 'integration_id']);
28 pyroutes.register('repo_integrations_home', '/%(repo_name)s/settings/integrations', ['repo_name']);
28 pyroutes.register('repo_integrations_home', '/%(repo_name)s/settings/integrations', ['repo_name']);
29 pyroutes.register('repo_integrations_new', '/%(repo_name)s/settings/integrations/new', ['repo_name']);
29 pyroutes.register('repo_integrations_new', '/%(repo_name)s/settings/integrations/new', ['repo_name']);
30 pyroutes.register('repo_integrations_list', '/%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
30 pyroutes.register('repo_integrations_list', '/%(repo_name)s/settings/integrations/%(integration)s', ['repo_name', 'integration']);
31 pyroutes.register('repo_integrations_create', '/%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
31 pyroutes.register('repo_integrations_create', '/%(repo_name)s/settings/integrations/%(integration)s/new', ['repo_name', 'integration']);
32 pyroutes.register('repo_integrations_edit', '/%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
32 pyroutes.register('repo_integrations_edit', '/%(repo_name)s/settings/integrations/%(integration)s/%(integration_id)s', ['repo_name', 'integration', 'integration_id']);
33 pyroutes.register('ops_ping', '/_admin/ops/ping', []);
33 pyroutes.register('ops_ping', '/_admin/ops/ping', []);
34 pyroutes.register('ops_error_test', '/_admin/ops/error', []);
34 pyroutes.register('ops_error_test', '/_admin/ops/error', []);
35 pyroutes.register('ops_redirect_test', '/_admin/ops/redirect', []);
35 pyroutes.register('ops_redirect_test', '/_admin/ops/redirect', []);
36 pyroutes.register('ops_ping_legacy', '/_admin/ping', []);
36 pyroutes.register('ops_ping_legacy', '/_admin/ping', []);
37 pyroutes.register('ops_error_test_legacy', '/_admin/error_test', []);
37 pyroutes.register('ops_error_test_legacy', '/_admin/error_test', []);
38 pyroutes.register('admin_home', '/_admin', []);
38 pyroutes.register('admin_home', '/_admin', []);
39 pyroutes.register('admin_audit_logs', '/_admin/audit_logs', []);
39 pyroutes.register('admin_audit_logs', '/_admin/audit_logs', []);
40 pyroutes.register('admin_audit_log_entry', '/_admin/audit_logs/%(audit_log_id)s', ['audit_log_id']);
40 pyroutes.register('admin_audit_log_entry', '/_admin/audit_logs/%(audit_log_id)s', ['audit_log_id']);
41 pyroutes.register('pull_requests_global_0', '/_admin/pull_requests/%(pull_request_id)s', ['pull_request_id']);
41 pyroutes.register('pull_requests_global_0', '/_admin/pull_requests/%(pull_request_id)s', ['pull_request_id']);
42 pyroutes.register('pull_requests_global_1', '/_admin/pull-requests/%(pull_request_id)s', ['pull_request_id']);
42 pyroutes.register('pull_requests_global_1', '/_admin/pull-requests/%(pull_request_id)s', ['pull_request_id']);
43 pyroutes.register('pull_requests_global', '/_admin/pull-request/%(pull_request_id)s', ['pull_request_id']);
43 pyroutes.register('pull_requests_global', '/_admin/pull-request/%(pull_request_id)s', ['pull_request_id']);
44 pyroutes.register('admin_settings_open_source', '/_admin/settings/open_source', []);
44 pyroutes.register('admin_settings_open_source', '/_admin/settings/open_source', []);
45 pyroutes.register('admin_settings_vcs_svn_generate_cfg', '/_admin/settings/vcs/svn_generate_cfg', []);
45 pyroutes.register('admin_settings_vcs_svn_generate_cfg', '/_admin/settings/vcs/svn_generate_cfg', []);
46 pyroutes.register('admin_settings_system', '/_admin/settings/system', []);
46 pyroutes.register('admin_settings_system', '/_admin/settings/system', []);
47 pyroutes.register('admin_settings_system_update', '/_admin/settings/system/updates', []);
47 pyroutes.register('admin_settings_system_update', '/_admin/settings/system/updates', []);
48 pyroutes.register('admin_settings_sessions', '/_admin/settings/sessions', []);
48 pyroutes.register('admin_settings_sessions', '/_admin/settings/sessions', []);
49 pyroutes.register('admin_settings_sessions_cleanup', '/_admin/settings/sessions/cleanup', []);
49 pyroutes.register('admin_settings_sessions_cleanup', '/_admin/settings/sessions/cleanup', []);
50 pyroutes.register('admin_settings_process_management', '/_admin/settings/process_management', []);
50 pyroutes.register('admin_settings_process_management', '/_admin/settings/process_management', []);
51 pyroutes.register('admin_settings_process_management_signal', '/_admin/settings/process_management/signal', []);
51 pyroutes.register('admin_settings_process_management_signal', '/_admin/settings/process_management/signal', []);
52 pyroutes.register('admin_defaults_repositories', '/_admin/defaults/repositories', []);
52 pyroutes.register('admin_defaults_repositories', '/_admin/defaults/repositories', []);
53 pyroutes.register('admin_defaults_repositories_update', '/_admin/defaults/repositories/update', []);
53 pyroutes.register('admin_defaults_repositories_update', '/_admin/defaults/repositories/update', []);
54 pyroutes.register('admin_settings', '/_admin/settings', []);
55 pyroutes.register('admin_settings_update', '/_admin/settings/update', []);
56 pyroutes.register('admin_settings_global', '/_admin/settings/global', []);
57 pyroutes.register('admin_settings_global_update', '/_admin/settings/global/update', []);
58 pyroutes.register('admin_settings_vcs', '/_admin/settings/vcs', []);
59 pyroutes.register('admin_settings_vcs_update', '/_admin/settings/vcs/update', []);
60 pyroutes.register('admin_settings_vcs_svn_pattern_delete', '/_admin/settings/vcs/svn_pattern_delete', []);
61 pyroutes.register('admin_settings_mapping', '/_admin/settings/mapping', []);
62 pyroutes.register('admin_settings_mapping_update', '/_admin/settings/mapping/update', []);
63 pyroutes.register('admin_settings_visual', '/_admin/settings/visual', []);
64 pyroutes.register('admin_settings_visual_update', '/_admin/settings/visual/update', []);
65 pyroutes.register('admin_settings_issuetracker', '/_admin/settings/issue-tracker', []);
66 pyroutes.register('admin_settings_issuetracker_update', '/_admin/settings/issue-tracker/update', []);
67 pyroutes.register('admin_settings_issuetracker_test', '/_admin/settings/issue-tracker/test', []);
68 pyroutes.register('admin_settings_issuetracker_delete', '/_admin/settings/issue-tracker/delete', []);
69 pyroutes.register('admin_settings_email', '/_admin/settings/email', []);
70 pyroutes.register('admin_settings_email_update', '/_admin/settings/email/update', []);
71 pyroutes.register('admin_settings_hooks', '/_admin/settings/hooks', []);
72 pyroutes.register('admin_settings_hooks_update', '/_admin/settings/hooks/update', []);
73 pyroutes.register('admin_settings_hooks_delete', '/_admin/settings/hooks/delete', []);
74 pyroutes.register('admin_settings_search', '/_admin/settings/search', []);
75 pyroutes.register('admin_settings_labs', '/_admin/settings/labs', []);
76 pyroutes.register('admin_settings_labs_update', '/_admin/settings/labs/update', []);
54 pyroutes.register('admin_permissions_application', '/_admin/permissions/application', []);
77 pyroutes.register('admin_permissions_application', '/_admin/permissions/application', []);
55 pyroutes.register('admin_permissions_application_update', '/_admin/permissions/application/update', []);
78 pyroutes.register('admin_permissions_application_update', '/_admin/permissions/application/update', []);
56 pyroutes.register('admin_permissions_global', '/_admin/permissions/global', []);
79 pyroutes.register('admin_permissions_global', '/_admin/permissions/global', []);
57 pyroutes.register('admin_permissions_global_update', '/_admin/permissions/global/update', []);
80 pyroutes.register('admin_permissions_global_update', '/_admin/permissions/global/update', []);
58 pyroutes.register('admin_permissions_object', '/_admin/permissions/object', []);
81 pyroutes.register('admin_permissions_object', '/_admin/permissions/object', []);
59 pyroutes.register('admin_permissions_object_update', '/_admin/permissions/object/update', []);
82 pyroutes.register('admin_permissions_object_update', '/_admin/permissions/object/update', []);
60 pyroutes.register('admin_permissions_ips', '/_admin/permissions/ips', []);
83 pyroutes.register('admin_permissions_ips', '/_admin/permissions/ips', []);
61 pyroutes.register('admin_permissions_overview', '/_admin/permissions/overview', []);
84 pyroutes.register('admin_permissions_overview', '/_admin/permissions/overview', []);
62 pyroutes.register('admin_permissions_auth_token_access', '/_admin/permissions/auth_token_access', []);
85 pyroutes.register('admin_permissions_auth_token_access', '/_admin/permissions/auth_token_access', []);
63 pyroutes.register('admin_permissions_ssh_keys', '/_admin/permissions/ssh_keys', []);
86 pyroutes.register('admin_permissions_ssh_keys', '/_admin/permissions/ssh_keys', []);
64 pyroutes.register('admin_permissions_ssh_keys_data', '/_admin/permissions/ssh_keys/data', []);
87 pyroutes.register('admin_permissions_ssh_keys_data', '/_admin/permissions/ssh_keys/data', []);
65 pyroutes.register('admin_permissions_ssh_keys_update', '/_admin/permissions/ssh_keys/update', []);
88 pyroutes.register('admin_permissions_ssh_keys_update', '/_admin/permissions/ssh_keys/update', []);
66 pyroutes.register('users', '/_admin/users', []);
89 pyroutes.register('users', '/_admin/users', []);
67 pyroutes.register('users_data', '/_admin/users_data', []);
90 pyroutes.register('users_data', '/_admin/users_data', []);
68 pyroutes.register('users_create', '/_admin/users/create', []);
91 pyroutes.register('users_create', '/_admin/users/create', []);
69 pyroutes.register('users_new', '/_admin/users/new', []);
92 pyroutes.register('users_new', '/_admin/users/new', []);
70 pyroutes.register('user_edit', '/_admin/users/%(user_id)s/edit', ['user_id']);
93 pyroutes.register('user_edit', '/_admin/users/%(user_id)s/edit', ['user_id']);
71 pyroutes.register('user_edit_advanced', '/_admin/users/%(user_id)s/edit/advanced', ['user_id']);
94 pyroutes.register('user_edit_advanced', '/_admin/users/%(user_id)s/edit/advanced', ['user_id']);
72 pyroutes.register('user_edit_global_perms', '/_admin/users/%(user_id)s/edit/global_permissions', ['user_id']);
95 pyroutes.register('user_edit_global_perms', '/_admin/users/%(user_id)s/edit/global_permissions', ['user_id']);
73 pyroutes.register('user_edit_global_perms_update', '/_admin/users/%(user_id)s/edit/global_permissions/update', ['user_id']);
96 pyroutes.register('user_edit_global_perms_update', '/_admin/users/%(user_id)s/edit/global_permissions/update', ['user_id']);
74 pyroutes.register('user_update', '/_admin/users/%(user_id)s/update', ['user_id']);
97 pyroutes.register('user_update', '/_admin/users/%(user_id)s/update', ['user_id']);
75 pyroutes.register('user_delete', '/_admin/users/%(user_id)s/delete', ['user_id']);
98 pyroutes.register('user_delete', '/_admin/users/%(user_id)s/delete', ['user_id']);
76 pyroutes.register('user_force_password_reset', '/_admin/users/%(user_id)s/password_reset', ['user_id']);
99 pyroutes.register('user_force_password_reset', '/_admin/users/%(user_id)s/password_reset', ['user_id']);
77 pyroutes.register('user_create_personal_repo_group', '/_admin/users/%(user_id)s/create_repo_group', ['user_id']);
100 pyroutes.register('user_create_personal_repo_group', '/_admin/users/%(user_id)s/create_repo_group', ['user_id']);
78 pyroutes.register('edit_user_auth_tokens', '/_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
101 pyroutes.register('edit_user_auth_tokens', '/_admin/users/%(user_id)s/edit/auth_tokens', ['user_id']);
79 pyroutes.register('edit_user_auth_tokens_add', '/_admin/users/%(user_id)s/edit/auth_tokens/new', ['user_id']);
102 pyroutes.register('edit_user_auth_tokens_add', '/_admin/users/%(user_id)s/edit/auth_tokens/new', ['user_id']);
80 pyroutes.register('edit_user_auth_tokens_delete', '/_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
103 pyroutes.register('edit_user_auth_tokens_delete', '/_admin/users/%(user_id)s/edit/auth_tokens/delete', ['user_id']);
81 pyroutes.register('edit_user_ssh_keys', '/_admin/users/%(user_id)s/edit/ssh_keys', ['user_id']);
104 pyroutes.register('edit_user_ssh_keys', '/_admin/users/%(user_id)s/edit/ssh_keys', ['user_id']);
82 pyroutes.register('edit_user_ssh_keys_generate_keypair', '/_admin/users/%(user_id)s/edit/ssh_keys/generate', ['user_id']);
105 pyroutes.register('edit_user_ssh_keys_generate_keypair', '/_admin/users/%(user_id)s/edit/ssh_keys/generate', ['user_id']);
83 pyroutes.register('edit_user_ssh_keys_add', '/_admin/users/%(user_id)s/edit/ssh_keys/new', ['user_id']);
106 pyroutes.register('edit_user_ssh_keys_add', '/_admin/users/%(user_id)s/edit/ssh_keys/new', ['user_id']);
84 pyroutes.register('edit_user_ssh_keys_delete', '/_admin/users/%(user_id)s/edit/ssh_keys/delete', ['user_id']);
107 pyroutes.register('edit_user_ssh_keys_delete', '/_admin/users/%(user_id)s/edit/ssh_keys/delete', ['user_id']);
85 pyroutes.register('edit_user_emails', '/_admin/users/%(user_id)s/edit/emails', ['user_id']);
108 pyroutes.register('edit_user_emails', '/_admin/users/%(user_id)s/edit/emails', ['user_id']);
86 pyroutes.register('edit_user_emails_add', '/_admin/users/%(user_id)s/edit/emails/new', ['user_id']);
109 pyroutes.register('edit_user_emails_add', '/_admin/users/%(user_id)s/edit/emails/new', ['user_id']);
87 pyroutes.register('edit_user_emails_delete', '/_admin/users/%(user_id)s/edit/emails/delete', ['user_id']);
110 pyroutes.register('edit_user_emails_delete', '/_admin/users/%(user_id)s/edit/emails/delete', ['user_id']);
88 pyroutes.register('edit_user_ips', '/_admin/users/%(user_id)s/edit/ips', ['user_id']);
111 pyroutes.register('edit_user_ips', '/_admin/users/%(user_id)s/edit/ips', ['user_id']);
89 pyroutes.register('edit_user_ips_add', '/_admin/users/%(user_id)s/edit/ips/new', ['user_id']);
112 pyroutes.register('edit_user_ips_add', '/_admin/users/%(user_id)s/edit/ips/new', ['user_id']);
90 pyroutes.register('edit_user_ips_delete', '/_admin/users/%(user_id)s/edit/ips/delete', ['user_id']);
113 pyroutes.register('edit_user_ips_delete', '/_admin/users/%(user_id)s/edit/ips/delete', ['user_id']);
91 pyroutes.register('edit_user_perms_summary', '/_admin/users/%(user_id)s/edit/permissions_summary', ['user_id']);
114 pyroutes.register('edit_user_perms_summary', '/_admin/users/%(user_id)s/edit/permissions_summary', ['user_id']);
92 pyroutes.register('edit_user_perms_summary_json', '/_admin/users/%(user_id)s/edit/permissions_summary/json', ['user_id']);
115 pyroutes.register('edit_user_perms_summary_json', '/_admin/users/%(user_id)s/edit/permissions_summary/json', ['user_id']);
93 pyroutes.register('edit_user_groups_management', '/_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
116 pyroutes.register('edit_user_groups_management', '/_admin/users/%(user_id)s/edit/groups_management', ['user_id']);
94 pyroutes.register('edit_user_groups_management_updates', '/_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
117 pyroutes.register('edit_user_groups_management_updates', '/_admin/users/%(user_id)s/edit/edit_user_groups_management/updates', ['user_id']);
95 pyroutes.register('edit_user_audit_logs', '/_admin/users/%(user_id)s/edit/audit', ['user_id']);
118 pyroutes.register('edit_user_audit_logs', '/_admin/users/%(user_id)s/edit/audit', ['user_id']);
96 pyroutes.register('user_groups', '/_admin/user_groups', []);
119 pyroutes.register('user_groups', '/_admin/user_groups', []);
97 pyroutes.register('user_groups_data', '/_admin/user_groups_data', []);
120 pyroutes.register('user_groups_data', '/_admin/user_groups_data', []);
98 pyroutes.register('user_groups_new', '/_admin/user_groups/new', []);
121 pyroutes.register('user_groups_new', '/_admin/user_groups/new', []);
99 pyroutes.register('user_groups_create', '/_admin/user_groups/create', []);
122 pyroutes.register('user_groups_create', '/_admin/user_groups/create', []);
100 pyroutes.register('repos', '/_admin/repos', []);
123 pyroutes.register('repos', '/_admin/repos', []);
101 pyroutes.register('repo_new', '/_admin/repos/new', []);
124 pyroutes.register('repo_new', '/_admin/repos/new', []);
102 pyroutes.register('repo_create', '/_admin/repos/create', []);
125 pyroutes.register('repo_create', '/_admin/repos/create', []);
103 pyroutes.register('repo_groups', '/_admin/repo_groups', []);
126 pyroutes.register('repo_groups', '/_admin/repo_groups', []);
104 pyroutes.register('repo_group_new', '/_admin/repo_group/new', []);
127 pyroutes.register('repo_group_new', '/_admin/repo_group/new', []);
105 pyroutes.register('repo_group_create', '/_admin/repo_group/create', []);
128 pyroutes.register('repo_group_create', '/_admin/repo_group/create', []);
106 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
129 pyroutes.register('channelstream_connect', '/_admin/channelstream/connect', []);
107 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
130 pyroutes.register('channelstream_subscribe', '/_admin/channelstream/subscribe', []);
108 pyroutes.register('channelstream_proxy', '/_channelstream', []);
131 pyroutes.register('channelstream_proxy', '/_channelstream', []);
109 pyroutes.register('login', '/_admin/login', []);
132 pyroutes.register('login', '/_admin/login', []);
110 pyroutes.register('logout', '/_admin/logout', []);
133 pyroutes.register('logout', '/_admin/logout', []);
111 pyroutes.register('register', '/_admin/register', []);
134 pyroutes.register('register', '/_admin/register', []);
112 pyroutes.register('reset_password', '/_admin/password_reset', []);
135 pyroutes.register('reset_password', '/_admin/password_reset', []);
113 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
136 pyroutes.register('reset_password_confirmation', '/_admin/password_reset_confirmation', []);
114 pyroutes.register('home', '/', []);
137 pyroutes.register('home', '/', []);
115 pyroutes.register('user_autocomplete_data', '/_users', []);
138 pyroutes.register('user_autocomplete_data', '/_users', []);
116 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
139 pyroutes.register('user_group_autocomplete_data', '/_user_groups', []);
117 pyroutes.register('repo_list_data', '/_repos', []);
140 pyroutes.register('repo_list_data', '/_repos', []);
118 pyroutes.register('goto_switcher_data', '/_goto_data', []);
141 pyroutes.register('goto_switcher_data', '/_goto_data', []);
119 pyroutes.register('journal', '/_admin/journal', []);
142 pyroutes.register('journal', '/_admin/journal', []);
120 pyroutes.register('journal_rss', '/_admin/journal/rss', []);
143 pyroutes.register('journal_rss', '/_admin/journal/rss', []);
121 pyroutes.register('journal_atom', '/_admin/journal/atom', []);
144 pyroutes.register('journal_atom', '/_admin/journal/atom', []);
122 pyroutes.register('journal_public', '/_admin/public_journal', []);
145 pyroutes.register('journal_public', '/_admin/public_journal', []);
123 pyroutes.register('journal_public_atom', '/_admin/public_journal/atom', []);
146 pyroutes.register('journal_public_atom', '/_admin/public_journal/atom', []);
124 pyroutes.register('journal_public_atom_old', '/_admin/public_journal_atom', []);
147 pyroutes.register('journal_public_atom_old', '/_admin/public_journal_atom', []);
125 pyroutes.register('journal_public_rss', '/_admin/public_journal/rss', []);
148 pyroutes.register('journal_public_rss', '/_admin/public_journal/rss', []);
126 pyroutes.register('journal_public_rss_old', '/_admin/public_journal_rss', []);
149 pyroutes.register('journal_public_rss_old', '/_admin/public_journal_rss', []);
127 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
150 pyroutes.register('toggle_following', '/_admin/toggle_following', []);
128 pyroutes.register('repo_creating', '/%(repo_name)s/repo_creating', ['repo_name']);
151 pyroutes.register('repo_creating', '/%(repo_name)s/repo_creating', ['repo_name']);
129 pyroutes.register('repo_creating_check', '/%(repo_name)s/repo_creating_check', ['repo_name']);
152 pyroutes.register('repo_creating_check', '/%(repo_name)s/repo_creating_check', ['repo_name']);
130 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
153 pyroutes.register('repo_summary_explicit', '/%(repo_name)s/summary', ['repo_name']);
131 pyroutes.register('repo_summary_commits', '/%(repo_name)s/summary-commits', ['repo_name']);
154 pyroutes.register('repo_summary_commits', '/%(repo_name)s/summary-commits', ['repo_name']);
132 pyroutes.register('repo_commit', '/%(repo_name)s/changeset/%(commit_id)s', ['repo_name', 'commit_id']);
155 pyroutes.register('repo_commit', '/%(repo_name)s/changeset/%(commit_id)s', ['repo_name', 'commit_id']);
133 pyroutes.register('repo_commit_children', '/%(repo_name)s/changeset_children/%(commit_id)s', ['repo_name', 'commit_id']);
156 pyroutes.register('repo_commit_children', '/%(repo_name)s/changeset_children/%(commit_id)s', ['repo_name', 'commit_id']);
134 pyroutes.register('repo_commit_parents', '/%(repo_name)s/changeset_parents/%(commit_id)s', ['repo_name', 'commit_id']);
157 pyroutes.register('repo_commit_parents', '/%(repo_name)s/changeset_parents/%(commit_id)s', ['repo_name', 'commit_id']);
135 pyroutes.register('repo_commit_raw', '/%(repo_name)s/changeset-diff/%(commit_id)s', ['repo_name', 'commit_id']);
158 pyroutes.register('repo_commit_raw', '/%(repo_name)s/changeset-diff/%(commit_id)s', ['repo_name', 'commit_id']);
136 pyroutes.register('repo_commit_patch', '/%(repo_name)s/changeset-patch/%(commit_id)s', ['repo_name', 'commit_id']);
159 pyroutes.register('repo_commit_patch', '/%(repo_name)s/changeset-patch/%(commit_id)s', ['repo_name', 'commit_id']);
137 pyroutes.register('repo_commit_download', '/%(repo_name)s/changeset-download/%(commit_id)s', ['repo_name', 'commit_id']);
160 pyroutes.register('repo_commit_download', '/%(repo_name)s/changeset-download/%(commit_id)s', ['repo_name', 'commit_id']);
138 pyroutes.register('repo_commit_data', '/%(repo_name)s/changeset-data/%(commit_id)s', ['repo_name', 'commit_id']);
161 pyroutes.register('repo_commit_data', '/%(repo_name)s/changeset-data/%(commit_id)s', ['repo_name', 'commit_id']);
139 pyroutes.register('repo_commit_comment_create', '/%(repo_name)s/changeset/%(commit_id)s/comment/create', ['repo_name', 'commit_id']);
162 pyroutes.register('repo_commit_comment_create', '/%(repo_name)s/changeset/%(commit_id)s/comment/create', ['repo_name', 'commit_id']);
140 pyroutes.register('repo_commit_comment_preview', '/%(repo_name)s/changeset/%(commit_id)s/comment/preview', ['repo_name', 'commit_id']);
163 pyroutes.register('repo_commit_comment_preview', '/%(repo_name)s/changeset/%(commit_id)s/comment/preview', ['repo_name', 'commit_id']);
141 pyroutes.register('repo_commit_comment_delete', '/%(repo_name)s/changeset/%(commit_id)s/comment/%(comment_id)s/delete', ['repo_name', 'commit_id', 'comment_id']);
164 pyroutes.register('repo_commit_comment_delete', '/%(repo_name)s/changeset/%(commit_id)s/comment/%(comment_id)s/delete', ['repo_name', 'commit_id', 'comment_id']);
142 pyroutes.register('repo_commit_raw_deprecated', '/%(repo_name)s/raw-changeset/%(commit_id)s', ['repo_name', 'commit_id']);
165 pyroutes.register('repo_commit_raw_deprecated', '/%(repo_name)s/raw-changeset/%(commit_id)s', ['repo_name', 'commit_id']);
143 pyroutes.register('repo_archivefile', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
166 pyroutes.register('repo_archivefile', '/%(repo_name)s/archive/%(fname)s', ['repo_name', 'fname']);
144 pyroutes.register('repo_files_diff', '/%(repo_name)s/diff/%(f_path)s', ['repo_name', 'f_path']);
167 pyroutes.register('repo_files_diff', '/%(repo_name)s/diff/%(f_path)s', ['repo_name', 'f_path']);
145 pyroutes.register('repo_files_diff_2way_redirect', '/%(repo_name)s/diff-2way/%(f_path)s', ['repo_name', 'f_path']);
168 pyroutes.register('repo_files_diff_2way_redirect', '/%(repo_name)s/diff-2way/%(f_path)s', ['repo_name', 'f_path']);
146 pyroutes.register('repo_files', '/%(repo_name)s/files/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
169 pyroutes.register('repo_files', '/%(repo_name)s/files/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
147 pyroutes.register('repo_files:default_path', '/%(repo_name)s/files/%(commit_id)s/', ['repo_name', 'commit_id']);
170 pyroutes.register('repo_files:default_path', '/%(repo_name)s/files/%(commit_id)s/', ['repo_name', 'commit_id']);
148 pyroutes.register('repo_files:default_commit', '/%(repo_name)s/files', ['repo_name']);
171 pyroutes.register('repo_files:default_commit', '/%(repo_name)s/files', ['repo_name']);
149 pyroutes.register('repo_files:rendered', '/%(repo_name)s/render/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
172 pyroutes.register('repo_files:rendered', '/%(repo_name)s/render/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
150 pyroutes.register('repo_files:annotated', '/%(repo_name)s/annotate/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
173 pyroutes.register('repo_files:annotated', '/%(repo_name)s/annotate/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
151 pyroutes.register('repo_files:annotated_previous', '/%(repo_name)s/annotate-previous/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
174 pyroutes.register('repo_files:annotated_previous', '/%(repo_name)s/annotate-previous/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
152 pyroutes.register('repo_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
175 pyroutes.register('repo_nodetree_full', '/%(repo_name)s/nodetree_full/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
153 pyroutes.register('repo_nodetree_full:default_path', '/%(repo_name)s/nodetree_full/%(commit_id)s/', ['repo_name', 'commit_id']);
176 pyroutes.register('repo_nodetree_full:default_path', '/%(repo_name)s/nodetree_full/%(commit_id)s/', ['repo_name', 'commit_id']);
154 pyroutes.register('repo_files_nodelist', '/%(repo_name)s/nodelist/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
177 pyroutes.register('repo_files_nodelist', '/%(repo_name)s/nodelist/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
155 pyroutes.register('repo_file_raw', '/%(repo_name)s/raw/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
178 pyroutes.register('repo_file_raw', '/%(repo_name)s/raw/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
156 pyroutes.register('repo_file_download', '/%(repo_name)s/download/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
179 pyroutes.register('repo_file_download', '/%(repo_name)s/download/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
157 pyroutes.register('repo_file_download:legacy', '/%(repo_name)s/rawfile/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
180 pyroutes.register('repo_file_download:legacy', '/%(repo_name)s/rawfile/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
158 pyroutes.register('repo_file_history', '/%(repo_name)s/history/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
181 pyroutes.register('repo_file_history', '/%(repo_name)s/history/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
159 pyroutes.register('repo_file_authors', '/%(repo_name)s/authors/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
182 pyroutes.register('repo_file_authors', '/%(repo_name)s/authors/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
160 pyroutes.register('repo_files_remove_file', '/%(repo_name)s/remove_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
183 pyroutes.register('repo_files_remove_file', '/%(repo_name)s/remove_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
161 pyroutes.register('repo_files_delete_file', '/%(repo_name)s/delete_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
184 pyroutes.register('repo_files_delete_file', '/%(repo_name)s/delete_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
162 pyroutes.register('repo_files_edit_file', '/%(repo_name)s/edit_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
185 pyroutes.register('repo_files_edit_file', '/%(repo_name)s/edit_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
163 pyroutes.register('repo_files_update_file', '/%(repo_name)s/update_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
186 pyroutes.register('repo_files_update_file', '/%(repo_name)s/update_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
164 pyroutes.register('repo_files_add_file', '/%(repo_name)s/add_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
187 pyroutes.register('repo_files_add_file', '/%(repo_name)s/add_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
165 pyroutes.register('repo_files_create_file', '/%(repo_name)s/create_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
188 pyroutes.register('repo_files_create_file', '/%(repo_name)s/create_file/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
166 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
189 pyroutes.register('repo_refs_data', '/%(repo_name)s/refs-data', ['repo_name']);
167 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
190 pyroutes.register('repo_refs_changelog_data', '/%(repo_name)s/refs-data-changelog', ['repo_name']);
168 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
191 pyroutes.register('repo_stats', '/%(repo_name)s/repo_stats/%(commit_id)s', ['repo_name', 'commit_id']);
169 pyroutes.register('repo_changelog', '/%(repo_name)s/changelog', ['repo_name']);
192 pyroutes.register('repo_changelog', '/%(repo_name)s/changelog', ['repo_name']);
170 pyroutes.register('repo_changelog_file', '/%(repo_name)s/changelog/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
193 pyroutes.register('repo_changelog_file', '/%(repo_name)s/changelog/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
171 pyroutes.register('repo_changelog_elements', '/%(repo_name)s/changelog_elements', ['repo_name']);
194 pyroutes.register('repo_changelog_elements', '/%(repo_name)s/changelog_elements', ['repo_name']);
172 pyroutes.register('repo_changelog_elements_file', '/%(repo_name)s/changelog_elements/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
195 pyroutes.register('repo_changelog_elements_file', '/%(repo_name)s/changelog_elements/%(commit_id)s/%(f_path)s', ['repo_name', 'commit_id', 'f_path']);
173 pyroutes.register('repo_compare_select', '/%(repo_name)s/compare', ['repo_name']);
196 pyroutes.register('repo_compare_select', '/%(repo_name)s/compare', ['repo_name']);
174 pyroutes.register('repo_compare', '/%(repo_name)s/compare/%(source_ref_type)s@%(source_ref)s...%(target_ref_type)s@%(target_ref)s', ['repo_name', 'source_ref_type', 'source_ref', 'target_ref_type', 'target_ref']);
197 pyroutes.register('repo_compare', '/%(repo_name)s/compare/%(source_ref_type)s@%(source_ref)s...%(target_ref_type)s@%(target_ref)s', ['repo_name', 'source_ref_type', 'source_ref', 'target_ref_type', 'target_ref']);
175 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
198 pyroutes.register('tags_home', '/%(repo_name)s/tags', ['repo_name']);
176 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
199 pyroutes.register('branches_home', '/%(repo_name)s/branches', ['repo_name']);
177 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
200 pyroutes.register('bookmarks_home', '/%(repo_name)s/bookmarks', ['repo_name']);
178 pyroutes.register('repo_fork_new', '/%(repo_name)s/fork', ['repo_name']);
201 pyroutes.register('repo_fork_new', '/%(repo_name)s/fork', ['repo_name']);
179 pyroutes.register('repo_fork_create', '/%(repo_name)s/fork/create', ['repo_name']);
202 pyroutes.register('repo_fork_create', '/%(repo_name)s/fork/create', ['repo_name']);
180 pyroutes.register('repo_forks_show_all', '/%(repo_name)s/forks', ['repo_name']);
203 pyroutes.register('repo_forks_show_all', '/%(repo_name)s/forks', ['repo_name']);
181 pyroutes.register('repo_forks_data', '/%(repo_name)s/forks/data', ['repo_name']);
204 pyroutes.register('repo_forks_data', '/%(repo_name)s/forks/data', ['repo_name']);
182 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
205 pyroutes.register('pullrequest_show', '/%(repo_name)s/pull-request/%(pull_request_id)s', ['repo_name', 'pull_request_id']);
183 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
206 pyroutes.register('pullrequest_show_all', '/%(repo_name)s/pull-request', ['repo_name']);
184 pyroutes.register('pullrequest_show_all_data', '/%(repo_name)s/pull-request-data', ['repo_name']);
207 pyroutes.register('pullrequest_show_all_data', '/%(repo_name)s/pull-request-data', ['repo_name']);
185 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
208 pyroutes.register('pullrequest_repo_refs', '/%(repo_name)s/pull-request/refs/%(target_repo_name)s', ['repo_name', 'target_repo_name']);
186 pyroutes.register('pullrequest_repo_destinations', '/%(repo_name)s/pull-request/repo-destinations', ['repo_name']);
209 pyroutes.register('pullrequest_repo_destinations', '/%(repo_name)s/pull-request/repo-destinations', ['repo_name']);
187 pyroutes.register('pullrequest_new', '/%(repo_name)s/pull-request/new', ['repo_name']);
210 pyroutes.register('pullrequest_new', '/%(repo_name)s/pull-request/new', ['repo_name']);
188 pyroutes.register('pullrequest_create', '/%(repo_name)s/pull-request/create', ['repo_name']);
211 pyroutes.register('pullrequest_create', '/%(repo_name)s/pull-request/create', ['repo_name']);
189 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s/update', ['repo_name', 'pull_request_id']);
212 pyroutes.register('pullrequest_update', '/%(repo_name)s/pull-request/%(pull_request_id)s/update', ['repo_name', 'pull_request_id']);
190 pyroutes.register('pullrequest_merge', '/%(repo_name)s/pull-request/%(pull_request_id)s/merge', ['repo_name', 'pull_request_id']);
213 pyroutes.register('pullrequest_merge', '/%(repo_name)s/pull-request/%(pull_request_id)s/merge', ['repo_name', 'pull_request_id']);
191 pyroutes.register('pullrequest_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/delete', ['repo_name', 'pull_request_id']);
214 pyroutes.register('pullrequest_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/delete', ['repo_name', 'pull_request_id']);
192 pyroutes.register('pullrequest_comment_create', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment', ['repo_name', 'pull_request_id']);
215 pyroutes.register('pullrequest_comment_create', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment', ['repo_name', 'pull_request_id']);
193 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment/%(comment_id)s/delete', ['repo_name', 'pull_request_id', 'comment_id']);
216 pyroutes.register('pullrequest_comment_delete', '/%(repo_name)s/pull-request/%(pull_request_id)s/comment/%(comment_id)s/delete', ['repo_name', 'pull_request_id', 'comment_id']);
194 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
217 pyroutes.register('edit_repo', '/%(repo_name)s/settings', ['repo_name']);
195 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
218 pyroutes.register('edit_repo_advanced', '/%(repo_name)s/settings/advanced', ['repo_name']);
196 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
219 pyroutes.register('edit_repo_advanced_delete', '/%(repo_name)s/settings/advanced/delete', ['repo_name']);
197 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
220 pyroutes.register('edit_repo_advanced_locking', '/%(repo_name)s/settings/advanced/locking', ['repo_name']);
198 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
221 pyroutes.register('edit_repo_advanced_journal', '/%(repo_name)s/settings/advanced/journal', ['repo_name']);
199 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
222 pyroutes.register('edit_repo_advanced_fork', '/%(repo_name)s/settings/advanced/fork', ['repo_name']);
200 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
223 pyroutes.register('edit_repo_caches', '/%(repo_name)s/settings/caches', ['repo_name']);
201 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
224 pyroutes.register('edit_repo_perms', '/%(repo_name)s/settings/permissions', ['repo_name']);
202 pyroutes.register('edit_repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
225 pyroutes.register('edit_repo_maintenance', '/%(repo_name)s/settings/maintenance', ['repo_name']);
203 pyroutes.register('edit_repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
226 pyroutes.register('edit_repo_maintenance_execute', '/%(repo_name)s/settings/maintenance/execute', ['repo_name']);
204 pyroutes.register('edit_repo_fields', '/%(repo_name)s/settings/fields', ['repo_name']);
227 pyroutes.register('edit_repo_fields', '/%(repo_name)s/settings/fields', ['repo_name']);
205 pyroutes.register('edit_repo_fields_create', '/%(repo_name)s/settings/fields/create', ['repo_name']);
228 pyroutes.register('edit_repo_fields_create', '/%(repo_name)s/settings/fields/create', ['repo_name']);
206 pyroutes.register('edit_repo_fields_delete', '/%(repo_name)s/settings/fields/%(field_id)s/delete', ['repo_name', 'field_id']);
229 pyroutes.register('edit_repo_fields_delete', '/%(repo_name)s/settings/fields/%(field_id)s/delete', ['repo_name', 'field_id']);
207 pyroutes.register('repo_edit_toggle_locking', '/%(repo_name)s/settings/toggle_locking', ['repo_name']);
230 pyroutes.register('repo_edit_toggle_locking', '/%(repo_name)s/settings/toggle_locking', ['repo_name']);
208 pyroutes.register('edit_repo_remote', '/%(repo_name)s/settings/remote', ['repo_name']);
231 pyroutes.register('edit_repo_remote', '/%(repo_name)s/settings/remote', ['repo_name']);
209 pyroutes.register('edit_repo_remote_pull', '/%(repo_name)s/settings/remote/pull', ['repo_name']);
232 pyroutes.register('edit_repo_remote_pull', '/%(repo_name)s/settings/remote/pull', ['repo_name']);
210 pyroutes.register('edit_repo_statistics', '/%(repo_name)s/settings/statistics', ['repo_name']);
233 pyroutes.register('edit_repo_statistics', '/%(repo_name)s/settings/statistics', ['repo_name']);
211 pyroutes.register('edit_repo_statistics_reset', '/%(repo_name)s/settings/statistics/update', ['repo_name']);
234 pyroutes.register('edit_repo_statistics_reset', '/%(repo_name)s/settings/statistics/update', ['repo_name']);
212 pyroutes.register('edit_repo_issuetracker', '/%(repo_name)s/settings/issue_trackers', ['repo_name']);
235 pyroutes.register('edit_repo_issuetracker', '/%(repo_name)s/settings/issue_trackers', ['repo_name']);
213 pyroutes.register('edit_repo_issuetracker_test', '/%(repo_name)s/settings/issue_trackers/test', ['repo_name']);
236 pyroutes.register('edit_repo_issuetracker_test', '/%(repo_name)s/settings/issue_trackers/test', ['repo_name']);
214 pyroutes.register('edit_repo_issuetracker_delete', '/%(repo_name)s/settings/issue_trackers/delete', ['repo_name']);
237 pyroutes.register('edit_repo_issuetracker_delete', '/%(repo_name)s/settings/issue_trackers/delete', ['repo_name']);
215 pyroutes.register('edit_repo_issuetracker_update', '/%(repo_name)s/settings/issue_trackers/update', ['repo_name']);
238 pyroutes.register('edit_repo_issuetracker_update', '/%(repo_name)s/settings/issue_trackers/update', ['repo_name']);
216 pyroutes.register('edit_repo_vcs', '/%(repo_name)s/settings/vcs', ['repo_name']);
239 pyroutes.register('edit_repo_vcs', '/%(repo_name)s/settings/vcs', ['repo_name']);
217 pyroutes.register('edit_repo_vcs_update', '/%(repo_name)s/settings/vcs/update', ['repo_name']);
240 pyroutes.register('edit_repo_vcs_update', '/%(repo_name)s/settings/vcs/update', ['repo_name']);
218 pyroutes.register('edit_repo_vcs_svn_pattern_delete', '/%(repo_name)s/settings/vcs/svn_pattern/delete', ['repo_name']);
241 pyroutes.register('edit_repo_vcs_svn_pattern_delete', '/%(repo_name)s/settings/vcs/svn_pattern/delete', ['repo_name']);
219 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
242 pyroutes.register('repo_reviewers', '/%(repo_name)s/settings/review/rules', ['repo_name']);
220 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/settings/review/default-reviewers', ['repo_name']);
243 pyroutes.register('repo_default_reviewers_data', '/%(repo_name)s/settings/review/default-reviewers', ['repo_name']);
221 pyroutes.register('edit_repo_strip', '/%(repo_name)s/settings/strip', ['repo_name']);
244 pyroutes.register('edit_repo_strip', '/%(repo_name)s/settings/strip', ['repo_name']);
222 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
245 pyroutes.register('strip_check', '/%(repo_name)s/settings/strip_check', ['repo_name']);
223 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
246 pyroutes.register('strip_execute', '/%(repo_name)s/settings/strip_execute', ['repo_name']);
224 pyroutes.register('edit_repo_audit_logs', '/%(repo_name)s/settings/audit_logs', ['repo_name']);
247 pyroutes.register('edit_repo_audit_logs', '/%(repo_name)s/settings/audit_logs', ['repo_name']);
225 pyroutes.register('rss_feed_home', '/%(repo_name)s/feed/rss', ['repo_name']);
248 pyroutes.register('rss_feed_home', '/%(repo_name)s/feed/rss', ['repo_name']);
226 pyroutes.register('atom_feed_home', '/%(repo_name)s/feed/atom', ['repo_name']);
249 pyroutes.register('atom_feed_home', '/%(repo_name)s/feed/atom', ['repo_name']);
227 pyroutes.register('repo_summary', '/%(repo_name)s', ['repo_name']);
250 pyroutes.register('repo_summary', '/%(repo_name)s', ['repo_name']);
228 pyroutes.register('repo_summary_slash', '/%(repo_name)s/', ['repo_name']);
251 pyroutes.register('repo_summary_slash', '/%(repo_name)s/', ['repo_name']);
229 pyroutes.register('edit_repo_group', '/%(repo_group_name)s/_edit', ['repo_group_name']);
252 pyroutes.register('edit_repo_group', '/%(repo_group_name)s/_edit', ['repo_group_name']);
230 pyroutes.register('edit_repo_group_advanced', '/%(repo_group_name)s/_settings/advanced', ['repo_group_name']);
253 pyroutes.register('edit_repo_group_advanced', '/%(repo_group_name)s/_settings/advanced', ['repo_group_name']);
231 pyroutes.register('edit_repo_group_advanced_delete', '/%(repo_group_name)s/_settings/advanced/delete', ['repo_group_name']);
254 pyroutes.register('edit_repo_group_advanced_delete', '/%(repo_group_name)s/_settings/advanced/delete', ['repo_group_name']);
232 pyroutes.register('edit_repo_group_perms', '/%(repo_group_name)s/_settings/permissions', ['repo_group_name']);
255 pyroutes.register('edit_repo_group_perms', '/%(repo_group_name)s/_settings/permissions', ['repo_group_name']);
233 pyroutes.register('edit_repo_group_perms_update', '/%(repo_group_name)s/_settings/permissions/update', ['repo_group_name']);
256 pyroutes.register('edit_repo_group_perms_update', '/%(repo_group_name)s/_settings/permissions/update', ['repo_group_name']);
234 pyroutes.register('repo_group_home', '/%(repo_group_name)s', ['repo_group_name']);
257 pyroutes.register('repo_group_home', '/%(repo_group_name)s', ['repo_group_name']);
235 pyroutes.register('repo_group_home_slash', '/%(repo_group_name)s/', ['repo_group_name']);
258 pyroutes.register('repo_group_home_slash', '/%(repo_group_name)s/', ['repo_group_name']);
236 pyroutes.register('user_group_members_data', '/_admin/user_groups/%(user_group_id)s/members', ['user_group_id']);
259 pyroutes.register('user_group_members_data', '/_admin/user_groups/%(user_group_id)s/members', ['user_group_id']);
237 pyroutes.register('edit_user_group_perms_summary', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary', ['user_group_id']);
260 pyroutes.register('edit_user_group_perms_summary', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary', ['user_group_id']);
238 pyroutes.register('edit_user_group_perms_summary_json', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary/json', ['user_group_id']);
261 pyroutes.register('edit_user_group_perms_summary_json', '/_admin/user_groups/%(user_group_id)s/edit/permissions_summary/json', ['user_group_id']);
239 pyroutes.register('edit_user_group', '/_admin/user_groups/%(user_group_id)s/edit', ['user_group_id']);
262 pyroutes.register('edit_user_group', '/_admin/user_groups/%(user_group_id)s/edit', ['user_group_id']);
240 pyroutes.register('user_groups_update', '/_admin/user_groups/%(user_group_id)s/update', ['user_group_id']);
263 pyroutes.register('user_groups_update', '/_admin/user_groups/%(user_group_id)s/update', ['user_group_id']);
241 pyroutes.register('edit_user_group_global_perms', '/_admin/user_groups/%(user_group_id)s/edit/global_permissions', ['user_group_id']);
264 pyroutes.register('edit_user_group_global_perms', '/_admin/user_groups/%(user_group_id)s/edit/global_permissions', ['user_group_id']);
242 pyroutes.register('edit_user_group_global_perms_update', '/_admin/user_groups/%(user_group_id)s/edit/global_permissions/update', ['user_group_id']);
265 pyroutes.register('edit_user_group_global_perms_update', '/_admin/user_groups/%(user_group_id)s/edit/global_permissions/update', ['user_group_id']);
243 pyroutes.register('edit_user_group_perms', '/_admin/user_groups/%(user_group_id)s/edit/permissions', ['user_group_id']);
266 pyroutes.register('edit_user_group_perms', '/_admin/user_groups/%(user_group_id)s/edit/permissions', ['user_group_id']);
244 pyroutes.register('edit_user_group_perms_update', '/_admin/user_groups/%(user_group_id)s/edit/permissions/update', ['user_group_id']);
267 pyroutes.register('edit_user_group_perms_update', '/_admin/user_groups/%(user_group_id)s/edit/permissions/update', ['user_group_id']);
245 pyroutes.register('edit_user_group_advanced', '/_admin/user_groups/%(user_group_id)s/edit/advanced', ['user_group_id']);
268 pyroutes.register('edit_user_group_advanced', '/_admin/user_groups/%(user_group_id)s/edit/advanced', ['user_group_id']);
246 pyroutes.register('edit_user_group_advanced_sync', '/_admin/user_groups/%(user_group_id)s/edit/advanced/sync', ['user_group_id']);
269 pyroutes.register('edit_user_group_advanced_sync', '/_admin/user_groups/%(user_group_id)s/edit/advanced/sync', ['user_group_id']);
247 pyroutes.register('user_groups_delete', '/_admin/user_groups/%(user_group_id)s/delete', ['user_group_id']);
270 pyroutes.register('user_groups_delete', '/_admin/user_groups/%(user_group_id)s/delete', ['user_group_id']);
248 pyroutes.register('search', '/_admin/search', []);
271 pyroutes.register('search', '/_admin/search', []);
249 pyroutes.register('search_repo', '/%(repo_name)s/search', ['repo_name']);
272 pyroutes.register('search_repo', '/%(repo_name)s/search', ['repo_name']);
250 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
273 pyroutes.register('user_profile', '/_profiles/%(username)s', ['username']);
251 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
274 pyroutes.register('my_account_profile', '/_admin/my_account/profile', []);
252 pyroutes.register('my_account_edit', '/_admin/my_account/edit', []);
275 pyroutes.register('my_account_edit', '/_admin/my_account/edit', []);
253 pyroutes.register('my_account_update', '/_admin/my_account/update', []);
276 pyroutes.register('my_account_update', '/_admin/my_account/update', []);
254 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
277 pyroutes.register('my_account_password', '/_admin/my_account/password', []);
255 pyroutes.register('my_account_password_update', '/_admin/my_account/password/update', []);
278 pyroutes.register('my_account_password_update', '/_admin/my_account/password/update', []);
256 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
279 pyroutes.register('my_account_auth_tokens', '/_admin/my_account/auth_tokens', []);
257 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
280 pyroutes.register('my_account_auth_tokens_add', '/_admin/my_account/auth_tokens/new', []);
258 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
281 pyroutes.register('my_account_auth_tokens_delete', '/_admin/my_account/auth_tokens/delete', []);
259 pyroutes.register('my_account_ssh_keys', '/_admin/my_account/ssh_keys', []);
282 pyroutes.register('my_account_ssh_keys', '/_admin/my_account/ssh_keys', []);
260 pyroutes.register('my_account_ssh_keys_generate', '/_admin/my_account/ssh_keys/generate', []);
283 pyroutes.register('my_account_ssh_keys_generate', '/_admin/my_account/ssh_keys/generate', []);
261 pyroutes.register('my_account_ssh_keys_add', '/_admin/my_account/ssh_keys/new', []);
284 pyroutes.register('my_account_ssh_keys_add', '/_admin/my_account/ssh_keys/new', []);
262 pyroutes.register('my_account_ssh_keys_delete', '/_admin/my_account/ssh_keys/delete', []);
285 pyroutes.register('my_account_ssh_keys_delete', '/_admin/my_account/ssh_keys/delete', []);
263 pyroutes.register('my_account_emails', '/_admin/my_account/emails', []);
286 pyroutes.register('my_account_emails', '/_admin/my_account/emails', []);
264 pyroutes.register('my_account_emails_add', '/_admin/my_account/emails/new', []);
287 pyroutes.register('my_account_emails_add', '/_admin/my_account/emails/new', []);
265 pyroutes.register('my_account_emails_delete', '/_admin/my_account/emails/delete', []);
288 pyroutes.register('my_account_emails_delete', '/_admin/my_account/emails/delete', []);
266 pyroutes.register('my_account_repos', '/_admin/my_account/repos', []);
289 pyroutes.register('my_account_repos', '/_admin/my_account/repos', []);
267 pyroutes.register('my_account_watched', '/_admin/my_account/watched', []);
290 pyroutes.register('my_account_watched', '/_admin/my_account/watched', []);
268 pyroutes.register('my_account_perms', '/_admin/my_account/perms', []);
291 pyroutes.register('my_account_perms', '/_admin/my_account/perms', []);
269 pyroutes.register('my_account_notifications', '/_admin/my_account/notifications', []);
292 pyroutes.register('my_account_notifications', '/_admin/my_account/notifications', []);
270 pyroutes.register('my_account_notifications_toggle_visibility', '/_admin/my_account/toggle_visibility', []);
293 pyroutes.register('my_account_notifications_toggle_visibility', '/_admin/my_account/toggle_visibility', []);
271 pyroutes.register('my_account_pullrequests', '/_admin/my_account/pull_requests', []);
294 pyroutes.register('my_account_pullrequests', '/_admin/my_account/pull_requests', []);
272 pyroutes.register('my_account_pullrequests_data', '/_admin/my_account/pull_requests/data', []);
295 pyroutes.register('my_account_pullrequests_data', '/_admin/my_account/pull_requests/data', []);
273 pyroutes.register('notifications_show_all', '/_admin/notifications', []);
296 pyroutes.register('notifications_show_all', '/_admin/notifications', []);
274 pyroutes.register('notifications_mark_all_read', '/_admin/notifications/mark_all_read', []);
297 pyroutes.register('notifications_mark_all_read', '/_admin/notifications/mark_all_read', []);
275 pyroutes.register('notifications_show', '/_admin/notifications/%(notification_id)s', ['notification_id']);
298 pyroutes.register('notifications_show', '/_admin/notifications/%(notification_id)s', ['notification_id']);
276 pyroutes.register('notifications_update', '/_admin/notifications/%(notification_id)s/update', ['notification_id']);
299 pyroutes.register('notifications_update', '/_admin/notifications/%(notification_id)s/update', ['notification_id']);
277 pyroutes.register('notifications_delete', '/_admin/notifications/%(notification_id)s/delete', ['notification_id']);
300 pyroutes.register('notifications_delete', '/_admin/notifications/%(notification_id)s/delete', ['notification_id']);
278 pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []);
301 pyroutes.register('my_account_notifications_test_channelstream', '/_admin/my_account/test_channelstream', []);
279 pyroutes.register('gists_show', '/_admin/gists', []);
302 pyroutes.register('gists_show', '/_admin/gists', []);
280 pyroutes.register('gists_new', '/_admin/gists/new', []);
303 pyroutes.register('gists_new', '/_admin/gists/new', []);
281 pyroutes.register('gists_create', '/_admin/gists/create', []);
304 pyroutes.register('gists_create', '/_admin/gists/create', []);
282 pyroutes.register('gist_show', '/_admin/gists/%(gist_id)s', ['gist_id']);
305 pyroutes.register('gist_show', '/_admin/gists/%(gist_id)s', ['gist_id']);
283 pyroutes.register('gist_delete', '/_admin/gists/%(gist_id)s/delete', ['gist_id']);
306 pyroutes.register('gist_delete', '/_admin/gists/%(gist_id)s/delete', ['gist_id']);
284 pyroutes.register('gist_edit', '/_admin/gists/%(gist_id)s/edit', ['gist_id']);
307 pyroutes.register('gist_edit', '/_admin/gists/%(gist_id)s/edit', ['gist_id']);
285 pyroutes.register('gist_edit_check_revision', '/_admin/gists/%(gist_id)s/edit/check_revision', ['gist_id']);
308 pyroutes.register('gist_edit_check_revision', '/_admin/gists/%(gist_id)s/edit/check_revision', ['gist_id']);
286 pyroutes.register('gist_update', '/_admin/gists/%(gist_id)s/update', ['gist_id']);
309 pyroutes.register('gist_update', '/_admin/gists/%(gist_id)s/update', ['gist_id']);
287 pyroutes.register('gist_show_rev', '/_admin/gists/%(gist_id)s/%(revision)s', ['gist_id', 'revision']);
310 pyroutes.register('gist_show_rev', '/_admin/gists/%(gist_id)s/%(revision)s', ['gist_id', 'revision']);
288 pyroutes.register('gist_show_formatted', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s', ['gist_id', 'revision', 'format']);
311 pyroutes.register('gist_show_formatted', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s', ['gist_id', 'revision', 'format']);
289 pyroutes.register('gist_show_formatted_path', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s/%(f_path)s', ['gist_id', 'revision', 'format', 'f_path']);
312 pyroutes.register('gist_show_formatted_path', '/_admin/gists/%(gist_id)s/%(revision)s/%(format)s/%(f_path)s', ['gist_id', 'revision', 'format', 'f_path']);
290 pyroutes.register('debug_style_home', '/_admin/debug_style', []);
313 pyroutes.register('debug_style_home', '/_admin/debug_style', []);
291 pyroutes.register('debug_style_template', '/_admin/debug_style/t/%(t_path)s', ['t_path']);
314 pyroutes.register('debug_style_template', '/_admin/debug_style/t/%(t_path)s', ['t_path']);
292 pyroutes.register('apiv2', '/_admin/api', []);
315 pyroutes.register('apiv2', '/_admin/api', []);
293 }
316 }
@@ -1,69 +1,69 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="base.mako"/>
2 <%inherit file="base.mako"/>
3
3
4 <%def name="breadcrumbs_links()">
4 <%def name="breadcrumbs_links()">
5 %if c.repo:
5 %if c.repo:
6 ${h.link_to('Settings',h.route_path('edit_repo', repo_name=c.repo.repo_name))}
6 ${h.link_to('Settings',h.route_path('edit_repo', repo_name=c.repo.repo_name))}
7 &raquo;
7 &raquo;
8 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_integrations_home', repo_name=c.repo.repo_name))}
8 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_integrations_home', repo_name=c.repo.repo_name))}
9 &raquo;
9 &raquo;
10 ${h.link_to(c.current_IntegrationType.display_name,
10 ${h.link_to(c.current_IntegrationType.display_name,
11 request.route_url(route_name='repo_integrations_list',
11 request.route_url(route_name='repo_integrations_list',
12 repo_name=c.repo.repo_name,
12 repo_name=c.repo.repo_name,
13 integration=c.current_IntegrationType.key))}
13 integration=c.current_IntegrationType.key))}
14 %elif c.repo_group:
14 %elif c.repo_group:
15 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
15 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
16 &raquo;
16 &raquo;
17 ${h.link_to(_('Repository Groups'),h.route_path('repo_groups'))}
17 ${h.link_to(_('Repository Groups'),h.route_path('repo_groups'))}
18 &raquo;
18 &raquo;
19 ${h.link_to(c.repo_group.group_name,h.route_path('edit_repo_group', repo_group_name=c.repo_group.group_name))}
19 ${h.link_to(c.repo_group.group_name,h.route_path('edit_repo_group', repo_group_name=c.repo_group.group_name))}
20 &raquo;
20 &raquo;
21 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_group_integrations_home', repo_group_name=c.repo_group.group_name))}
21 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_group_integrations_home', repo_group_name=c.repo_group.group_name))}
22 &raquo;
22 &raquo;
23 ${h.link_to(c.current_IntegrationType.display_name,
23 ${h.link_to(c.current_IntegrationType.display_name,
24 request.route_url(route_name='repo_group_integrations_list',
24 request.route_url(route_name='repo_group_integrations_list',
25 repo_group_name=c.repo_group.group_name,
25 repo_group_name=c.repo_group.group_name,
26 integration=c.current_IntegrationType.key))}
26 integration=c.current_IntegrationType.key))}
27 %else:
27 %else:
28 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
28 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
29 &raquo;
29 &raquo;
30 ${h.link_to(_('Settings'),h.url('admin_settings'))}
30 ${h.link_to(_('Settings'),h.route_path('admin_settings'))}
31 &raquo;
31 &raquo;
32 ${h.link_to(_('Integrations'),request.route_url(route_name='global_integrations_home'))}
32 ${h.link_to(_('Integrations'),request.route_url(route_name='global_integrations_home'))}
33 &raquo;
33 &raquo;
34 ${h.link_to(c.current_IntegrationType.display_name,
34 ${h.link_to(c.current_IntegrationType.display_name,
35 request.route_url(route_name='global_integrations_list',
35 request.route_url(route_name='global_integrations_list',
36 integration=c.current_IntegrationType.key))}
36 integration=c.current_IntegrationType.key))}
37 %endif
37 %endif
38
38
39 %if c.integration:
39 %if c.integration:
40 &raquo;
40 &raquo;
41 ${c.integration.name}
41 ${c.integration.name}
42 %elif c.current_IntegrationType:
42 %elif c.current_IntegrationType:
43 &raquo;
43 &raquo;
44 ${c.current_IntegrationType.display_name}
44 ${c.current_IntegrationType.display_name}
45 %endif
45 %endif
46 </%def>
46 </%def>
47
47
48 <style>
48 <style>
49 .control-inputs.item-options, .control-inputs.item-settings {
49 .control-inputs.item-options, .control-inputs.item-settings {
50 float: left;
50 float: left;
51 width: 100%;
51 width: 100%;
52 }
52 }
53 </style>
53 </style>
54 <div class="panel panel-default">
54 <div class="panel panel-default">
55 <div class="panel-heading">
55 <div class="panel-heading">
56 <h2 class="panel-title">
56 <h2 class="panel-title">
57 %if c.integration:
57 %if c.integration:
58 ${c.current_IntegrationType.display_name} - ${c.integration.name}
58 ${c.current_IntegrationType.display_name} - ${c.integration.name}
59 %else:
59 %else:
60 ${_('Create New %(integration_type)s Integration') % {
60 ${_('Create New %(integration_type)s Integration') % {
61 'integration_type': c.current_IntegrationType.display_name
61 'integration_type': c.current_IntegrationType.display_name
62 }}
62 }}
63 %endif
63 %endif
64 </h2>
64 </h2>
65 </div>
65 </div>
66 <div class="panel-body">
66 <div class="panel-body">
67 ${c.form.render() | n}
67 ${c.form.render() | n}
68 </div>
68 </div>
69 </div>
69 </div>
@@ -1,256 +1,256 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="base.mako"/>
2 <%inherit file="base.mako"/>
3
3
4 <%def name="breadcrumbs_links()">
4 <%def name="breadcrumbs_links()">
5 %if c.repo:
5 %if c.repo:
6 ${h.link_to('Settings',h.route_path('edit_repo', repo_name=c.repo.repo_name))}
6 ${h.link_to('Settings',h.route_path('edit_repo', repo_name=c.repo.repo_name))}
7 %elif c.repo_group:
7 %elif c.repo_group:
8 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
8 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
9 &raquo;
9 &raquo;
10 ${h.link_to(_('Repository Groups'),h.route_path('repo_groups'))}
10 ${h.link_to(_('Repository Groups'),h.route_path('repo_groups'))}
11 &raquo;
11 &raquo;
12 ${h.link_to(c.repo_group.group_name,h.route_path('edit_repo_group', repo_group_name=c.repo_group.group_name))}
12 ${h.link_to(c.repo_group.group_name,h.route_path('edit_repo_group', repo_group_name=c.repo_group.group_name))}
13 %else:
13 %else:
14 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
14 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
15 &raquo;
15 &raquo;
16 ${h.link_to(_('Settings'),h.url('admin_settings'))}
16 ${h.link_to(_('Settings'),h.route_path('admin_settings'))}
17 %endif
17 %endif
18 %if c.current_IntegrationType:
18 %if c.current_IntegrationType:
19 &raquo;
19 &raquo;
20 %if c.repo:
20 %if c.repo:
21 ${h.link_to(_('Integrations'),
21 ${h.link_to(_('Integrations'),
22 request.route_path(route_name='repo_integrations_home',
22 request.route_path(route_name='repo_integrations_home',
23 repo_name=c.repo.repo_name))}
23 repo_name=c.repo.repo_name))}
24 %elif c.repo_group:
24 %elif c.repo_group:
25 ${h.link_to(_('Integrations'),
25 ${h.link_to(_('Integrations'),
26 request.route_path(route_name='repo_group_integrations_home',
26 request.route_path(route_name='repo_group_integrations_home',
27 repo_group_name=c.repo_group.group_name))}
27 repo_group_name=c.repo_group.group_name))}
28 %else:
28 %else:
29 ${h.link_to(_('Integrations'),
29 ${h.link_to(_('Integrations'),
30 request.route_path(route_name='global_integrations_home'))}
30 request.route_path(route_name='global_integrations_home'))}
31 %endif
31 %endif
32 &raquo;
32 &raquo;
33 ${c.current_IntegrationType.display_name}
33 ${c.current_IntegrationType.display_name}
34 %else:
34 %else:
35 &raquo;
35 &raquo;
36 ${_('Integrations')}
36 ${_('Integrations')}
37 %endif
37 %endif
38 </%def>
38 </%def>
39
39
40 <div class="panel panel-default">
40 <div class="panel panel-default">
41 <div class="panel-heading">
41 <div class="panel-heading">
42 <h3 class="panel-title">
42 <h3 class="panel-title">
43 %if c.repo:
43 %if c.repo:
44 ${_('Current Integrations for Repository: {repo_name}').format(repo_name=c.repo.repo_name)}
44 ${_('Current Integrations for Repository: {repo_name}').format(repo_name=c.repo.repo_name)}
45 %elif c.repo_group:
45 %elif c.repo_group:
46 ${_('Current Integrations for repository group: {repo_group_name}').format(repo_group_name=c.repo_group.group_name)}
46 ${_('Current Integrations for repository group: {repo_group_name}').format(repo_group_name=c.repo_group.group_name)}
47 %else:
47 %else:
48 ${_('Current Integrations')}
48 ${_('Current Integrations')}
49 %endif
49 %endif
50 </h3>
50 </h3>
51 </div>
51 </div>
52 <div class="panel-body">
52 <div class="panel-body">
53 <%
53 <%
54 if c.repo:
54 if c.repo:
55 home_url = request.route_path('repo_integrations_home',
55 home_url = request.route_path('repo_integrations_home',
56 repo_name=c.repo.repo_name)
56 repo_name=c.repo.repo_name)
57 elif c.repo_group:
57 elif c.repo_group:
58 home_url = request.route_path('repo_group_integrations_home',
58 home_url = request.route_path('repo_group_integrations_home',
59 repo_group_name=c.repo_group.group_name)
59 repo_group_name=c.repo_group.group_name)
60 else:
60 else:
61 home_url = request.route_path('global_integrations_home')
61 home_url = request.route_path('global_integrations_home')
62 %>
62 %>
63
63
64 <a href="${home_url}" class="btn ${not c.current_IntegrationType and 'btn-primary' or ''}">${_('All')}</a>
64 <a href="${home_url}" class="btn ${not c.current_IntegrationType and 'btn-primary' or ''}">${_('All')}</a>
65
65
66 %for integration_key, IntegrationType in c.available_integrations.items():
66 %for integration_key, IntegrationType in c.available_integrations.items():
67 % if not IntegrationType.is_dummy:
67 % if not IntegrationType.is_dummy:
68 <%
68 <%
69 if c.repo:
69 if c.repo:
70 list_url = request.route_path('repo_integrations_list',
70 list_url = request.route_path('repo_integrations_list',
71 repo_name=c.repo.repo_name,
71 repo_name=c.repo.repo_name,
72 integration=integration_key)
72 integration=integration_key)
73 elif c.repo_group:
73 elif c.repo_group:
74 list_url = request.route_path('repo_group_integrations_list',
74 list_url = request.route_path('repo_group_integrations_list',
75 repo_group_name=c.repo_group.group_name,
75 repo_group_name=c.repo_group.group_name,
76 integration=integration_key)
76 integration=integration_key)
77 else:
77 else:
78 list_url = request.route_path('global_integrations_list',
78 list_url = request.route_path('global_integrations_list',
79 integration=integration_key)
79 integration=integration_key)
80 %>
80 %>
81 <a href="${list_url}"
81 <a href="${list_url}"
82 class="btn ${c.current_IntegrationType and integration_key == c.current_IntegrationType.key and 'btn-primary' or ''}">
82 class="btn ${c.current_IntegrationType and integration_key == c.current_IntegrationType.key and 'btn-primary' or ''}">
83 ${IntegrationType.display_name}
83 ${IntegrationType.display_name}
84 </a>
84 </a>
85 % endif
85 % endif
86 %endfor
86 %endfor
87
87
88 <%
88 <%
89 integration_type = c.current_IntegrationType and c.current_IntegrationType.display_name or ''
89 integration_type = c.current_IntegrationType and c.current_IntegrationType.display_name or ''
90
90
91 if c.repo:
91 if c.repo:
92 create_url = h.route_path('repo_integrations_new', repo_name=c.repo.repo_name)
92 create_url = h.route_path('repo_integrations_new', repo_name=c.repo.repo_name)
93 elif c.repo_group:
93 elif c.repo_group:
94 create_url = h.route_path('repo_group_integrations_new', repo_group_name=c.repo_group.group_name)
94 create_url = h.route_path('repo_group_integrations_new', repo_group_name=c.repo_group.group_name)
95 else:
95 else:
96 create_url = h.route_path('global_integrations_new')
96 create_url = h.route_path('global_integrations_new')
97 %>
97 %>
98 <p class="pull-right">
98 <p class="pull-right">
99 <a href="${create_url}" class="btn btn-small btn-success">${_(u'Create new integration')}</a>
99 <a href="${create_url}" class="btn btn-small btn-success">${_(u'Create new integration')}</a>
100 </p>
100 </p>
101
101
102 <table class="rctable integrations">
102 <table class="rctable integrations">
103 <thead>
103 <thead>
104 <tr>
104 <tr>
105 <th><a href="?sort=enabled:${c.rev_sort_dir}">${_('Enabled')}</a></th>
105 <th><a href="?sort=enabled:${c.rev_sort_dir}">${_('Enabled')}</a></th>
106 <th><a href="?sort=name:${c.rev_sort_dir}">${_('Name')}</a></th>
106 <th><a href="?sort=name:${c.rev_sort_dir}">${_('Name')}</a></th>
107 <th colspan="2"><a href="?sort=integration_type:${c.rev_sort_dir}">${_('Type')}</a></th>
107 <th colspan="2"><a href="?sort=integration_type:${c.rev_sort_dir}">${_('Type')}</a></th>
108 <th><a href="?sort=scope:${c.rev_sort_dir}">${_('Scope')}</a></th>
108 <th><a href="?sort=scope:${c.rev_sort_dir}">${_('Scope')}</a></th>
109 <th>${_('Actions')}</th>
109 <th>${_('Actions')}</th>
110 <th></th>
110 <th></th>
111 </tr>
111 </tr>
112 </thead>
112 </thead>
113 <tbody>
113 <tbody>
114 %if not c.integrations_list:
114 %if not c.integrations_list:
115 <tr>
115 <tr>
116 <td colspan="7">
116 <td colspan="7">
117
117
118 %if c.repo:
118 %if c.repo:
119 ${_('No {type} integrations for repo {repo} exist yet.').format(type=integration_type, repo=c.repo.repo_name)}
119 ${_('No {type} integrations for repo {repo} exist yet.').format(type=integration_type, repo=c.repo.repo_name)}
120 %elif c.repo_group:
120 %elif c.repo_group:
121 ${_('No {type} integrations for repogroup {repogroup} exist yet.').format(type=integration_type, repogroup=c.repo_group.group_name)}
121 ${_('No {type} integrations for repogroup {repogroup} exist yet.').format(type=integration_type, repogroup=c.repo_group.group_name)}
122 %else:
122 %else:
123 ${_('No {type} integrations exist yet.').format(type=integration_type)}
123 ${_('No {type} integrations exist yet.').format(type=integration_type)}
124 %endif
124 %endif
125
125
126 %if c.current_IntegrationType:
126 %if c.current_IntegrationType:
127 <%
127 <%
128 if c.repo:
128 if c.repo:
129 create_url = h.route_path('repo_integrations_create', repo_name=c.repo.repo_name, integration=c.current_IntegrationType.key)
129 create_url = h.route_path('repo_integrations_create', repo_name=c.repo.repo_name, integration=c.current_IntegrationType.key)
130 elif c.repo_group:
130 elif c.repo_group:
131 create_url = h.route_path('repo_group_integrations_create', repo_group_name=c.repo_group.group_name, integration=c.current_IntegrationType.key)
131 create_url = h.route_path('repo_group_integrations_create', repo_group_name=c.repo_group.group_name, integration=c.current_IntegrationType.key)
132 else:
132 else:
133 create_url = h.route_path('global_integrations_create', integration=c.current_IntegrationType.key)
133 create_url = h.route_path('global_integrations_create', integration=c.current_IntegrationType.key)
134 %>
134 %>
135 %endif
135 %endif
136
136
137 <a href="${create_url}">${_(u'Create one')}</a>
137 <a href="${create_url}">${_(u'Create one')}</a>
138 </td>
138 </td>
139 </tr>
139 </tr>
140 %endif
140 %endif
141 %for IntegrationType, integration in c.integrations_list:
141 %for IntegrationType, integration in c.integrations_list:
142 <tr id="integration_${integration.integration_id}">
142 <tr id="integration_${integration.integration_id}">
143 <td class="td-enabled">
143 <td class="td-enabled">
144 %if integration.enabled:
144 %if integration.enabled:
145 <div class="flag_status approved pull-left"></div>
145 <div class="flag_status approved pull-left"></div>
146 %else:
146 %else:
147 <div class="flag_status rejected pull-left"></div>
147 <div class="flag_status rejected pull-left"></div>
148 %endif
148 %endif
149 </td>
149 </td>
150 <td class="td-description">
150 <td class="td-description">
151 ${integration.name}
151 ${integration.name}
152 </td>
152 </td>
153 <td class="td-icon">
153 <td class="td-icon">
154 %if integration.integration_type in c.available_integrations:
154 %if integration.integration_type in c.available_integrations:
155 <div class="integration-icon">
155 <div class="integration-icon">
156 ${c.available_integrations[integration.integration_type].icon|n}
156 ${c.available_integrations[integration.integration_type].icon|n}
157 </div>
157 </div>
158 %else:
158 %else:
159 ?
159 ?
160 %endif
160 %endif
161 </td>
161 </td>
162 <td class="td-type">
162 <td class="td-type">
163 ${integration.integration_type}
163 ${integration.integration_type}
164 </td>
164 </td>
165 <td class="td-scope">
165 <td class="td-scope">
166 %if integration.repo:
166 %if integration.repo:
167 <a href="${h.route_path('repo_summary', repo_name=integration.repo.repo_name)}">
167 <a href="${h.route_path('repo_summary', repo_name=integration.repo.repo_name)}">
168 ${_('repo')}:${integration.repo.repo_name}
168 ${_('repo')}:${integration.repo.repo_name}
169 </a>
169 </a>
170 %elif integration.repo_group:
170 %elif integration.repo_group:
171 <a href="${h.route_path('repo_group_home', repo_group_name=integration.repo_group.group_name)}">
171 <a href="${h.route_path('repo_group_home', repo_group_name=integration.repo_group.group_name)}">
172 ${_('repogroup')}:${integration.repo_group.group_name}
172 ${_('repogroup')}:${integration.repo_group.group_name}
173 %if integration.child_repos_only:
173 %if integration.child_repos_only:
174 ${_('child repos only')}
174 ${_('child repos only')}
175 %else:
175 %else:
176 ${_('cascade to all')}
176 ${_('cascade to all')}
177 %endif
177 %endif
178 </a>
178 </a>
179 %else:
179 %else:
180 %if integration.child_repos_only:
180 %if integration.child_repos_only:
181 ${_('top level repos only')}
181 ${_('top level repos only')}
182 %else:
182 %else:
183 ${_('global')}
183 ${_('global')}
184 %endif
184 %endif
185 </td>
185 </td>
186 %endif
186 %endif
187 <td class="td-action">
187 <td class="td-action">
188 %if not IntegrationType:
188 %if not IntegrationType:
189 ${_('unknown integration')}
189 ${_('unknown integration')}
190 %else:
190 %else:
191 <%
191 <%
192 if c.repo:
192 if c.repo:
193 edit_url = request.route_path('repo_integrations_edit',
193 edit_url = request.route_path('repo_integrations_edit',
194 repo_name=c.repo.repo_name,
194 repo_name=c.repo.repo_name,
195 integration=integration.integration_type,
195 integration=integration.integration_type,
196 integration_id=integration.integration_id)
196 integration_id=integration.integration_id)
197 elif c.repo_group:
197 elif c.repo_group:
198 edit_url = request.route_path('repo_group_integrations_edit',
198 edit_url = request.route_path('repo_group_integrations_edit',
199 repo_group_name=c.repo_group.group_name,
199 repo_group_name=c.repo_group.group_name,
200 integration=integration.integration_type,
200 integration=integration.integration_type,
201 integration_id=integration.integration_id)
201 integration_id=integration.integration_id)
202 else:
202 else:
203 edit_url = request.route_path('global_integrations_edit',
203 edit_url = request.route_path('global_integrations_edit',
204 integration=integration.integration_type,
204 integration=integration.integration_type,
205 integration_id=integration.integration_id)
205 integration_id=integration.integration_id)
206 %>
206 %>
207 <div class="grid_edit">
207 <div class="grid_edit">
208 <a href="${edit_url}">${_('Edit')}</a>
208 <a href="${edit_url}">${_('Edit')}</a>
209 </div>
209 </div>
210 <div class="grid_delete">
210 <div class="grid_delete">
211 <a href="${edit_url}"
211 <a href="${edit_url}"
212 class="btn btn-link btn-danger delete_integration_entry"
212 class="btn btn-link btn-danger delete_integration_entry"
213 data-desc="${integration.name}"
213 data-desc="${integration.name}"
214 data-uid="${integration.integration_id}">
214 data-uid="${integration.integration_id}">
215 ${_('Delete')}
215 ${_('Delete')}
216 </a>
216 </a>
217 </div>
217 </div>
218 %endif
218 %endif
219 </td>
219 </td>
220 </tr>
220 </tr>
221 %endfor
221 %endfor
222 <tr id="last-row"></tr>
222 <tr id="last-row"></tr>
223 </tbody>
223 </tbody>
224 </table>
224 </table>
225 <div class="integrations-paginator">
225 <div class="integrations-paginator">
226 <div class="pagination-wh pagination-left">
226 <div class="pagination-wh pagination-left">
227 ${c.integrations_list.pager('$link_previous ~2~ $link_next')}
227 ${c.integrations_list.pager('$link_previous ~2~ $link_next')}
228 </div>
228 </div>
229 </div>
229 </div>
230 </div>
230 </div>
231 </div>
231 </div>
232 <script type="text/javascript">
232 <script type="text/javascript">
233 var delete_integration = function(entry) {
233 var delete_integration = function(entry) {
234 if (confirm("Confirm to remove this integration: "+$(entry).data('desc'))) {
234 if (confirm("Confirm to remove this integration: "+$(entry).data('desc'))) {
235 var request = $.ajax({
235 var request = $.ajax({
236 type: "POST",
236 type: "POST",
237 url: $(entry).attr('href'),
237 url: $(entry).attr('href'),
238 data: {
238 data: {
239 'delete': 'delete',
239 'delete': 'delete',
240 'csrf_token': CSRF_TOKEN
240 'csrf_token': CSRF_TOKEN
241 },
241 },
242 success: function(){
242 success: function(){
243 location.reload();
243 location.reload();
244 },
244 },
245 error: function(data, textStatus, errorThrown){
245 error: function(data, textStatus, errorThrown){
246 alert("Error while deleting entry.\nError code {0} ({1}). URL: {2}".format(data.status,data.statusText,$(entry)[0].url));
246 alert("Error while deleting entry.\nError code {0} ({1}). URL: {2}".format(data.status,data.statusText,$(entry)[0].url));
247 }
247 }
248 });
248 });
249 };
249 };
250 };
250 };
251
251
252 $('.delete_integration_entry').on('click', function(e){
252 $('.delete_integration_entry').on('click', function(e){
253 e.preventDefault();
253 e.preventDefault();
254 delete_integration(this);
254 delete_integration(this);
255 });
255 });
256 </script> No newline at end of file
256 </script>
@@ -1,68 +1,68 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="base.mako"/>
2 <%inherit file="base.mako"/>
3 <%namespace name="widgets" file="/widgets.mako"/>
3 <%namespace name="widgets" file="/widgets.mako"/>
4
4
5 <%def name="breadcrumbs_links()">
5 <%def name="breadcrumbs_links()">
6 %if c.repo:
6 %if c.repo:
7 ${h.link_to('Settings',h.route_path('edit_repo', repo_name=c.repo.repo_name))}
7 ${h.link_to('Settings',h.route_path('edit_repo', repo_name=c.repo.repo_name))}
8 &raquo;
8 &raquo;
9 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_integrations_home', repo_name=c.repo.repo_name))}
9 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_integrations_home', repo_name=c.repo.repo_name))}
10 %elif c.repo_group:
10 %elif c.repo_group:
11 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
11 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
12 &raquo;
12 &raquo;
13 ${h.link_to(_('Repository Groups'),h.route_path('repo_groups'))}
13 ${h.link_to(_('Repository Groups'),h.route_path('repo_groups'))}
14 &raquo;
14 &raquo;
15 ${h.link_to(c.repo_group.group_name,h.route_path('edit_repo_group', repo_group_name=c.repo_group.group_name))}
15 ${h.link_to(c.repo_group.group_name,h.route_path('edit_repo_group', repo_group_name=c.repo_group.group_name))}
16 &raquo;
16 &raquo;
17 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_group_integrations_home', repo_group_name=c.repo_group.group_name))}
17 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_group_integrations_home', repo_group_name=c.repo_group.group_name))}
18 %else:
18 %else:
19 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
19 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
20 &raquo;
20 &raquo;
21 ${h.link_to(_('Settings'),h.url('admin_settings'))}
21 ${h.link_to(_('Settings'),h.route_path('admin_settings'))}
22 &raquo;
22 &raquo;
23 ${h.link_to(_('Integrations'),request.route_url(route_name='global_integrations_home'))}
23 ${h.link_to(_('Integrations'),request.route_url(route_name='global_integrations_home'))}
24 %endif
24 %endif
25 &raquo;
25 &raquo;
26 ${_('Create new integration')}
26 ${_('Create new integration')}
27 </%def>
27 </%def>
28 <%widgets:panel class_='integrations'>
28 <%widgets:panel class_='integrations'>
29 <%def name="title()">
29 <%def name="title()">
30 %if c.repo:
30 %if c.repo:
31 ${_('Create New Integration for repository: {repo_name}').format(repo_name=c.repo.repo_name)}
31 ${_('Create New Integration for repository: {repo_name}').format(repo_name=c.repo.repo_name)}
32 %elif c.repo_group:
32 %elif c.repo_group:
33 ${_('Create New Integration for repository group: {repo_group_name}').format(repo_group_name=c.repo_group.group_name)}
33 ${_('Create New Integration for repository group: {repo_group_name}').format(repo_group_name=c.repo_group.group_name)}
34 %else:
34 %else:
35 ${_('Create New Global Integration')}
35 ${_('Create New Global Integration')}
36 %endif
36 %endif
37 </%def>
37 </%def>
38
38
39 %for integration, IntegrationObject in c.available_integrations.items():
39 %for integration, IntegrationObject in c.available_integrations.items():
40 <%
40 <%
41 if c.repo:
41 if c.repo:
42 create_url = request.route_path('repo_integrations_create',
42 create_url = request.route_path('repo_integrations_create',
43 repo_name=c.repo.repo_name,
43 repo_name=c.repo.repo_name,
44 integration=integration)
44 integration=integration)
45 elif c.repo_group:
45 elif c.repo_group:
46 create_url = request.route_path('repo_group_integrations_create',
46 create_url = request.route_path('repo_group_integrations_create',
47 repo_group_name=c.repo_group.group_name,
47 repo_group_name=c.repo_group.group_name,
48 integration=integration)
48 integration=integration)
49 else:
49 else:
50 create_url = request.route_path('global_integrations_create',
50 create_url = request.route_path('global_integrations_create',
51 integration=integration)
51 integration=integration)
52 if IntegrationObject.is_dummy:
52 if IntegrationObject.is_dummy:
53 create_url = request.current_route_path()
53 create_url = request.current_route_path()
54 %>
54 %>
55 <a href="${create_url}" class="integration-box ${'dummy-integration' if IntegrationObject.is_dummy else ''}">
55 <a href="${create_url}" class="integration-box ${'dummy-integration' if IntegrationObject.is_dummy else ''}">
56 <%widgets:panel>
56 <%widgets:panel>
57 <h2>
57 <h2>
58 <div class="integration-icon">
58 <div class="integration-icon">
59 ${IntegrationObject.icon|n}
59 ${IntegrationObject.icon|n}
60 </div>
60 </div>
61 ${IntegrationObject.display_name}
61 ${IntegrationObject.display_name}
62 </h2>
62 </h2>
63 ${IntegrationObject.description or _('No description available')}
63 ${IntegrationObject.description or _('No description available')}
64 </%widgets:panel>
64 </%widgets:panel>
65 </a>
65 </a>
66 %endfor
66 %endfor
67 <div style="clear:both"></div>
67 <div style="clear:both"></div>
68 </%widgets:panel>
68 </%widgets:panel>
@@ -1,164 +1,164 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2
2
3 ${h.secure_form(h.route_path('repo_create'), request=request)}
3 ${h.secure_form(h.route_path('repo_create'), request=request)}
4 <div class="form">
4 <div class="form">
5 <!-- fields -->
5 <!-- fields -->
6 <div class="fields">
6 <div class="fields">
7 <div class="field">
7 <div class="field">
8 <div class="label">
8 <div class="label">
9 <label for="repo_name">${_('Name')}:</label>
9 <label for="repo_name">${_('Name')}:</label>
10 </div>
10 </div>
11 <div class="input">
11 <div class="input">
12 ${h.text('repo_name', class_="medium")}
12 ${h.text('repo_name', class_="medium")}
13 <div class="info-block">
13 <div class="info-block">
14 <a id="remote_clone_toggle" href="#"><i class="icon-download-alt"></i> ${_('Import Existing Repository ?')}</a>
14 <a id="remote_clone_toggle" href="#"><i class="icon-download-alt"></i> ${_('Import Existing Repository ?')}</a>
15 </div>
15 </div>
16 %if not c.rhodecode_user.is_admin:
16 %if not c.rhodecode_user.is_admin:
17 ${h.hidden('user_created',True)}
17 ${h.hidden('user_created',True)}
18 %endif
18 %endif
19 </div>
19 </div>
20 </div>
20 </div>
21 <div id="remote_clone" class="field" style="display: none;">
21 <div id="remote_clone" class="field" style="display: none;">
22 <div class="label">
22 <div class="label">
23 <label for="clone_uri">${_('Clone from')}:</label>
23 <label for="clone_uri">${_('Clone from')}:</label>
24 </div>
24 </div>
25 <div class="input">
25 <div class="input">
26 ${h.text('clone_uri', class_="medium")}
26 ${h.text('clone_uri', class_="medium")}
27 <span class="help-block">
27 <span class="help-block">
28 <pre>
28 <pre>
29 - The repository must be accessible over http:// or https://
29 - The repository must be accessible over http:// or https://
30 - For Git projects it's recommended appending .git to the end of clone url.
30 - For Git projects it's recommended appending .git to the end of clone url.
31 - Make sure to select proper repository type from the below selector before importing it.
31 - Make sure to select proper repository type from the below selector before importing it.
32 - If your HTTP[S] repository is not publicly accessible,
32 - If your HTTP[S] repository is not publicly accessible,
33 add authentication information to the URL: https://username:password@server.company.com/repo-name.
33 add authentication information to the URL: https://username:password@server.company.com/repo-name.
34 - The Git LFS/Mercurial Largefiles objects will not be imported.
34 - The Git LFS/Mercurial Largefiles objects will not be imported.
35 - For very large repositories, it's recommended to manually copy them into the
35 - For very large repositories, it's recommended to manually copy them into the
36 RhodeCode <a href="${h.url('admin_settings_vcs', anchor='vcs-storage-options')}">storage location</a> and run <a href="${h.url('admin_settings_mapping')}">Remap and Rescan</a>.
36 RhodeCode <a href="${h.route_path('admin_settings_vcs', _anchor='vcs-storage-options')}">storage location</a> and run <a href="${h.route_path('admin_settings_mapping')}">Remap and Rescan</a>.
37 </pre>
37 </pre>
38 </span>
38 </span>
39 </div>
39 </div>
40 </div>
40 </div>
41 <div class="field">
41 <div class="field">
42 <div class="label">
42 <div class="label">
43 <label for="repo_description">${_('Description')}:</label>
43 <label for="repo_description">${_('Description')}:</label>
44 </div>
44 </div>
45 <div class="textarea editor">
45 <div class="textarea editor">
46 ${h.textarea('repo_description')}
46 ${h.textarea('repo_description')}
47 <% metatags_url = h.literal('''<a href="#metatagsShow" onclick="$('#meta-tags-desc').toggle();return false">meta-tags</a>''') %>
47 <% metatags_url = h.literal('''<a href="#metatagsShow" onclick="$('#meta-tags-desc').toggle();return false">meta-tags</a>''') %>
48 <span class="help-block">${_('Plain text format with support of {metatags}. Add a README file for longer descriptions').format(metatags=metatags_url)|n}</span>
48 <span class="help-block">${_('Plain text format with support of {metatags}. Add a README file for longer descriptions').format(metatags=metatags_url)|n}</span>
49 <span id="meta-tags-desc" style="display: none">
49 <span id="meta-tags-desc" style="display: none">
50 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
50 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
51 ${dt.metatags_help()}
51 ${dt.metatags_help()}
52 </span>
52 </span>
53 </div>
53 </div>
54 </div>
54 </div>
55 <div class="field">
55 <div class="field">
56 <div class="label">
56 <div class="label">
57 <label for="repo_group">${_('Repository Group')}:</label>
57 <label for="repo_group">${_('Repository Group')}:</label>
58 </div>
58 </div>
59 <div class="select">
59 <div class="select">
60 ${h.select('repo_group',request.GET.get('parent_group'),c.repo_groups,class_="medium")}
60 ${h.select('repo_group',request.GET.get('parent_group'),c.repo_groups,class_="medium")}
61 % if c.personal_repo_group:
61 % if c.personal_repo_group:
62 <a class="btn" href="#" id="select_my_group" data-personal-group-id="${c.personal_repo_group.group_id}">
62 <a class="btn" href="#" id="select_my_group" data-personal-group-id="${c.personal_repo_group.group_id}">
63 ${_('Select my personal group (%(repo_group_name)s)') % {'repo_group_name': c.personal_repo_group.group_name}}
63 ${_('Select my personal group (%(repo_group_name)s)') % {'repo_group_name': c.personal_repo_group.group_name}}
64 </a>
64 </a>
65 % endif
65 % endif
66 <span class="help-block">${_('Optionally select a group to put this repository into.')}</span>
66 <span class="help-block">${_('Optionally select a group to put this repository into.')}</span>
67 </div>
67 </div>
68 </div>
68 </div>
69 <div id="copy_perms" class="field">
69 <div id="copy_perms" class="field">
70 <div class="label label-checkbox">
70 <div class="label label-checkbox">
71 <label for="repo_copy_permissions">${_('Copy Parent Group Permissions')}:</label>
71 <label for="repo_copy_permissions">${_('Copy Parent Group Permissions')}:</label>
72 </div>
72 </div>
73 <div class="checkboxes">
73 <div class="checkboxes">
74 ${h.checkbox('repo_copy_permissions', value="True", checked="checked")}
74 ${h.checkbox('repo_copy_permissions', value="True", checked="checked")}
75 <span class="help-block">${_('Copy permission set from the parent repository group.')}</span>
75 <span class="help-block">${_('Copy permission set from the parent repository group.')}</span>
76 </div>
76 </div>
77 </div>
77 </div>
78 <div class="field">
78 <div class="field">
79 <div class="label">
79 <div class="label">
80 <label for="repo_type">${_('Type')}:</label>
80 <label for="repo_type">${_('Type')}:</label>
81 </div>
81 </div>
82 <div class="select">
82 <div class="select">
83 ${h.select('repo_type','hg',c.backends)}
83 ${h.select('repo_type','hg',c.backends)}
84 <span class="help-block">${_('Set the type of repository to create.')}</span>
84 <span class="help-block">${_('Set the type of repository to create.')}</span>
85 </div>
85 </div>
86 </div>
86 </div>
87 <div class="field">
87 <div class="field">
88 <div class="label">
88 <div class="label">
89 <label for="repo_landing_rev">${_('Landing commit')}:</label>
89 <label for="repo_landing_rev">${_('Landing commit')}:</label>
90 </div>
90 </div>
91 <div class="select">
91 <div class="select">
92 ${h.select('repo_landing_rev','',c.landing_revs,class_="medium")}
92 ${h.select('repo_landing_rev','',c.landing_revs,class_="medium")}
93 <span class="help-block">${_('The default commit for file pages, downloads, full text search index, and README generation.')}</span>
93 <span class="help-block">${_('The default commit for file pages, downloads, full text search index, and README generation.')}</span>
94 </div>
94 </div>
95 </div>
95 </div>
96 <div class="field">
96 <div class="field">
97 <div class="label label-checkbox">
97 <div class="label label-checkbox">
98 <label for="repo_private">${_('Private Repository')}:</label>
98 <label for="repo_private">${_('Private Repository')}:</label>
99 </div>
99 </div>
100 <div class="checkboxes">
100 <div class="checkboxes">
101 ${h.checkbox('repo_private',value="True")}
101 ${h.checkbox('repo_private',value="True")}
102 <span class="help-block">${_('Private repositories are only visible to people explicitly added as collaborators.')}</span>
102 <span class="help-block">${_('Private repositories are only visible to people explicitly added as collaborators.')}</span>
103 </div>
103 </div>
104 </div>
104 </div>
105 <div class="buttons">
105 <div class="buttons">
106 ${h.submit('save',_('Save'),class_="btn")}
106 ${h.submit('save',_('Save'),class_="btn")}
107 </div>
107 </div>
108 </div>
108 </div>
109 </div>
109 </div>
110 <script>
110 <script>
111 $(document).ready(function(){
111 $(document).ready(function(){
112 var setCopyPermsOption = function(group_val){
112 var setCopyPermsOption = function(group_val){
113 if(group_val != "-1"){
113 if(group_val != "-1"){
114 $('#copy_perms').show()
114 $('#copy_perms').show()
115 }
115 }
116 else{
116 else{
117 $('#copy_perms').hide();
117 $('#copy_perms').hide();
118 }
118 }
119 };
119 };
120
120
121 $('#remote_clone_toggle').on('click', function(e){
121 $('#remote_clone_toggle').on('click', function(e){
122 $('#remote_clone').show();
122 $('#remote_clone').show();
123 e.preventDefault();
123 e.preventDefault();
124 });
124 });
125
125
126 if($('#remote_clone input').hasClass('error')){
126 if($('#remote_clone input').hasClass('error')){
127 $('#remote_clone').show();
127 $('#remote_clone').show();
128 }
128 }
129 if($('#remote_clone input').val()){
129 if($('#remote_clone input').val()){
130 $('#remote_clone').show();
130 $('#remote_clone').show();
131 }
131 }
132
132
133 $("#repo_group").select2({
133 $("#repo_group").select2({
134 'containerCssClass': "drop-menu",
134 'containerCssClass': "drop-menu",
135 'dropdownCssClass': "drop-menu-dropdown",
135 'dropdownCssClass': "drop-menu-dropdown",
136 'dropdownAutoWidth': true,
136 'dropdownAutoWidth': true,
137 'width': "resolve"
137 'width': "resolve"
138 });
138 });
139
139
140 setCopyPermsOption($('#repo_group').val());
140 setCopyPermsOption($('#repo_group').val());
141 $("#repo_group").on("change", function(e) {
141 $("#repo_group").on("change", function(e) {
142 setCopyPermsOption(e.val)
142 setCopyPermsOption(e.val)
143 });
143 });
144
144
145 $("#repo_type").select2({
145 $("#repo_type").select2({
146 'containerCssClass': "drop-menu",
146 'containerCssClass': "drop-menu",
147 'dropdownCssClass': "drop-menu-dropdown",
147 'dropdownCssClass': "drop-menu-dropdown",
148 'minimumResultsForSearch': -1,
148 'minimumResultsForSearch': -1,
149 });
149 });
150 $("#repo_landing_rev").select2({
150 $("#repo_landing_rev").select2({
151 'containerCssClass': "drop-menu",
151 'containerCssClass': "drop-menu",
152 'dropdownCssClass': "drop-menu-dropdown",
152 'dropdownCssClass': "drop-menu-dropdown",
153 'minimumResultsForSearch': -1,
153 'minimumResultsForSearch': -1,
154 });
154 });
155 $('#repo_name').focus();
155 $('#repo_name').focus();
156
156
157 $('#select_my_group').on('click', function(e){
157 $('#select_my_group').on('click', function(e){
158 e.preventDefault();
158 e.preventDefault();
159 $("#repo_group").val($(this).data('personalGroupId')).trigger("change");
159 $("#repo_group").val($(this).data('personalGroupId')).trigger("change");
160 })
160 })
161
161
162 })
162 })
163 </script>
163 </script>
164 ${h.end_form()}
164 ${h.end_form()}
@@ -1,56 +1,56 b''
1 <div class="panel panel-default">
1 <div class="panel panel-default">
2 <div class="panel-heading">
2 <div class="panel-heading">
3 <h3 class="panel-title">${_('Email Configuration')}</h3>
3 <h3 class="panel-title">${_('Email Configuration')}</h3>
4 </div>
4 </div>
5 <div class="panel-body">
5 <div class="panel-body">
6 <%
6 <%
7 elems = [
7 elems = [
8 (_('Email prefix'), c.rhodecode_ini.get('email_prefix'), ''),
8 (_('Email prefix'), c.rhodecode_ini.get('email_prefix'), ''),
9 (_('RhodeCode email from'), c.rhodecode_ini.get('app_email_from'), ''),
9 (_('RhodeCode email from'), c.rhodecode_ini.get('app_email_from'), ''),
10 (_('Error email from'), c.rhodecode_ini.get('error_email_from'), ''),
10 (_('Error email from'), c.rhodecode_ini.get('error_email_from'), ''),
11 (_('Error email recipients'), c.rhodecode_ini.get('email_to'), ''),
11 (_('Error email recipients'), c.rhodecode_ini.get('email_to'), ''),
12
12
13 (_('SMTP server'), c.rhodecode_ini.get('smtp_server'), ''),
13 (_('SMTP server'), c.rhodecode_ini.get('smtp_server'), ''),
14 (_('SMTP username'), c.rhodecode_ini.get('smtp_username'), ''),
14 (_('SMTP username'), c.rhodecode_ini.get('smtp_username'), ''),
15 (_('SMTP password'), '%s chars' % len(c.rhodecode_ini.get('smtp_password', '')), ''),
15 (_('SMTP password'), '%s chars' % len(c.rhodecode_ini.get('smtp_password', '')), ''),
16 (_('SMTP port'), c.rhodecode_ini.get('smtp_port'), ''),
16 (_('SMTP port'), c.rhodecode_ini.get('smtp_port'), ''),
17
17
18 (_('SMTP use TLS'), c.rhodecode_ini.get('smtp_use_tls'), ''),
18 (_('SMTP use TLS'), c.rhodecode_ini.get('smtp_use_tls'), ''),
19 (_('SMTP use SSL'), c.rhodecode_ini.get('smtp_use_ssl'), ''),
19 (_('SMTP use SSL'), c.rhodecode_ini.get('smtp_use_ssl'), ''),
20 (_('SMTP auth'), c.rhodecode_ini.get('smtp_auth'), ''),
20 (_('SMTP auth'), c.rhodecode_ini.get('smtp_auth'), ''),
21 ]
21 ]
22 %>
22 %>
23 <dl class="dl-horizontal settings">
23 <dl class="dl-horizontal settings">
24 %for dt, dd, tt in elems:
24 %for dt, dd, tt in elems:
25 <dt >${dt}:</dt>
25 <dt >${dt}:</dt>
26 <dd title="${h.tooltip(tt)}">${dd}</dd>
26 <dd title="${h.tooltip(tt)}">${dd}</dd>
27 %endfor
27 %endfor
28 </dl>
28 </dl>
29 </div>
29 </div>
30 </div>
30 </div>
31
31
32 <div class="panel panel-default">
32 <div class="panel panel-default">
33 <div class="panel-heading">
33 <div class="panel-heading">
34 <h3 class="panel-title">${_('Test Email')}</h3>
34 <h3 class="panel-title">${_('Test Email')}</h3>
35 </div>
35 </div>
36 <div class="panel-body">
36 <div class="panel-body">
37 ${h.secure_form(h.url('admin_settings_email'), request=request)}
37 ${h.secure_form(h.route_path('admin_settings_email_update'), request=request)}
38
38
39 <div class="field input">
39 <div class="field input">
40 ${h.text('test_email', size=60, placeholder=_('enter valid email'))}
40 ${h.text('test_email', size=60, placeholder=_('enter valid email'))}
41 </div>
41 </div>
42 <div class="field">
42 <div class="field">
43 <span class="help-block">
43 <span class="help-block">
44 ${_('Send an auto-generated email from this server to above email...')}
44 ${_('Send an auto-generated email from this server to above email...')}
45 </span>
45 </span>
46 </div>
46 </div>
47 <div class="buttons">
47 <div class="buttons">
48 ${h.submit('send',_('Send'),class_="btn")}
48 ${h.submit('send',_('Send'),class_="btn")}
49 </div>
49 </div>
50 ${h.end_form()}
50 ${h.end_form()}
51 </div>
51 </div>
52 </div>
52 </div>
53
53
54
54
55
55
56
56
@@ -1,295 +1,295 b''
1 ${h.secure_form(h.url('admin_settings_global'), request=request)}
1 ${h.secure_form(h.route_path('admin_settings_global_update'), request=request)}
2
2
3 <div class="panel panel-default">
3 <div class="panel panel-default">
4 <div class="panel-heading" id="branding-options">
4 <div class="panel-heading" id="branding-options">
5 <h3 class="panel-title">${_('Branding')} <a class="permalink" href="#branding-options"></a></h3>
5 <h3 class="panel-title">${_('Branding')} <a class="permalink" href="#branding-options"></a></h3>
6 </div>
6 </div>
7 <div class="panel-body">
7 <div class="panel-body">
8 <div class="label">
8 <div class="label">
9 <label for="rhodecode_title">${_('Title')}</label>
9 <label for="rhodecode_title">${_('Title')}</label>
10 </div>
10 </div>
11 <div class="field input">
11 <div class="field input">
12 ${h.text('rhodecode_title',size=60)}
12 ${h.text('rhodecode_title',size=60)}
13 </div>
13 </div>
14 <div class="field">
14 <div class="field">
15 <span class="help-block">
15 <span class="help-block">
16 ${_('Set a custom title for your RhodeCode instance (limited to 40 characters).')}
16 ${_('Set a custom title for your RhodeCode instance (limited to 40 characters).')}
17 </span>
17 </span>
18 </div>
18 </div>
19 <div class="label">
19 <div class="label">
20 <label for="rhodecode_realm">${_('HTTP[S] authentication realm')}</label>
20 <label for="rhodecode_realm">${_('HTTP[S] authentication realm')}</label>
21 </div>
21 </div>
22 <div class="field input">
22 <div class="field input">
23 ${h.text('rhodecode_realm',size=60)}
23 ${h.text('rhodecode_realm',size=60)}
24 </div>
24 </div>
25 <div class="field">
25 <div class="field">
26 <span class="help-block">
26 <span class="help-block">
27 ${_('Set a custom text that is shown as authentication message to clients trying to connect.')}
27 ${_('Set a custom text that is shown as authentication message to clients trying to connect.')}
28 </span>
28 </span>
29 </div>
29 </div>
30 </div>
30 </div>
31 </div>
31 </div>
32
32
33
33
34 <div class="panel panel-default">
34 <div class="panel panel-default">
35 <div class="panel-heading" id="personal-group-options">
35 <div class="panel-heading" id="personal-group-options">
36 <h3 class="panel-title">${_('Personal Repository Group')} <a class="permalink" href="#personal-group-options"></a></h3>
36 <h3 class="panel-title">${_('Personal Repository Group')} <a class="permalink" href="#personal-group-options"></a></h3>
37 </div>
37 </div>
38 <div class="panel-body">
38 <div class="panel-body">
39 <div class="checkbox">
39 <div class="checkbox">
40 ${h.checkbox('rhodecode_create_personal_repo_group','True')}
40 ${h.checkbox('rhodecode_create_personal_repo_group','True')}
41 <label for="rhodecode_create_personal_repo_group">${_('Create Personal Repository Group')}</label>
41 <label for="rhodecode_create_personal_repo_group">${_('Create Personal Repository Group')}</label>
42 </div>
42 </div>
43 <span class="help-block">
43 <span class="help-block">
44 ${_('Always create Personal Repository Groups for new users.')} <br/>
44 ${_('Always create Personal Repository Groups for new users.')} <br/>
45 ${_('When creating new users from add user form or API you can still turn this off via a checkbox or flag')}
45 ${_('When creating new users from add user form or API you can still turn this off via a checkbox or flag')}
46 </span>
46 </span>
47
47
48 <div class="label">
48 <div class="label">
49 <label for="rhodecode_personal_repo_group_pattern">${_('Personal Repo Group Pattern')}</label>
49 <label for="rhodecode_personal_repo_group_pattern">${_('Personal Repo Group Pattern')}</label>
50 </div>
50 </div>
51 <div class="field input">
51 <div class="field input">
52 ${h.text('rhodecode_personal_repo_group_pattern',size=60, placeholder=c.personal_repo_group_default_pattern)}
52 ${h.text('rhodecode_personal_repo_group_pattern',size=60, placeholder=c.personal_repo_group_default_pattern)}
53 </div>
53 </div>
54 <span class="help-block">
54 <span class="help-block">
55 ${_('Pattern used to create Personal Repository Groups. Prefix can be other existing repository group path[s], eg. /u/${username}')} <br/>
55 ${_('Pattern used to create Personal Repository Groups. Prefix can be other existing repository group path[s], eg. /u/${username}')} <br/>
56 ${_('Available variables are currently ${username} and ${user_id}')}
56 ${_('Available variables are currently ${username} and ${user_id}')}
57 </span>
57 </span>
58 </div>
58 </div>
59 </div>
59 </div>
60
60
61
61
62 <div class="panel panel-default">
62 <div class="panel panel-default">
63 <div class="panel-heading" id="captcha-options">
63 <div class="panel-heading" id="captcha-options">
64 <h3 class="panel-title">${_('Registration Captcha')} <a class="permalink" href="#captcha-options"></a></h3>
64 <h3 class="panel-title">${_('Registration Captcha')} <a class="permalink" href="#captcha-options"></a></h3>
65 </div>
65 </div>
66 <div class="panel-body">
66 <div class="panel-body">
67 <div class="label">
67 <div class="label">
68 <label for="rhodecode_captcha_public_key">${_('Google ReCaptcha public key')}</label>
68 <label for="rhodecode_captcha_public_key">${_('Google ReCaptcha public key')}</label>
69 </div>
69 </div>
70 <div class="field input">
70 <div class="field input">
71 ${h.text('rhodecode_captcha_public_key',size=60)}
71 ${h.text('rhodecode_captcha_public_key',size=60)}
72 </div>
72 </div>
73 <div class="field">
73 <div class="field">
74 <span class="help-block">
74 <span class="help-block">
75 ${_('Public key for reCaptcha system.')}
75 ${_('Public key for reCaptcha system.')}
76 </span>
76 </span>
77 </div>
77 </div>
78
78
79 <div class="label">
79 <div class="label">
80 <label for="rhodecode_captcha_private_key">${_('Google ReCaptcha private key')}</label>
80 <label for="rhodecode_captcha_private_key">${_('Google ReCaptcha private key')}</label>
81 </div>
81 </div>
82 <div class="field input">
82 <div class="field input">
83 ${h.text('rhodecode_captcha_private_key',size=60)}
83 ${h.text('rhodecode_captcha_private_key',size=60)}
84 </div>
84 </div>
85 <div class="field">
85 <div class="field">
86 <span class="help-block">
86 <span class="help-block">
87 ${_('Private key for reCaptcha system. Setting this value will enable captcha on registration')}
87 ${_('Private key for reCaptcha system. Setting this value will enable captcha on registration')}
88 </span>
88 </span>
89 </div>
89 </div>
90 </div>
90 </div>
91 </div>
91 </div>
92
92
93 <div class="panel panel-default">
93 <div class="panel panel-default">
94 <div class="panel-heading" id="header-code-options">
94 <div class="panel-heading" id="header-code-options">
95 <h3 class="panel-title">${_('Custom Header Code')} <a class="permalink" href="#header-code-options"></a></h3>
95 <h3 class="panel-title">${_('Custom Header Code')} <a class="permalink" href="#header-code-options"></a></h3>
96 </div>
96 </div>
97 <div class="panel-body">
97 <div class="panel-body">
98 <div class="select">
98 <div class="select">
99 <select id="pre_template" >
99 <select id="pre_template" >
100 <option value="#">${_('Templates...')}</option>
100 <option value="#">${_('Templates...')}</option>
101 <option value="ga">Google Analytics</option>
101 <option value="ga">Google Analytics</option>
102 <option value="clicky">Clicky</option>
102 <option value="clicky">Clicky</option>
103 <option value="server_announce">${_('Server Announcement')}</option>
103 <option value="server_announce">${_('Server Announcement')}</option>
104 <option value="flash_filtering">${_('Flash message filtering')}</option>
104 <option value="flash_filtering">${_('Flash message filtering')}</option>
105 </select>
105 </select>
106 </div>
106 </div>
107 <div style="padding: 10px 0px"></div>
107 <div style="padding: 10px 0px"></div>
108 <div class="textarea text-area">
108 <div class="textarea text-area">
109 ${h.textarea('rhodecode_pre_code',cols=23,rows=5,class_="medium")}
109 ${h.textarea('rhodecode_pre_code',cols=23,rows=5,class_="medium")}
110 <span class="help-block">${_('Custom js/css code added at the end of the <header/> tag.')}
110 <span class="help-block">${_('Custom js/css code added at the end of the <header/> tag.')}
111 ${_('Use <script/> or <css/> tags to define custom styling or scripting')}</span>
111 ${_('Use <script/> or <css/> tags to define custom styling or scripting')}</span>
112 </div>
112 </div>
113 </div>
113 </div>
114 </div>
114 </div>
115
115
116 <div class="panel panel-default">
116 <div class="panel panel-default">
117 <div class="panel-heading" id="footer-code-options">
117 <div class="panel-heading" id="footer-code-options">
118 <h3 class="panel-title">${_('Custom Footer Code')} <a class="permalink" href="#footer-code-options"></a></h3>
118 <h3 class="panel-title">${_('Custom Footer Code')} <a class="permalink" href="#footer-code-options"></a></h3>
119 </div>
119 </div>
120 <div class="panel-body">
120 <div class="panel-body">
121 <div class="select">
121 <div class="select">
122 <select id="post_template" >
122 <select id="post_template" >
123 <option value="#">${_('Templates...')}</option>
123 <option value="#">${_('Templates...')}</option>
124 <option value="ga">Google Analytics</option>
124 <option value="ga">Google Analytics</option>
125 <option value="clicky">Clicky</option>
125 <option value="clicky">Clicky</option>
126 <option value="server_announce">${_('Server Announcement')}</option>
126 <option value="server_announce">${_('Server Announcement')}</option>
127 </select>
127 </select>
128 </div>
128 </div>
129 <div style="padding: 10px 0px"></div>
129 <div style="padding: 10px 0px"></div>
130 <div class="textarea text-area">
130 <div class="textarea text-area">
131 ${h.textarea('rhodecode_post_code',cols=23,rows=5, class_="medium")}
131 ${h.textarea('rhodecode_post_code',cols=23,rows=5, class_="medium")}
132 <span class="help-block">${_('Custom js/css code added at the end of the <body> tag.')}
132 <span class="help-block">${_('Custom js/css code added at the end of the <body> tag.')}
133 ${_('Use <script> or <css> tags to define custom styling or scripting')}</span>
133 ${_('Use <script> or <css> tags to define custom styling or scripting')}</span>
134 </div>
134 </div>
135 </div>
135 </div>
136 </div>
136 </div>
137
137
138 <div class="buttons">
138 <div class="buttons">
139 ${h.submit('save',_('Save settings'),class_="btn")}
139 ${h.submit('save',_('Save settings'),class_="btn")}
140 ${h.reset('reset',_('Reset'),class_="btn")}
140 ${h.reset('reset',_('Reset'),class_="btn")}
141 </div>
141 </div>
142 ${h.end_form()}
142 ${h.end_form()}
143
143
144
144
145
145
146 ## TEMPLATES ##
146 ## TEMPLATES ##
147 ###############
147 ###############
148
148
149 <script id="ga_tmpl" type="text/x-template">
149 <script id="ga_tmpl" type="text/x-template">
150 <%text filter="h">
150 <%text filter="h">
151 <script>
151 <script>
152 // Google Analytics
152 // Google Analytics
153 // Put your Google Analytics code instead of _GACODE_
153 // Put your Google Analytics code instead of _GACODE_
154 var _gaq_code = '_GACODE_';
154 var _gaq_code = '_GACODE_';
155 var _gaq = _gaq || [];
155 var _gaq = _gaq || [];
156 _gaq.push(['_setAccount', _gaq_code]);
156 _gaq.push(['_setAccount', _gaq_code]);
157 _gaq.push(['_trackPageview']);
157 _gaq.push(['_trackPageview']);
158
158
159 (function() {
159 (function() {
160 var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
160 var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
161 ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
161 ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
162 var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
162 var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
163 })();
163 })();
164
164
165 rhodecode_statechange_callback = function(url, data){
165 rhodecode_statechange_callback = function(url, data){
166 // ANALYTICS callback on html5 history state changed
166 // ANALYTICS callback on html5 history state changed
167 // triggered by file browser, url is the new url,
167 // triggered by file browser, url is the new url,
168 // data is extra info passed from the State object
168 // data is extra info passed from the State object
169 if (typeof window._gaq !== 'undefined') {
169 if (typeof window._gaq !== 'undefined') {
170 _gaq.push(['_trackPageview', url]);
170 _gaq.push(['_trackPageview', url]);
171 }
171 }
172 };
172 };
173 </script>
173 </script>
174 </%text>
174 </%text>
175 </script>
175 </script>
176
176
177
177
178
178
179 <script id="clicky_tmpl" type="text/x-template">
179 <script id="clicky_tmpl" type="text/x-template">
180 <%text filter="h">
180 <%text filter="h">
181 <script src="//static.getclicky.com/js" type="text/javascript"></script>
181 <script src="//static.getclicky.com/js" type="text/javascript"></script>
182 <script type="text/javascript">
182 <script type="text/javascript">
183 // Clicky Analytics - should be used in the footer code section.
183 // Clicky Analytics - should be used in the footer code section.
184 // Put your Clicky code instead of _CLICKYCODE_ here,
184 // Put your Clicky code instead of _CLICKYCODE_ here,
185 // and below in the <img> tag.
185 // and below in the <img> tag.
186 var _cl_code = _CLICKYCODE_;
186 var _cl_code = _CLICKYCODE_;
187 try{clicky.init(_cl_code);}catch(e){}
187 try{clicky.init(_cl_code);}catch(e){}
188
188
189 rhodecode_statechange_callback = function(url, data){
189 rhodecode_statechange_callback = function(url, data){
190 // ANALYTICS callback on html5 history state changed
190 // ANALYTICS callback on html5 history state changed
191 // triggered by file browser, url is the new url,
191 // triggered by file browser, url is the new url,
192 // data is extra info passed from the State object
192 // data is extra info passed from the State object
193 if (typeof window.clicky !== 'undefined') {
193 if (typeof window.clicky !== 'undefined') {
194 clicky.log(url);
194 clicky.log(url);
195 }
195 }
196 }
196 }
197 </script>
197 </script>
198 <noscript>
198 <noscript>
199 // Put your clicky code in the src file.
199 // Put your clicky code in the src file.
200 <p><img alt="Clicky" width="1" height="1"
200 <p><img alt="Clicky" width="1" height="1"
201 src="//in.getclicky.com/_CLICKYCODE_ns.gif" /></p>
201 src="//in.getclicky.com/_CLICKYCODE_ns.gif" /></p>
202 </noscript>
202 </noscript>
203 </%text>
203 </%text>
204 </script>
204 </script>
205
205
206
206
207
207
208 <script id="server_announce_tmpl" type='text/x-template'>
208 <script id="server_announce_tmpl" type='text/x-template'>
209 <%text filter="h">
209 <%text filter="h">
210 <script>
210 <script>
211 // Server announcement displayed on the top of the page.
211 // Server announcement displayed on the top of the page.
212 // This can be used to send a global maintenance messages or other
212 // This can be used to send a global maintenance messages or other
213 // important messages to all users of the RhodeCode Enterprise system.
213 // important messages to all users of the RhodeCode Enterprise system.
214
214
215 $(document).ready(function(e){
215 $(document).ready(function(e){
216
216
217 // EDIT - put your message below
217 // EDIT - put your message below
218 var message = "TYPE YOUR MESSAGE HERE";
218 var message = "TYPE YOUR MESSAGE HERE";
219
219
220 // EDIT - choose "info"/"warning"/"error"/"success"/"neutral" as appropriate
220 // EDIT - choose "info"/"warning"/"error"/"success"/"neutral" as appropriate
221 var alert_level = "info";
221 var alert_level = "info";
222
222
223 $("#body").prepend(
223 $("#body").prepend(
224 ("<div id='server-announcement' class='"+alert_level+"'>_MSG_"+"</div>").replace("_MSG_", message)
224 ("<div id='server-announcement' class='"+alert_level+"'>_MSG_"+"</div>").replace("_MSG_", message)
225 )
225 )
226 })
226 })
227 </script>
227 </script>
228 </%text>
228 </%text>
229 </script>
229 </script>
230
230
231 <script id="flash_filtering_tmpl" type='text/x-template'>
231 <script id="flash_filtering_tmpl" type='text/x-template'>
232 <%text filter="h">
232 <%text filter="h">
233 <script>
233 <script>
234 // This filters out some flash messages before they are presented to user
234 // This filters out some flash messages before they are presented to user
235 // based on their contents. Could be used to filter out warnings/errors
235 // based on their contents. Could be used to filter out warnings/errors
236 // of license messages
236 // of license messages
237
237
238 var filteredMessages = [];
238 var filteredMessages = [];
239 for(var i =0; i< alertMessagePayloads.length; i++){
239 for(var i =0; i< alertMessagePayloads.length; i++){
240 if (typeof alertMessagePayloads[i].message.subdata.subtype !== 'undefined' &&
240 if (typeof alertMessagePayloads[i].message.subdata.subtype !== 'undefined' &&
241 alertMessagePayloads[i].message.subdata.subtype.indexOf('rc_license') !== -1){
241 alertMessagePayloads[i].message.subdata.subtype.indexOf('rc_license') !== -1){
242 continue
242 continue
243 }
243 }
244 filteredMessages.push(alertMessagePayloads[i]);
244 filteredMessages.push(alertMessagePayloads[i]);
245 }
245 }
246 alertMessagePayloads = filteredMessages;
246 alertMessagePayloads = filteredMessages;
247 </script>
247 </script>
248 </%text>
248 </%text>
249 </script>
249 </script>
250
250
251 <script>
251 <script>
252 var pre_cm = initCodeMirror('rhodecode_pre_code', '', false);
252 var pre_cm = initCodeMirror('rhodecode_pre_code', '', false);
253 var pre_old = pre_cm.getValue();
253 var pre_old = pre_cm.getValue();
254
254
255 var post_cm = initCodeMirror('rhodecode_post_code', '', false);
255 var post_cm = initCodeMirror('rhodecode_post_code', '', false);
256 var post_old = post_cm.getValue();
256 var post_old = post_cm.getValue();
257
257
258 var get_data = function(type, old){
258 var get_data = function(type, old){
259 var get_tmpl = function(tmpl_name){
259 var get_tmpl = function(tmpl_name){
260 // unescape some stuff
260 // unescape some stuff
261 return htmlEnDeCode.htmlDecode($('#'+tmpl_name+'_tmpl').html());
261 return htmlEnDeCode.htmlDecode($('#'+tmpl_name+'_tmpl').html());
262 };
262 };
263 return {
263 return {
264 '#': old,
264 '#': old,
265 'ga': get_tmpl('ga'),
265 'ga': get_tmpl('ga'),
266 'clicky': get_tmpl('clicky'),
266 'clicky': get_tmpl('clicky'),
267 'server_announce': get_tmpl('server_announce'),
267 'server_announce': get_tmpl('server_announce'),
268 'flash_filtering': get_tmpl('flash_filtering')
268 'flash_filtering': get_tmpl('flash_filtering')
269 }[type]
269 }[type]
270 };
270 };
271
271
272 $('#pre_template').select2({
272 $('#pre_template').select2({
273 containerCssClass: 'drop-menu',
273 containerCssClass: 'drop-menu',
274 dropdownCssClass: 'drop-menu-dropdown',
274 dropdownCssClass: 'drop-menu-dropdown',
275 dropdownAutoWidth: true,
275 dropdownAutoWidth: true,
276 minimumResultsForSearch: -1
276 minimumResultsForSearch: -1
277 });
277 });
278
278
279 $('#post_template').select2({
279 $('#post_template').select2({
280 containerCssClass: 'drop-menu',
280 containerCssClass: 'drop-menu',
281 dropdownCssClass: 'drop-menu-dropdown',
281 dropdownCssClass: 'drop-menu-dropdown',
282 dropdownAutoWidth: true,
282 dropdownAutoWidth: true,
283 minimumResultsForSearch: -1
283 minimumResultsForSearch: -1
284 });
284 });
285
285
286 $('#post_template').on('change', function(e){
286 $('#post_template').on('change', function(e){
287 var sel = this.value;
287 var sel = this.value;
288 post_cm.setValue(get_data(sel, post_old))
288 post_cm.setValue(get_data(sel, post_old))
289 });
289 });
290
290
291 $('#pre_template').on('change', function(e){
291 $('#pre_template').on('change', function(e){
292 var sel = this.value;
292 var sel = this.value;
293 pre_cm.setValue(get_data(sel, pre_old))
293 pre_cm.setValue(get_data(sel, pre_old))
294 })
294 })
295 </script>
295 </script>
@@ -1,93 +1,92 b''
1 <div class="panel panel-default">
1 <div class="panel panel-default">
2 <div class="panel-heading">
2 <div class="panel-heading">
3 <h3 class="panel-title">${_('Built in Mercurial hooks - read only')}</h3>
3 <h3 class="panel-title">${_('Built in Mercurial hooks - read only')}</h3>
4 </div>
4 </div>
5 <div class="panel-body">
5 <div class="panel-body">
6 <div class="form">
6 <div class="form">
7 <div class="fields">
7 <div class="fields">
8 % for hook in c.hooks:
8 % for hook in c.hooks:
9 <div class="field">
9 <div class="field">
10 <div class="label label">
10 <div class="label label">
11 <label for="${hook.ui_key}">${hook.ui_key}</label>
11 <label for="${hook.ui_key}">${hook.ui_key}</label>
12 </div>
12 </div>
13 <div class="input" >
13 <div class="input" >
14 ${h.text(hook.ui_key,hook.ui_value,size=59,readonly="readonly")}
14 ${h.text(hook.ui_key,hook.ui_value,size=59,readonly="readonly")}
15 </div>
15 </div>
16 </div>
16 </div>
17 % endfor
17 % endfor
18 </div>
18 </div>
19 <span class="help-block">${_('Hooks can be used to trigger actions on certain events such as push / pull. They can trigger Python functions or external applications.')}</span>
19 <span class="help-block">${_('Hooks can be used to trigger actions on certain events such as push / pull. They can trigger Python functions or external applications.')}</span>
20 </div>
20 </div>
21 </div>
21 </div>
22 </div>
22 </div>
23
23
24
24
25 <div class="panel panel-default">
25 <div class="panel panel-default">
26 <div class="panel-heading">
26 <div class="panel-heading">
27 <h3 class="panel-title">${_('Custom hooks')}</h3>
27 <h3 class="panel-title">${_('Custom hooks')}</h3>
28 </div>
28 </div>
29 <div class="panel-body">
29 <div class="panel-body">
30 % if c.visual.allow_custom_hooks_settings:
30 % if c.visual.allow_custom_hooks_settings:
31 ${h.secure_form(h.url('admin_settings_hooks'), request=request)}
31 ${h.secure_form(h.route_path('admin_settings_hooks_update'), request=request)}
32 <div class="form">
32 <div class="form">
33 <div class="fields">
33 <div class="fields">
34
34
35 % for hook in c.custom_hooks:
35 % for hook in c.custom_hooks:
36 <div class="field" id="${'id%s' % hook.ui_id }">
36 <div class="field" id="${'id%s' % hook.ui_id }">
37 <div class="label label">
37 <div class="label label">
38 <label for="${hook.ui_key}">${hook.ui_key}</label>
38 <label for="${hook.ui_key}">${hook.ui_key}</label>
39 </div>
39 </div>
40 <div class="input" >
40 <div class="input" >
41 ${h.hidden('hook_ui_key',hook.ui_key)}
41 ${h.hidden('hook_ui_key',hook.ui_key)}
42 ${h.hidden('hook_ui_value',hook.ui_value)}
42 ${h.hidden('hook_ui_value',hook.ui_value)}
43 ${h.text('hook_ui_value_new',hook.ui_value,size=59)}
43 ${h.text('hook_ui_value_new',hook.ui_value,size=59)}
44 <span class="btn btn-danger"
44 <span class="btn btn-danger"
45 onclick="ajaxActionHook(${hook.ui_id},'${'id%s' % hook.ui_id }')">
45 onclick="ajaxActionHook(${hook.ui_id},'${'id%s' % hook.ui_id }')">
46 ${_('Delete')}
46 ${_('Delete')}
47 </span>
47 </span>
48 </div>
48 </div>
49 </div>
49 </div>
50 % endfor
50 % endfor
51
51
52 <div class="field customhooks">
52 <div class="field customhooks">
53 <div class="label">
53 <div class="label">
54 <div class="input-wrapper">
54 <div class="input-wrapper">
55 ${h.text('new_hook_ui_key',size=30)}
55 ${h.text('new_hook_ui_key',size=30)}
56 </div>
56 </div>
57 </div>
57 </div>
58 <div class="input">
58 <div class="input">
59 ${h.text('new_hook_ui_value',size=59)}
59 ${h.text('new_hook_ui_value',size=59)}
60 </div>
60 </div>
61 </div>
61 </div>
62 <div class="buttons">
62 <div class="buttons">
63 ${h.submit('save',_('Save'),class_="btn")}
63 ${h.submit('save',_('Save'),class_="btn")}
64 </div>
64 </div>
65 </div>
65 </div>
66 </div>
66 </div>
67 ${h.end_form()}
67 ${h.end_form()}
68 %else:
68 %else:
69 DISABLED
69 DISABLED
70 % endif
70 % endif
71 </div>
71 </div>
72 </div>
72 </div>
73
73
74
74
75 <script type="text/javascript">
75 <script type="text/javascript">
76 function ajaxActionHook(hook_id,field_id) {
76 function ajaxActionHook(hook_id,field_id) {
77 var sUrl = "${h.url('admin_settings_hooks')}";
77 var sUrl = "${h.route_path('admin_settings_hooks_delete')}";
78 var callback = function (o) {
78 var callback = function (o) {
79 var elem = $("#"+field_id);
79 var elem = $("#"+field_id);
80 elem.remove();
80 elem.remove();
81 };
81 };
82 var postData = {
82 var postData = {
83 '_method': 'delete',
84 'hook_id': hook_id,
83 'hook_id': hook_id,
85 'csrf_token': CSRF_TOKEN
84 'csrf_token': CSRF_TOKEN
86 };
85 };
87 var request = $.post(sUrl, postData)
86 var request = $.post(sUrl, postData)
88 .done(callback)
87 .done(callback)
89 .fail(function (data, textStatus, errorThrown) {
88 .fail(function (data, textStatus, errorThrown) {
90 alert("Error while deleting hooks.\nError code {0} ({1}). URL: {2}".format(data.status,data.statusText,$(this)[0].url));
89 alert("Error while deleting hooks.\nError code {0} ({1}). URL: {2}".format(data.status,data.statusText,$(this)[0].url));
91 });
90 });
92 };
91 }
93 </script>
92 </script>
@@ -1,34 +1,34 b''
1 <%namespace name="its" file="/base/issue_tracker_settings.mako"/>
1 <%namespace name="its" file="/base/issue_tracker_settings.mako"/>
2
2
3 <div class="panel panel-default">
3 <div class="panel panel-default">
4 <div class="panel-heading">
4 <div class="panel-heading">
5 <h3 class="panel-title">${_('Issue Tracker / Wiki Patterns')}</h3>
5 <h3 class="panel-title">${_('Issue Tracker / Wiki Patterns')}</h3>
6 </div>
6 </div>
7 <div class="panel-body">
7 <div class="panel-body">
8 ${h.secure_form(h.url('admin_settings_issuetracker_save'), request=request)}
8 ${h.secure_form(h.route_path('admin_settings_issuetracker_update'), request=request)}
9 ${its.issue_tracker_settings_table(
9 ${its.issue_tracker_settings_table(
10 patterns=c.issuetracker_entries.items(),
10 patterns=c.issuetracker_entries.items(),
11 form_url=h.url('admin_settings_issuetracker'),
11 form_url=h.route_path('admin_settings_issuetracker'),
12 delete_url=h.url('admin_issuetracker_delete')
12 delete_url=h.route_path('admin_settings_issuetracker_delete')
13 )}
13 )}
14 <div class="buttons">
14 <div class="buttons">
15 <button type="submit" class="btn btn-primary" id="save">${_('Save')}</button>
15 <button type="submit" class="btn btn-primary" id="save">${_('Save')}</button>
16 <button type="reset" class="btn">${_('Reset')}</button>
16 <button type="reset" class="btn">${_('Reset')}</button>
17 </div>
17 </div>
18 ${h.end_form()}
18 ${h.end_form()}
19 </div>
19 </div>
20 </div>
20 </div>
21
21
22 <div class="panel panel-default">
22 <div class="panel panel-default">
23 <div class="panel-heading">
23 <div class="panel-heading">
24 <h3 class="panel-title">${_('Test Patterns')}</h3>
24 <h3 class="panel-title">${_('Test Patterns')}</h3>
25 </div>
25 </div>
26 <div class="panel-body">
26 <div class="panel-body">
27 ${its.issue_tracker_new_row()}
27 ${its.issue_tracker_new_row()}
28 ${its.issue_tracker_settings_test(test_url=h.url('admin_issuetracker_test'))}
28 ${its.issue_tracker_settings_test(test_url=h.route_path('admin_settings_issuetracker_test'))}
29 </div>
29 </div>
30 </div>
30 </div>
31
31
32
32
33
33
34
34
@@ -1,58 +1,58 b''
1 <div class="panel panel-default">
1 <div class="panel panel-default">
2 <div class="panel-heading">
2 <div class="panel-heading">
3 <h3 class="panel-title">${_('Labs Settings')}</h3>
3 <h3 class="panel-title">${_('Labs Settings')}</h3>
4 </div>
4 </div>
5 <div class="panel-body">
5 <div class="panel-body">
6 ${h.secure_form(h.url('admin_settings_labs'), request=request)}
6 ${h.secure_form(h.route_path('admin_settings_labs_update'), request=request)}
7 <div class="form">
7 <div class="form">
8 <div class="fields">
8 <div class="fields">
9 % if not c.lab_settings:
9 % if not c.lab_settings:
10 ${_('There are no Labs settings currently')}
10 ${_('There are no Labs settings currently')}
11 % else:
11 % else:
12 % for lab_setting in c.lab_settings:
12 % for lab_setting in c.lab_settings:
13 <div class="field">
13 <div class="field">
14 <div class="label">
14 <div class="label">
15 <label>${lab_setting.group}:</label>
15 <label>${lab_setting.group}:</label>
16 </div>
16 </div>
17 % if lab_setting.type == 'bool':
17 % if lab_setting.type == 'bool':
18 <div class="checkboxes">
18 <div class="checkboxes">
19 <div class="checkbox">
19 <div class="checkbox">
20 ${h.checkbox(lab_setting.key, 'True')}
20 ${h.checkbox(lab_setting.key, 'True')}
21 % if lab_setting.label:
21 % if lab_setting.label:
22 <label for="${lab_setting.key}">${lab_setting.label}</label>
22 <label for="${lab_setting.key}">${lab_setting.label}</label>
23 % endif
23 % endif
24 </div>
24 </div>
25 % if lab_setting.help:
25 % if lab_setting.help:
26 <p class="help-block">${lab_setting.help}</p>
26 <p class="help-block">${lab_setting.help}</p>
27 % endif
27 % endif
28 </div>
28 </div>
29 % else:
29 % else:
30 <div class="input">
30 <div class="input">
31 ${h.text(lab_setting.key, size=60)}
31 ${h.text(lab_setting.key, size=60)}
32
32
33 ## TODO: johbo: This style does not yet exist for our forms,
33 ## TODO: johbo: This style does not yet exist for our forms,
34 ## the lab settings seem not to adhere to the structure which
34 ## the lab settings seem not to adhere to the structure which
35 ## we use in other places.
35 ## we use in other places.
36 % if lab_setting.label:
36 % if lab_setting.label:
37 <label for="${lab_setting.key}">${lab_setting.label}</label>
37 <label for="${lab_setting.key}">${lab_setting.label}</label>
38 % endif
38 % endif
39
39
40 % if lab_setting.help:
40 % if lab_setting.help:
41 <p class="help-block">${lab_setting.help}</p>
41 <p class="help-block">${lab_setting.help}</p>
42 % endif
42 % endif
43 </div>
43 </div>
44 % endif
44 % endif
45 </div>
45 </div>
46 % endfor
46 % endfor
47 <div class="buttons">
47 <div class="buttons">
48 ${h.submit('save', _('Save settings'), class_='btn')}
48 ${h.submit('save', _('Save settings'), class_='btn')}
49 ${h.reset('reset', _('Reset'), class_='btn')}
49 ${h.reset('reset', _('Reset'), class_='btn')}
50 </div>
50 </div>
51 % endif
51 % endif
52 </div>
52 </div>
53 </div>
53 </div>
54 ${h.end_form()}
54 ${h.end_form()}
55 </div>
55 </div>
56 </div>
56 </div>
57
57
58
58
@@ -1,28 +1,28 b''
1 ${h.secure_form(h.url('admin_settings_mapping'), request=request)}
1 ${h.secure_form(h.route_path('admin_settings_mapping_update'), request=request)}
2
2
3 <div class="panel panel-default">
3 <div class="panel panel-default">
4 <div class="panel-heading">
4 <div class="panel-heading">
5 <h3 class="panel-title">${_('Import New Groups or Repositories')}</h3>
5 <h3 class="panel-title">${_('Import New Groups or Repositories')}</h3>
6 </div>
6 </div>
7 <div class="panel-body">
7 <div class="panel-body">
8 <div class="checkbox">
8 <div class="checkbox">
9 ${h.checkbox('destroy',True)}
9 ${h.checkbox('destroy',True)}
10 <label for="destroy">${_('Destroy old data')}</label>
10 <label for="destroy">${_('Destroy old data')}</label>
11 </div>
11 </div>
12 <span class="help-block">${_('In case a repository or a group was deleted from the filesystem and it still exists in the database, check this option to remove obsolete data from the database.')}</span>
12 <span class="help-block">${_('In case a repository or a group was deleted from the filesystem and it still exists in the database, check this option to remove obsolete data from the database.')}</span>
13
13
14 <div class="checkbox">
14 <div class="checkbox">
15 ${h.checkbox('invalidate',True)}
15 ${h.checkbox('invalidate',True)}
16 <label for="invalidate"> ${_('Invalidate cache for all repositories')}</label>
16 <label for="invalidate"> ${_('Invalidate cache for all repositories')}</label>
17 </div>
17 </div>
18 <span class="help-block">${_('Each cache data for repositories will be cleaned with this option selected. Use this to reload data and clear cache keys.')}</span>
18 <span class="help-block">${_('Each cache data for repositories will be cleaned with this option selected. Use this to reload data and clear cache keys.')}</span>
19
19
20 <div class="buttons">
20 <div class="buttons">
21 ${h.submit('rescan',_('Rescan Filesystem'),class_="btn")}
21 ${h.submit('rescan',_('Rescan Filesystem'),class_="btn")}
22 </div>
22 </div>
23
23
24 </div>
24 </div>
25 </div>
25 </div>
26
26
27
27
28 ${h.end_form()}
28 ${h.end_form()}
@@ -1,66 +1,65 b''
1 <%namespace name="vcss" file="/base/vcs_settings.mako"/>
1 <%namespace name="vcss" file="/base/vcs_settings.mako"/>
2
2
3 ${h.secure_form(h.url('admin_settings_vcs'), request=request)}
3 ${h.secure_form(h.route_path('admin_settings_vcs_update'), request=request)}
4 <div>
4 <div>
5 ${vcss.vcs_settings_fields(
5 ${vcss.vcs_settings_fields(
6 suffix='',
6 suffix='',
7 svn_tag_patterns=c.svn_tag_patterns,
7 svn_tag_patterns=c.svn_tag_patterns,
8 svn_branch_patterns=c.svn_branch_patterns,
8 svn_branch_patterns=c.svn_branch_patterns,
9 display_globals=True,
9 display_globals=True,
10 allow_repo_location_change=c.visual.allow_repo_location_change
10 allow_repo_location_change=c.visual.allow_repo_location_change
11 )}
11 )}
12 <div class="buttons">
12 <div class="buttons">
13 ${h.submit('save',_('Save settings'),class_="btn")}
13 ${h.submit('save',_('Save settings'),class_="btn")}
14 ${h.reset('reset',_('Reset'),class_="btn")}
14 ${h.reset('reset',_('Reset'),class_="btn")}
15 </div>
15 </div>
16 </div>
16 </div>
17 ${h.end_form()}
17 ${h.end_form()}
18
18
19 <script type="text/javascript">
19 <script type="text/javascript">
20
20
21 function ajaxDeletePattern(pattern_id, field_id) {
21 function ajaxDeletePattern(pattern_id, field_id) {
22 var sUrl = "${h.url('admin_settings_vcs')}";
22 var sUrl = "${h.route_path('admin_settings_vcs_svn_pattern_delete')}";
23 var callback = function (o) {
23 var callback = function (o) {
24 var elem = $("#"+field_id);
24 var elem = $("#"+field_id);
25 elem.remove();
25 elem.remove();
26 };
26 };
27 var postData = {
27 var postData = {
28 '_method': 'delete',
29 'delete_svn_pattern': pattern_id,
28 'delete_svn_pattern': pattern_id,
30 'csrf_token': CSRF_TOKEN
29 'csrf_token': CSRF_TOKEN
31 };
30 };
32 var request = $.post(sUrl, postData)
31 var request = $.post(sUrl, postData)
33 .done(callback)
32 .done(callback)
34 .fail(function (data, textStatus, errorThrown) {
33 .fail(function (data, textStatus, errorThrown) {
35 alert("Error while deleting hooks.\nError code {0} ({1}). URL: {2}".format(data.status,data.statusText,$(this)[0].url));
34 alert("Error while deleting hooks.\nError code {0} ({1}). URL: {2}".format(data.status,data.statusText,$(this)[0].url));
36 });
35 });
37 };
36 };
38
37
39 $(document).ready(function() {
38 $(document).ready(function() {
40
39
41 var unlockpath = function() {
40 var unlockpath = function() {
42 $('#path_unlock_icon').removeClass('icon-lock').addClass('icon-unlock');
41 $('#path_unlock_icon').removeClass('icon-lock').addClass('icon-unlock');
43 $('#paths_root_path').removeAttr('readonly').removeClass('disabled');
42 $('#paths_root_path').removeAttr('readonly').removeClass('disabled');
44 };
43 };
45
44
46 $('#path_unlock').on('click', function(e) {
45 $('#path_unlock').on('click', function(e) {
47 unlockpath();
46 unlockpath();
48 });
47 });
49
48
50 if ($('.locked_input').children().hasClass('error-message')) {
49 if ($('.locked_input').children().hasClass('error-message')) {
51 unlockpath();
50 unlockpath();
52 }
51 }
53
52
54 /* On click handler for the `Generate Apache Config` button. It sends a
53 /* On click handler for the `Generate Apache Config` button. It sends a
55 POST request to trigger the (re)generation of the mod_dav_svn config. */
54 POST request to trigger the (re)generation of the mod_dav_svn config. */
56 $('#vcs_svn_generate_cfg').on('click', function(event) {
55 $('#vcs_svn_generate_cfg').on('click', function(event) {
57 event.preventDefault();
56 event.preventDefault();
58 var url = "${h.route_path('admin_settings_vcs_svn_generate_cfg')}";
57 var url = "${h.route_path('admin_settings_vcs_svn_generate_cfg')}";
59 var jqxhr = $.post(url, {'csrf_token': CSRF_TOKEN});
58 var jqxhr = $.post(url, {'csrf_token': CSRF_TOKEN});
60 jqxhr.done(function(data) {
59 jqxhr.done(function(data) {
61 $.Topic('/notifications').publish(data);
60 $.Topic('/notifications').publish(data);
62 });
61 });
63 });
62 });
64
63
65 });
64 });
66 </script>
65 </script>
@@ -1,226 +1,226 b''
1 ${h.secure_form(h.url('admin_settings_visual'), request=request)}
1 ${h.secure_form(h.route_path('admin_settings_visual_update'), request=request)}
2
2
3 <div class="panel panel-default">
3 <div class="panel panel-default">
4 <div class="panel-heading" id="general">
4 <div class="panel-heading" id="general">
5 <h3 class="panel-title">${_('General')}</h3>
5 <h3 class="panel-title">${_('General')}</h3>
6 </div>
6 </div>
7 <div class="panel-body">
7 <div class="panel-body">
8 <div class="checkbox">
8 <div class="checkbox">
9 ${h.checkbox('rhodecode_repository_fields','True')}
9 ${h.checkbox('rhodecode_repository_fields','True')}
10 <label for="rhodecode_repository_fields">${_('Use repository extra fields')}</label>
10 <label for="rhodecode_repository_fields">${_('Use repository extra fields')}</label>
11 </div>
11 </div>
12 <span class="help-block">${_('Allows storing additional customized fields per repository.')}</span>
12 <span class="help-block">${_('Allows storing additional customized fields per repository.')}</span>
13
13
14 <div></div>
14 <div></div>
15 <div class="checkbox">
15 <div class="checkbox">
16 ${h.checkbox('rhodecode_show_version','True')}
16 ${h.checkbox('rhodecode_show_version','True')}
17 <label for="rhodecode_show_version">${_('Show RhodeCode version')}</label>
17 <label for="rhodecode_show_version">${_('Show RhodeCode version')}</label>
18 </div>
18 </div>
19 <span class="help-block">${_('Shows or hides a version number of RhodeCode displayed in the footer.')}</span>
19 <span class="help-block">${_('Shows or hides a version number of RhodeCode displayed in the footer.')}</span>
20 </div>
20 </div>
21 </div>
21 </div>
22
22
23
23
24 <div class="panel panel-default">
24 <div class="panel panel-default">
25 <div class="panel-heading" id="gravatars">
25 <div class="panel-heading" id="gravatars">
26 <h3 class="panel-title">${_('Gravatars')}</h3>
26 <h3 class="panel-title">${_('Gravatars')}</h3>
27 </div>
27 </div>
28 <div class="panel-body">
28 <div class="panel-body">
29 <div class="checkbox">
29 <div class="checkbox">
30 ${h.checkbox('rhodecode_use_gravatar','True')}
30 ${h.checkbox('rhodecode_use_gravatar','True')}
31 <label for="rhodecode_use_gravatar">${_('Use Gravatars based avatars')}</label>
31 <label for="rhodecode_use_gravatar">${_('Use Gravatars based avatars')}</label>
32 </div>
32 </div>
33 <span class="help-block">${_('Use gravatar.com as avatar system for RhodeCode accounts. If this is disabled avatars are generated based on initials and email.')}</span>
33 <span class="help-block">${_('Use gravatar.com as avatar system for RhodeCode accounts. If this is disabled avatars are generated based on initials and email.')}</span>
34
34
35 <div class="label">
35 <div class="label">
36 <label for="rhodecode_gravatar_url">${_('Gravatar URL')}</label>
36 <label for="rhodecode_gravatar_url">${_('Gravatar URL')}</label>
37 </div>
37 </div>
38 <div class="input">
38 <div class="input">
39 <div class="field">
39 <div class="field">
40 ${h.text('rhodecode_gravatar_url', size='100%')}
40 ${h.text('rhodecode_gravatar_url', size='100%')}
41 </div>
41 </div>
42
42
43 <div class="field">
43 <div class="field">
44 <span class="help-block">${_('''Gravatar url allows you to use other avatar server application.
44 <span class="help-block">${_('''Gravatar url allows you to use other avatar server application.
45 Following variables of the URL will be replaced accordingly.
45 Following variables of the URL will be replaced accordingly.
46 {scheme} 'http' or 'https' sent from running RhodeCode server,
46 {scheme} 'http' or 'https' sent from running RhodeCode server,
47 {email} user email,
47 {email} user email,
48 {md5email} md5 hash of the user email (like at gravatar.com),
48 {md5email} md5 hash of the user email (like at gravatar.com),
49 {size} size of the image that is expected from the server application,
49 {size} size of the image that is expected from the server application,
50 {netloc} network location/server host of running RhodeCode server''')}</span>
50 {netloc} network location/server host of running RhodeCode server''')}</span>
51 </div>
51 </div>
52 </div>
52 </div>
53 </div>
53 </div>
54 </div>
54 </div>
55
55
56
56
57 <div class="panel panel-default">
57 <div class="panel panel-default">
58 <div class="panel-heading" id="meta-tagging">
58 <div class="panel-heading" id="meta-tagging">
59 <h3 class="panel-title">${_('Meta-Tagging')}</h3>
59 <h3 class="panel-title">${_('Meta-Tagging')}</h3>
60 </div>
60 </div>
61 <div class="panel-body">
61 <div class="panel-body">
62 <div class="checkbox">
62 <div class="checkbox">
63 ${h.checkbox('rhodecode_stylify_metatags','True')}
63 ${h.checkbox('rhodecode_stylify_metatags','True')}
64 <label for="rhodecode_stylify_metatags">${_('Stylify recognised meta tags')}</label>
64 <label for="rhodecode_stylify_metatags">${_('Stylify recognised meta tags')}</label>
65 </div>
65 </div>
66 <span class="help-block">${_('Parses meta tags from repository or repository group description fields and turns them into colored tags.')}</span>
66 <span class="help-block">${_('Parses meta tags from repository or repository group description fields and turns them into colored tags.')}</span>
67 <div>
67 <div>
68 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
68 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
69 ${dt.metatags_help()}
69 ${dt.metatags_help()}
70 </div>
70 </div>
71 </div>
71 </div>
72 </div>
72 </div>
73
73
74
74
75 <div class="panel panel-default">
75 <div class="panel panel-default">
76 <div class="panel-heading">
76 <div class="panel-heading">
77 <h3 class="panel-title">${_('Dashboard Items')}</h3>
77 <h3 class="panel-title">${_('Dashboard Items')}</h3>
78 </div>
78 </div>
79 <div class="panel-body">
79 <div class="panel-body">
80 <div class="label">
80 <div class="label">
81 <label for="rhodecode_dashboard_items">${_('Main page dashboard items')}</label>
81 <label for="rhodecode_dashboard_items">${_('Main page dashboard items')}</label>
82 </div>
82 </div>
83 <div class="field input">
83 <div class="field input">
84 ${h.text('rhodecode_dashboard_items',size=5)}
84 ${h.text('rhodecode_dashboard_items',size=5)}
85 </div>
85 </div>
86 <div class="field">
86 <div class="field">
87 <span class="help-block">${_('Number of items displayed in the main page dashboard before pagination is shown.')}</span>
87 <span class="help-block">${_('Number of items displayed in the main page dashboard before pagination is shown.')}</span>
88 </div>
88 </div>
89
89
90 <div class="label">
90 <div class="label">
91 <label for="rhodecode_admin_grid_items">${_('Admin pages items')}</label>
91 <label for="rhodecode_admin_grid_items">${_('Admin pages items')}</label>
92 </div>
92 </div>
93 <div class="field input">
93 <div class="field input">
94 ${h.text('rhodecode_admin_grid_items',size=5)}
94 ${h.text('rhodecode_admin_grid_items',size=5)}
95 </div>
95 </div>
96 <div class="field">
96 <div class="field">
97 <span class="help-block">${_('Number of items displayed in the admin pages grids before pagination is shown.')}</span>
97 <span class="help-block">${_('Number of items displayed in the admin pages grids before pagination is shown.')}</span>
98 </div>
98 </div>
99 </div>
99 </div>
100 </div>
100 </div>
101
101
102
102
103
103
104 <div class="panel panel-default">
104 <div class="panel panel-default">
105 <div class="panel-heading" id="commit-id">
105 <div class="panel-heading" id="commit-id">
106 <h3 class="panel-title">${_('Commit ID Style')}</h3>
106 <h3 class="panel-title">${_('Commit ID Style')}</h3>
107 </div>
107 </div>
108 <div class="panel-body">
108 <div class="panel-body">
109 <div class="label">
109 <div class="label">
110 <label for="rhodecode_show_sha_length">${_('Commit sha length')}</label>
110 <label for="rhodecode_show_sha_length">${_('Commit sha length')}</label>
111 </div>
111 </div>
112 <div class="input">
112 <div class="input">
113 <div class="field">
113 <div class="field">
114 ${h.text('rhodecode_show_sha_length',size=5)}
114 ${h.text('rhodecode_show_sha_length',size=5)}
115 </div>
115 </div>
116 <div class="field">
116 <div class="field">
117 <span class="help-block">${_('''Number of chars to show in commit sha displayed in web interface.
117 <span class="help-block">${_('''Number of chars to show in commit sha displayed in web interface.
118 By default it's shown as r123:9043a6a4c226 this value defines the
118 By default it's shown as r123:9043a6a4c226 this value defines the
119 length of the sha after the `r123:` part.''')}</span>
119 length of the sha after the `r123:` part.''')}</span>
120 </div>
120 </div>
121 </div>
121 </div>
122
122
123 <div class="checkbox">
123 <div class="checkbox">
124 ${h.checkbox('rhodecode_show_revision_number','True')}
124 ${h.checkbox('rhodecode_show_revision_number','True')}
125 <label for="rhodecode_show_revision_number">${_('Show commit ID numeric reference')} / ${_('Commit show revision number')}</label>
125 <label for="rhodecode_show_revision_number">${_('Show commit ID numeric reference')} / ${_('Commit show revision number')}</label>
126 </div>
126 </div>
127 <span class="help-block">${_('''Show revision number in commit sha displayed in web interface.
127 <span class="help-block">${_('''Show revision number in commit sha displayed in web interface.
128 By default it's shown as r123:9043a6a4c226 this value defines the
128 By default it's shown as r123:9043a6a4c226 this value defines the
129 if the `r123:` part is shown.''')}</span>
129 if the `r123:` part is shown.''')}</span>
130 </div>
130 </div>
131 </div>
131 </div>
132
132
133
133
134 <div class="panel panel-default">
134 <div class="panel panel-default">
135 <div class="panel-heading" id="icons">
135 <div class="panel-heading" id="icons">
136 <h3 class="panel-title">${_('Icons')}</h3>
136 <h3 class="panel-title">${_('Icons')}</h3>
137 </div>
137 </div>
138 <div class="panel-body">
138 <div class="panel-body">
139 <div class="checkbox">
139 <div class="checkbox">
140 ${h.checkbox('rhodecode_show_public_icon','True')}
140 ${h.checkbox('rhodecode_show_public_icon','True')}
141 <label for="rhodecode_show_public_icon">${_('Show public repo icon on repositories')}</label>
141 <label for="rhodecode_show_public_icon">${_('Show public repo icon on repositories')}</label>
142 </div>
142 </div>
143 <div></div>
143 <div></div>
144
144
145 <div class="checkbox">
145 <div class="checkbox">
146 ${h.checkbox('rhodecode_show_private_icon','True')}
146 ${h.checkbox('rhodecode_show_private_icon','True')}
147 <label for="rhodecode_show_private_icon">${_('Show private repo icon on repositories')}</label>
147 <label for="rhodecode_show_private_icon">${_('Show private repo icon on repositories')}</label>
148 </div>
148 </div>
149 <span class="help-block">${_('Show public/private icons next to repositories names.')}</span>
149 <span class="help-block">${_('Show public/private icons next to repositories names.')}</span>
150 </div>
150 </div>
151 </div>
151 </div>
152
152
153
153
154 <div class="panel panel-default">
154 <div class="panel panel-default">
155 <div class="panel-heading">
155 <div class="panel-heading">
156 <h3 class="panel-title">${_('Markup Renderer')}</h3>
156 <h3 class="panel-title">${_('Markup Renderer')}</h3>
157 </div>
157 </div>
158 <div class="panel-body">
158 <div class="panel-body">
159 <div class="field select">
159 <div class="field select">
160 ${h.select('rhodecode_markup_renderer', '', ['rst', 'markdown'])}
160 ${h.select('rhodecode_markup_renderer', '', ['rst', 'markdown'])}
161 </div>
161 </div>
162 <div class="field">
162 <div class="field">
163 <span class="help-block">${_('Default renderer used to render comments, pull request descriptions and other description elements. After change old entries will still work correctly.')}</span>
163 <span class="help-block">${_('Default renderer used to render comments, pull request descriptions and other description elements. After change old entries will still work correctly.')}</span>
164 </div>
164 </div>
165 </div>
165 </div>
166 </div>
166 </div>
167
167
168 <div class="panel panel-default">
168 <div class="panel panel-default">
169 <div class="panel-heading">
169 <div class="panel-heading">
170 <h3 class="panel-title">${_('Clone URL')}</h3>
170 <h3 class="panel-title">${_('Clone URL')}</h3>
171 </div>
171 </div>
172 <div class="panel-body">
172 <div class="panel-body">
173 <div class="field">
173 <div class="field">
174 ${h.text('rhodecode_clone_uri_tmpl', size=60)}
174 ${h.text('rhodecode_clone_uri_tmpl', size=60)}
175 </div>
175 </div>
176
176
177 <div class="field">
177 <div class="field">
178 <span class="help-block">
178 <span class="help-block">
179 ${_('''Schema of clone url construction eg. '{scheme}://{user}@{netloc}/{repo}', available vars:
179 ${_('''Schema of clone url construction eg. '{scheme}://{user}@{netloc}/{repo}', available vars:
180 {scheme} 'http' or 'https' sent from running RhodeCode server,
180 {scheme} 'http' or 'https' sent from running RhodeCode server,
181 {user} current user username,
181 {user} current user username,
182 {netloc} network location/server host of running RhodeCode server,
182 {netloc} network location/server host of running RhodeCode server,
183 {repo} full repository name,
183 {repo} full repository name,
184 {repoid} ID of repository, can be used to contruct clone-by-id''')}
184 {repoid} ID of repository, can be used to contruct clone-by-id''')}
185 </span>
185 </span>
186 </div>
186 </div>
187 </div>
187 </div>
188 </div>
188 </div>
189
189
190 <div class="panel panel-default">
190 <div class="panel panel-default">
191 <div class="panel-heading">
191 <div class="panel-heading">
192 <h3 class="panel-title">${_('Custom Support Link')}</h3>
192 <h3 class="panel-title">${_('Custom Support Link')}</h3>
193 </div>
193 </div>
194 <div class="panel-body">
194 <div class="panel-body">
195 <div class="field">
195 <div class="field">
196 ${h.text('rhodecode_support_url', size=60)}
196 ${h.text('rhodecode_support_url', size=60)}
197 </div>
197 </div>
198 <div class="field">
198 <div class="field">
199 <span class="help-block">
199 <span class="help-block">
200 ${_('''Custom url for the support link located at the bottom.
200 ${_('''Custom url for the support link located at the bottom.
201 The default is set to %(default_url)s. In case there's a need
201 The default is set to %(default_url)s. In case there's a need
202 to change the support link to internal issue tracker, it should be done here.
202 to change the support link to internal issue tracker, it should be done here.
203 ''') % {'default_url': h.route_url('rhodecode_support')}}
203 ''') % {'default_url': h.route_url('rhodecode_support')}}
204 </span>
204 </span>
205 </div>
205 </div>
206 </div>
206 </div>
207 </div>
207 </div>
208
208
209 <div class="buttons">
209 <div class="buttons">
210 ${h.submit('save',_('Save settings'),class_="btn")}
210 ${h.submit('save',_('Save settings'),class_="btn")}
211 ${h.reset('reset',_('Reset'),class_="btn")}
211 ${h.reset('reset',_('Reset'),class_="btn")}
212 </div>
212 </div>
213
213
214
214
215 ${h.end_form()}
215 ${h.end_form()}
216
216
217 <script>
217 <script>
218 $(document).ready(function() {
218 $(document).ready(function() {
219 $('#rhodecode_markup_renderer').select2({
219 $('#rhodecode_markup_renderer').select2({
220 containerCssClass: 'drop-menu',
220 containerCssClass: 'drop-menu',
221 dropdownCssClass: 'drop-menu-dropdown',
221 dropdownCssClass: 'drop-menu-dropdown',
222 dropdownAutoWidth: true,
222 dropdownAutoWidth: true,
223 minimumResultsForSearch: -1
223 minimumResultsForSearch: -1
224 });
224 });
225 });
225 });
226 </script>
226 </script>
@@ -1,609 +1,609 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="root.mako"/>
2 <%inherit file="root.mako"/>
3
3
4 <div class="outerwrapper">
4 <div class="outerwrapper">
5 <!-- HEADER -->
5 <!-- HEADER -->
6 <div class="header">
6 <div class="header">
7 <div id="header-inner" class="wrapper">
7 <div id="header-inner" class="wrapper">
8 <div id="logo">
8 <div id="logo">
9 <div class="logo-wrapper">
9 <div class="logo-wrapper">
10 <a href="${h.route_path('home')}"><img src="${h.asset('images/rhodecode-logo-white-216x60.png')}" alt="RhodeCode"/></a>
10 <a href="${h.route_path('home')}"><img src="${h.asset('images/rhodecode-logo-white-216x60.png')}" alt="RhodeCode"/></a>
11 </div>
11 </div>
12 %if c.rhodecode_name:
12 %if c.rhodecode_name:
13 <div class="branding">- ${h.branding(c.rhodecode_name)}</div>
13 <div class="branding">- ${h.branding(c.rhodecode_name)}</div>
14 %endif
14 %endif
15 </div>
15 </div>
16 <!-- MENU BAR NAV -->
16 <!-- MENU BAR NAV -->
17 ${self.menu_bar_nav()}
17 ${self.menu_bar_nav()}
18 <!-- END MENU BAR NAV -->
18 <!-- END MENU BAR NAV -->
19 </div>
19 </div>
20 </div>
20 </div>
21 ${self.menu_bar_subnav()}
21 ${self.menu_bar_subnav()}
22 <!-- END HEADER -->
22 <!-- END HEADER -->
23
23
24 <!-- CONTENT -->
24 <!-- CONTENT -->
25 <div id="content" class="wrapper">
25 <div id="content" class="wrapper">
26
26
27 <rhodecode-toast id="notifications"></rhodecode-toast>
27 <rhodecode-toast id="notifications"></rhodecode-toast>
28
28
29 <div class="main">
29 <div class="main">
30 ${next.main()}
30 ${next.main()}
31 </div>
31 </div>
32 </div>
32 </div>
33 <!-- END CONTENT -->
33 <!-- END CONTENT -->
34
34
35 </div>
35 </div>
36 <!-- FOOTER -->
36 <!-- FOOTER -->
37 <div id="footer">
37 <div id="footer">
38 <div id="footer-inner" class="title wrapper">
38 <div id="footer-inner" class="title wrapper">
39 <div>
39 <div>
40 <p class="footer-link-right">
40 <p class="footer-link-right">
41 % if c.visual.show_version:
41 % if c.visual.show_version:
42 RhodeCode Enterprise ${c.rhodecode_version} ${c.rhodecode_edition}
42 RhodeCode Enterprise ${c.rhodecode_version} ${c.rhodecode_edition}
43 % endif
43 % endif
44 &copy; 2010-${h.datetime.today().year}, <a href="${h.route_url('rhodecode_official')}" target="_blank">RhodeCode GmbH</a>. All rights reserved.
44 &copy; 2010-${h.datetime.today().year}, <a href="${h.route_url('rhodecode_official')}" target="_blank">RhodeCode GmbH</a>. All rights reserved.
45 % if c.visual.rhodecode_support_url:
45 % if c.visual.rhodecode_support_url:
46 <a href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a>
46 <a href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a>
47 % endif
47 % endif
48 </p>
48 </p>
49 <% sid = 'block' if request.GET.get('showrcid') else 'none' %>
49 <% sid = 'block' if request.GET.get('showrcid') else 'none' %>
50 <p class="server-instance" style="display:${sid}">
50 <p class="server-instance" style="display:${sid}">
51 ## display hidden instance ID if specially defined
51 ## display hidden instance ID if specially defined
52 % if c.rhodecode_instanceid:
52 % if c.rhodecode_instanceid:
53 ${_('RhodeCode instance id: %s') % c.rhodecode_instanceid}
53 ${_('RhodeCode instance id: %s') % c.rhodecode_instanceid}
54 % endif
54 % endif
55 </p>
55 </p>
56 </div>
56 </div>
57 </div>
57 </div>
58 </div>
58 </div>
59
59
60 <!-- END FOOTER -->
60 <!-- END FOOTER -->
61
61
62 ### MAKO DEFS ###
62 ### MAKO DEFS ###
63
63
64 <%def name="menu_bar_subnav()">
64 <%def name="menu_bar_subnav()">
65 </%def>
65 </%def>
66
66
67 <%def name="breadcrumbs(class_='breadcrumbs')">
67 <%def name="breadcrumbs(class_='breadcrumbs')">
68 <div class="${class_}">
68 <div class="${class_}">
69 ${self.breadcrumbs_links()}
69 ${self.breadcrumbs_links()}
70 </div>
70 </div>
71 </%def>
71 </%def>
72
72
73 <%def name="admin_menu()">
73 <%def name="admin_menu()">
74 <ul class="admin_menu submenu">
74 <ul class="admin_menu submenu">
75 <li><a href="${h.route_path('admin_audit_logs')}">${_('Admin audit logs')}</a></li>
75 <li><a href="${h.route_path('admin_audit_logs')}">${_('Admin audit logs')}</a></li>
76 <li><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
76 <li><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
77 <li><a href="${h.route_path('repo_groups')}">${_('Repository groups')}</a></li>
77 <li><a href="${h.route_path('repo_groups')}">${_('Repository groups')}</a></li>
78 <li><a href="${h.route_path('users')}">${_('Users')}</a></li>
78 <li><a href="${h.route_path('users')}">${_('Users')}</a></li>
79 <li><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li>
79 <li><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li>
80 <li><a href="${h.route_path('admin_permissions_application')}">${_('Permissions')}</a></li>
80 <li><a href="${h.route_path('admin_permissions_application')}">${_('Permissions')}</a></li>
81 <li><a href="${h.route_path('auth_home', traverse='')}">${_('Authentication')}</a></li>
81 <li><a href="${h.route_path('auth_home', traverse='')}">${_('Authentication')}</a></li>
82 <li><a href="${h.route_path('global_integrations_home')}">${_('Integrations')}</a></li>
82 <li><a href="${h.route_path('global_integrations_home')}">${_('Integrations')}</a></li>
83 <li><a href="${h.route_path('admin_defaults_repositories')}">${_('Defaults')}</a></li>
83 <li><a href="${h.route_path('admin_defaults_repositories')}">${_('Defaults')}</a></li>
84 <li class="last"><a href="${h.url('admin_settings')}">${_('Settings')}</a></li>
84 <li class="last"><a href="${h.route_path('admin_settings')}">${_('Settings')}</a></li>
85 </ul>
85 </ul>
86 </%def>
86 </%def>
87
87
88
88
89 <%def name="dt_info_panel(elements)">
89 <%def name="dt_info_panel(elements)">
90 <dl class="dl-horizontal">
90 <dl class="dl-horizontal">
91 %for dt, dd, title, show_items in elements:
91 %for dt, dd, title, show_items in elements:
92 <dt>${dt}:</dt>
92 <dt>${dt}:</dt>
93 <dd title="${h.tooltip(title)}">
93 <dd title="${h.tooltip(title)}">
94 %if callable(dd):
94 %if callable(dd):
95 ## allow lazy evaluation of elements
95 ## allow lazy evaluation of elements
96 ${dd()}
96 ${dd()}
97 %else:
97 %else:
98 ${dd}
98 ${dd}
99 %endif
99 %endif
100 %if show_items:
100 %if show_items:
101 <span class="btn-collapse" data-toggle="item-${h.md5_safe(dt)[:6]}-details">${_('Show More')} </span>
101 <span class="btn-collapse" data-toggle="item-${h.md5_safe(dt)[:6]}-details">${_('Show More')} </span>
102 %endif
102 %endif
103 </dd>
103 </dd>
104
104
105 %if show_items:
105 %if show_items:
106 <div class="collapsable-content" data-toggle="item-${h.md5_safe(dt)[:6]}-details" style="display: none">
106 <div class="collapsable-content" data-toggle="item-${h.md5_safe(dt)[:6]}-details" style="display: none">
107 %for item in show_items:
107 %for item in show_items:
108 <dt></dt>
108 <dt></dt>
109 <dd>${item}</dd>
109 <dd>${item}</dd>
110 %endfor
110 %endfor
111 </div>
111 </div>
112 %endif
112 %endif
113
113
114 %endfor
114 %endfor
115 </dl>
115 </dl>
116 </%def>
116 </%def>
117
117
118
118
119 <%def name="gravatar(email, size=16)">
119 <%def name="gravatar(email, size=16)">
120 <%
120 <%
121 if (size > 16):
121 if (size > 16):
122 gravatar_class = 'gravatar gravatar-large'
122 gravatar_class = 'gravatar gravatar-large'
123 else:
123 else:
124 gravatar_class = 'gravatar'
124 gravatar_class = 'gravatar'
125 %>
125 %>
126 <%doc>
126 <%doc>
127 TODO: johbo: For now we serve double size images to make it smooth
127 TODO: johbo: For now we serve double size images to make it smooth
128 for retina. This is how it worked until now. Should be replaced
128 for retina. This is how it worked until now. Should be replaced
129 with a better solution at some point.
129 with a better solution at some point.
130 </%doc>
130 </%doc>
131 <img class="${gravatar_class}" src="${h.gravatar_url(email, size * 2)}" height="${size}" width="${size}">
131 <img class="${gravatar_class}" src="${h.gravatar_url(email, size * 2)}" height="${size}" width="${size}">
132 </%def>
132 </%def>
133
133
134
134
135 <%def name="gravatar_with_user(contact, size=16, show_disabled=False)">
135 <%def name="gravatar_with_user(contact, size=16, show_disabled=False)">
136 <% email = h.email_or_none(contact) %>
136 <% email = h.email_or_none(contact) %>
137 <div class="rc-user tooltip" title="${h.tooltip(h.author_string(email))}">
137 <div class="rc-user tooltip" title="${h.tooltip(h.author_string(email))}">
138 ${self.gravatar(email, size)}
138 ${self.gravatar(email, size)}
139 <span class="${'user user-disabled' if show_disabled else 'user'}"> ${h.link_to_user(contact)}</span>
139 <span class="${'user user-disabled' if show_disabled else 'user'}"> ${h.link_to_user(contact)}</span>
140 </div>
140 </div>
141 </%def>
141 </%def>
142
142
143
143
144 ## admin menu used for people that have some admin resources
144 ## admin menu used for people that have some admin resources
145 <%def name="admin_menu_simple(repositories=None, repository_groups=None, user_groups=None)">
145 <%def name="admin_menu_simple(repositories=None, repository_groups=None, user_groups=None)">
146 <ul class="submenu">
146 <ul class="submenu">
147 %if repositories:
147 %if repositories:
148 <li class="local-admin-repos"><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
148 <li class="local-admin-repos"><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
149 %endif
149 %endif
150 %if repository_groups:
150 %if repository_groups:
151 <li class="local-admin-repo-groups"><a href="${h.route_path('repo_groups')}">${_('Repository groups')}</a></li>
151 <li class="local-admin-repo-groups"><a href="${h.route_path('repo_groups')}">${_('Repository groups')}</a></li>
152 %endif
152 %endif
153 %if user_groups:
153 %if user_groups:
154 <li class="local-admin-user-groups"><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li>
154 <li class="local-admin-user-groups"><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li>
155 %endif
155 %endif
156 </ul>
156 </ul>
157 </%def>
157 </%def>
158
158
159 <%def name="repo_page_title(repo_instance)">
159 <%def name="repo_page_title(repo_instance)">
160 <div class="title-content">
160 <div class="title-content">
161 <div class="title-main">
161 <div class="title-main">
162 ## SVN/HG/GIT icons
162 ## SVN/HG/GIT icons
163 %if h.is_hg(repo_instance):
163 %if h.is_hg(repo_instance):
164 <i class="icon-hg"></i>
164 <i class="icon-hg"></i>
165 %endif
165 %endif
166 %if h.is_git(repo_instance):
166 %if h.is_git(repo_instance):
167 <i class="icon-git"></i>
167 <i class="icon-git"></i>
168 %endif
168 %endif
169 %if h.is_svn(repo_instance):
169 %if h.is_svn(repo_instance):
170 <i class="icon-svn"></i>
170 <i class="icon-svn"></i>
171 %endif
171 %endif
172
172
173 ## public/private
173 ## public/private
174 %if repo_instance.private:
174 %if repo_instance.private:
175 <i class="icon-repo-private"></i>
175 <i class="icon-repo-private"></i>
176 %else:
176 %else:
177 <i class="icon-repo-public"></i>
177 <i class="icon-repo-public"></i>
178 %endif
178 %endif
179
179
180 ## repo name with group name
180 ## repo name with group name
181 ${h.breadcrumb_repo_link(c.rhodecode_db_repo)}
181 ${h.breadcrumb_repo_link(c.rhodecode_db_repo)}
182
182
183 </div>
183 </div>
184
184
185 ## FORKED
185 ## FORKED
186 %if repo_instance.fork:
186 %if repo_instance.fork:
187 <p>
187 <p>
188 <i class="icon-code-fork"></i> ${_('Fork of')}
188 <i class="icon-code-fork"></i> ${_('Fork of')}
189 <a href="${h.route_path('repo_summary',repo_name=repo_instance.fork.repo_name)}">${repo_instance.fork.repo_name}</a>
189 <a href="${h.route_path('repo_summary',repo_name=repo_instance.fork.repo_name)}">${repo_instance.fork.repo_name}</a>
190 </p>
190 </p>
191 %endif
191 %endif
192
192
193 ## IMPORTED FROM REMOTE
193 ## IMPORTED FROM REMOTE
194 %if repo_instance.clone_uri:
194 %if repo_instance.clone_uri:
195 <p>
195 <p>
196 <i class="icon-code-fork"></i> ${_('Clone from')}
196 <i class="icon-code-fork"></i> ${_('Clone from')}
197 <a href="${h.safe_str(h.hide_credentials(repo_instance.clone_uri))}">${h.hide_credentials(repo_instance.clone_uri)}</a>
197 <a href="${h.safe_str(h.hide_credentials(repo_instance.clone_uri))}">${h.hide_credentials(repo_instance.clone_uri)}</a>
198 </p>
198 </p>
199 %endif
199 %endif
200
200
201 ## LOCKING STATUS
201 ## LOCKING STATUS
202 %if repo_instance.locked[0]:
202 %if repo_instance.locked[0]:
203 <p class="locking_locked">
203 <p class="locking_locked">
204 <i class="icon-repo-lock"></i>
204 <i class="icon-repo-lock"></i>
205 ${_('Repository locked by %(user)s') % {'user': h.person_by_id(repo_instance.locked[0])}}
205 ${_('Repository locked by %(user)s') % {'user': h.person_by_id(repo_instance.locked[0])}}
206 </p>
206 </p>
207 %elif repo_instance.enable_locking:
207 %elif repo_instance.enable_locking:
208 <p class="locking_unlocked">
208 <p class="locking_unlocked">
209 <i class="icon-repo-unlock"></i>
209 <i class="icon-repo-unlock"></i>
210 ${_('Repository not locked. Pull repository to lock it.')}
210 ${_('Repository not locked. Pull repository to lock it.')}
211 </p>
211 </p>
212 %endif
212 %endif
213
213
214 </div>
214 </div>
215 </%def>
215 </%def>
216
216
217 <%def name="repo_menu(active=None)">
217 <%def name="repo_menu(active=None)">
218 <%
218 <%
219 def is_active(selected):
219 def is_active(selected):
220 if selected == active:
220 if selected == active:
221 return "active"
221 return "active"
222 %>
222 %>
223
223
224 <!--- CONTEXT BAR -->
224 <!--- CONTEXT BAR -->
225 <div id="context-bar">
225 <div id="context-bar">
226 <div class="wrapper">
226 <div class="wrapper">
227 <ul id="context-pages" class="horizontal-list navigation">
227 <ul id="context-pages" class="horizontal-list navigation">
228 <li class="${is_active('summary')}"><a class="menulink" href="${h.route_path('repo_summary', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li>
228 <li class="${is_active('summary')}"><a class="menulink" href="${h.route_path('repo_summary', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li>
229 <li class="${is_active('changelog')}"><a class="menulink" href="${h.route_path('repo_changelog', repo_name=c.repo_name)}"><div class="menulabel">${_('Changelog')}</div></a></li>
229 <li class="${is_active('changelog')}"><a class="menulink" href="${h.route_path('repo_changelog', repo_name=c.repo_name)}"><div class="menulabel">${_('Changelog')}</div></a></li>
230 <li class="${is_active('files')}"><a class="menulink" href="${h.route_path('repo_files', repo_name=c.repo_name, commit_id=c.rhodecode_db_repo.landing_rev[1], f_path='')}"><div class="menulabel">${_('Files')}</div></a></li>
230 <li class="${is_active('files')}"><a class="menulink" href="${h.route_path('repo_files', repo_name=c.repo_name, commit_id=c.rhodecode_db_repo.landing_rev[1], f_path='')}"><div class="menulabel">${_('Files')}</div></a></li>
231 <li class="${is_active('compare')}"><a class="menulink" href="${h.route_path('repo_compare_select',repo_name=c.repo_name)}"><div class="menulabel">${_('Compare')}</div></a></li>
231 <li class="${is_active('compare')}"><a class="menulink" href="${h.route_path('repo_compare_select',repo_name=c.repo_name)}"><div class="menulabel">${_('Compare')}</div></a></li>
232 ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()"
232 ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()"
233 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
233 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
234 <li class="${is_active('showpullrequest')}">
234 <li class="${is_active('showpullrequest')}">
235 <a class="menulink" href="${h.route_path('pullrequest_show_all', repo_name=c.repo_name)}" title="${h.tooltip(_('Show Pull Requests for %s') % c.repo_name)}">
235 <a class="menulink" href="${h.route_path('pullrequest_show_all', repo_name=c.repo_name)}" title="${h.tooltip(_('Show Pull Requests for %s') % c.repo_name)}">
236 %if c.repository_pull_requests:
236 %if c.repository_pull_requests:
237 <span class="pr_notifications">${c.repository_pull_requests}</span>
237 <span class="pr_notifications">${c.repository_pull_requests}</span>
238 %endif
238 %endif
239 <div class="menulabel">${_('Pull Requests')}</div>
239 <div class="menulabel">${_('Pull Requests')}</div>
240 </a>
240 </a>
241 </li>
241 </li>
242 %endif
242 %endif
243 <li class="${is_active('options')}">
243 <li class="${is_active('options')}">
244 <a class="menulink dropdown">
244 <a class="menulink dropdown">
245 <div class="menulabel">${_('Options')} <div class="show_more"></div></div>
245 <div class="menulabel">${_('Options')} <div class="show_more"></div></div>
246 </a>
246 </a>
247 <ul class="submenu">
247 <ul class="submenu">
248 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
248 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
249 <li><a href="${h.route_path('edit_repo',repo_name=c.repo_name)}">${_('Settings')}</a></li>
249 <li><a href="${h.route_path('edit_repo',repo_name=c.repo_name)}">${_('Settings')}</a></li>
250 %endif
250 %endif
251 %if c.rhodecode_db_repo.fork:
251 %if c.rhodecode_db_repo.fork:
252 <li>
252 <li>
253 <a title="${h.tooltip(_('Compare fork with %s' % c.rhodecode_db_repo.fork.repo_name))}"
253 <a title="${h.tooltip(_('Compare fork with %s' % c.rhodecode_db_repo.fork.repo_name))}"
254 href="${h.route_path('repo_compare',
254 href="${h.route_path('repo_compare',
255 repo_name=c.rhodecode_db_repo.fork.repo_name,
255 repo_name=c.rhodecode_db_repo.fork.repo_name,
256 source_ref_type=c.rhodecode_db_repo.landing_rev[0],
256 source_ref_type=c.rhodecode_db_repo.landing_rev[0],
257 source_ref=c.rhodecode_db_repo.landing_rev[1],
257 source_ref=c.rhodecode_db_repo.landing_rev[1],
258 target_repo=c.repo_name,target_ref_type='branch' if request.GET.get('branch') else c.rhodecode_db_repo.landing_rev[0],
258 target_repo=c.repo_name,target_ref_type='branch' if request.GET.get('branch') else c.rhodecode_db_repo.landing_rev[0],
259 target_ref=request.GET.get('branch') or c.rhodecode_db_repo.landing_rev[1],
259 target_ref=request.GET.get('branch') or c.rhodecode_db_repo.landing_rev[1],
260 _query=dict(merge=1))}"
260 _query=dict(merge=1))}"
261 >
261 >
262 ${_('Compare fork')}
262 ${_('Compare fork')}
263 </a>
263 </a>
264 </li>
264 </li>
265 %endif
265 %endif
266
266
267 <li><a href="${h.route_path('search_repo',repo_name=c.repo_name)}">${_('Search')}</a></li>
267 <li><a href="${h.route_path('search_repo',repo_name=c.repo_name)}">${_('Search')}</a></li>
268
268
269 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking:
269 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking:
270 %if c.rhodecode_db_repo.locked[0]:
270 %if c.rhodecode_db_repo.locked[0]:
271 <li><a class="locking_del" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Unlock')}</a></li>
271 <li><a class="locking_del" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Unlock')}</a></li>
272 %else:
272 %else:
273 <li><a class="locking_add" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Lock')}</a></li>
273 <li><a class="locking_add" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Lock')}</a></li>
274 %endif
274 %endif
275 %endif
275 %endif
276 %if c.rhodecode_user.username != h.DEFAULT_USER:
276 %if c.rhodecode_user.username != h.DEFAULT_USER:
277 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
277 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
278 <li><a href="${h.route_path('repo_fork_new',repo_name=c.repo_name)}">${_('Fork')}</a></li>
278 <li><a href="${h.route_path('repo_fork_new',repo_name=c.repo_name)}">${_('Fork')}</a></li>
279 <li><a href="${h.route_path('pullrequest_new',repo_name=c.repo_name)}">${_('Create Pull Request')}</a></li>
279 <li><a href="${h.route_path('pullrequest_new',repo_name=c.repo_name)}">${_('Create Pull Request')}</a></li>
280 %endif
280 %endif
281 %endif
281 %endif
282 </ul>
282 </ul>
283 </li>
283 </li>
284 </ul>
284 </ul>
285 </div>
285 </div>
286 <div class="clear"></div>
286 <div class="clear"></div>
287 </div>
287 </div>
288 <!--- END CONTEXT BAR -->
288 <!--- END CONTEXT BAR -->
289
289
290 </%def>
290 </%def>
291
291
292 <%def name="usermenu(active=False)">
292 <%def name="usermenu(active=False)">
293 ## USER MENU
293 ## USER MENU
294 <li id="quick_login_li" class="${'active' if active else ''}">
294 <li id="quick_login_li" class="${'active' if active else ''}">
295 <a id="quick_login_link" class="menulink childs">
295 <a id="quick_login_link" class="menulink childs">
296 ${gravatar(c.rhodecode_user.email, 20)}
296 ${gravatar(c.rhodecode_user.email, 20)}
297 <span class="user">
297 <span class="user">
298 %if c.rhodecode_user.username != h.DEFAULT_USER:
298 %if c.rhodecode_user.username != h.DEFAULT_USER:
299 <span class="menu_link_user">${c.rhodecode_user.username}</span><div class="show_more"></div>
299 <span class="menu_link_user">${c.rhodecode_user.username}</span><div class="show_more"></div>
300 %else:
300 %else:
301 <span>${_('Sign in')}</span>
301 <span>${_('Sign in')}</span>
302 %endif
302 %endif
303 </span>
303 </span>
304 </a>
304 </a>
305
305
306 <div class="user-menu submenu">
306 <div class="user-menu submenu">
307 <div id="quick_login">
307 <div id="quick_login">
308 %if c.rhodecode_user.username == h.DEFAULT_USER:
308 %if c.rhodecode_user.username == h.DEFAULT_USER:
309 <h4>${_('Sign in to your account')}</h4>
309 <h4>${_('Sign in to your account')}</h4>
310 ${h.form(h.route_path('login', _query={'came_from': h.current_route_path(request)}), needs_csrf_token=False)}
310 ${h.form(h.route_path('login', _query={'came_from': h.current_route_path(request)}), needs_csrf_token=False)}
311 <div class="form form-vertical">
311 <div class="form form-vertical">
312 <div class="fields">
312 <div class="fields">
313 <div class="field">
313 <div class="field">
314 <div class="label">
314 <div class="label">
315 <label for="username">${_('Username')}:</label>
315 <label for="username">${_('Username')}:</label>
316 </div>
316 </div>
317 <div class="input">
317 <div class="input">
318 ${h.text('username',class_='focus',tabindex=1)}
318 ${h.text('username',class_='focus',tabindex=1)}
319 </div>
319 </div>
320
320
321 </div>
321 </div>
322 <div class="field">
322 <div class="field">
323 <div class="label">
323 <div class="label">
324 <label for="password">${_('Password')}:</label>
324 <label for="password">${_('Password')}:</label>
325 %if h.HasPermissionAny('hg.password_reset.enabled')():
325 %if h.HasPermissionAny('hg.password_reset.enabled')():
326 <span class="forgot_password">${h.link_to(_('(Forgot password?)'),h.route_path('reset_password'), class_='pwd_reset')}</span>
326 <span class="forgot_password">${h.link_to(_('(Forgot password?)'),h.route_path('reset_password'), class_='pwd_reset')}</span>
327 %endif
327 %endif
328 </div>
328 </div>
329 <div class="input">
329 <div class="input">
330 ${h.password('password',class_='focus',tabindex=2)}
330 ${h.password('password',class_='focus',tabindex=2)}
331 </div>
331 </div>
332 </div>
332 </div>
333 <div class="buttons">
333 <div class="buttons">
334 <div class="register">
334 <div class="register">
335 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
335 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
336 ${h.link_to(_("Don't have an account?"),h.route_path('register'))} <br/>
336 ${h.link_to(_("Don't have an account?"),h.route_path('register'))} <br/>
337 %endif
337 %endif
338 ${h.link_to(_("Using external auth? Sign In here."),h.route_path('login'))}
338 ${h.link_to(_("Using external auth? Sign In here."),h.route_path('login'))}
339 </div>
339 </div>
340 <div class="submit">
340 <div class="submit">
341 ${h.submit('sign_in',_('Sign In'),class_="btn btn-small",tabindex=3)}
341 ${h.submit('sign_in',_('Sign In'),class_="btn btn-small",tabindex=3)}
342 </div>
342 </div>
343 </div>
343 </div>
344 </div>
344 </div>
345 </div>
345 </div>
346 ${h.end_form()}
346 ${h.end_form()}
347 %else:
347 %else:
348 <div class="">
348 <div class="">
349 <div class="big_gravatar">${gravatar(c.rhodecode_user.email, 48)}</div>
349 <div class="big_gravatar">${gravatar(c.rhodecode_user.email, 48)}</div>
350 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
350 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
351 <div class="email">${c.rhodecode_user.email}</div>
351 <div class="email">${c.rhodecode_user.email}</div>
352 </div>
352 </div>
353 <div class="">
353 <div class="">
354 <ol class="links">
354 <ol class="links">
355 <li>${h.link_to(_(u'My account'),h.route_path('my_account_profile'))}</li>
355 <li>${h.link_to(_(u'My account'),h.route_path('my_account_profile'))}</li>
356 % if c.rhodecode_user.personal_repo_group:
356 % if c.rhodecode_user.personal_repo_group:
357 <li>${h.link_to(_(u'My personal group'), h.route_path('repo_group_home', repo_group_name=c.rhodecode_user.personal_repo_group.group_name))}</li>
357 <li>${h.link_to(_(u'My personal group'), h.route_path('repo_group_home', repo_group_name=c.rhodecode_user.personal_repo_group.group_name))}</li>
358 % endif
358 % endif
359 <li class="logout">
359 <li class="logout">
360 ${h.secure_form(h.route_path('logout'), request=request)}
360 ${h.secure_form(h.route_path('logout'), request=request)}
361 ${h.submit('log_out', _(u'Sign Out'),class_="btn btn-primary")}
361 ${h.submit('log_out', _(u'Sign Out'),class_="btn btn-primary")}
362 ${h.end_form()}
362 ${h.end_form()}
363 </li>
363 </li>
364 </ol>
364 </ol>
365 </div>
365 </div>
366 %endif
366 %endif
367 </div>
367 </div>
368 </div>
368 </div>
369 %if c.rhodecode_user.username != h.DEFAULT_USER:
369 %if c.rhodecode_user.username != h.DEFAULT_USER:
370 <div class="pill_container">
370 <div class="pill_container">
371 <a class="menu_link_notifications ${'empty' if c.unread_notifications == 0 else ''}" href="${h.route_path('notifications_show_all')}">${c.unread_notifications}</a>
371 <a class="menu_link_notifications ${'empty' if c.unread_notifications == 0 else ''}" href="${h.route_path('notifications_show_all')}">${c.unread_notifications}</a>
372 </div>
372 </div>
373 % endif
373 % endif
374 </li>
374 </li>
375 </%def>
375 </%def>
376
376
377 <%def name="menu_items(active=None)">
377 <%def name="menu_items(active=None)">
378 <%
378 <%
379 def is_active(selected):
379 def is_active(selected):
380 if selected == active:
380 if selected == active:
381 return "active"
381 return "active"
382 return ""
382 return ""
383 %>
383 %>
384 <ul id="quick" class="main_nav navigation horizontal-list">
384 <ul id="quick" class="main_nav navigation horizontal-list">
385 <!-- repo switcher -->
385 <!-- repo switcher -->
386 <li class="${is_active('repositories')} repo_switcher_li has_select2">
386 <li class="${is_active('repositories')} repo_switcher_li has_select2">
387 <input id="repo_switcher" name="repo_switcher" type="hidden">
387 <input id="repo_switcher" name="repo_switcher" type="hidden">
388 </li>
388 </li>
389
389
390 ## ROOT MENU
390 ## ROOT MENU
391 %if c.rhodecode_user.username != h.DEFAULT_USER:
391 %if c.rhodecode_user.username != h.DEFAULT_USER:
392 <li class="${is_active('journal')}">
392 <li class="${is_active('journal')}">
393 <a class="menulink" title="${_('Show activity journal')}" href="${h.route_path('journal')}">
393 <a class="menulink" title="${_('Show activity journal')}" href="${h.route_path('journal')}">
394 <div class="menulabel">${_('Journal')}</div>
394 <div class="menulabel">${_('Journal')}</div>
395 </a>
395 </a>
396 </li>
396 </li>
397 %else:
397 %else:
398 <li class="${is_active('journal')}">
398 <li class="${is_active('journal')}">
399 <a class="menulink" title="${_('Show Public activity journal')}" href="${h.route_path('journal_public')}">
399 <a class="menulink" title="${_('Show Public activity journal')}" href="${h.route_path('journal_public')}">
400 <div class="menulabel">${_('Public journal')}</div>
400 <div class="menulabel">${_('Public journal')}</div>
401 </a>
401 </a>
402 </li>
402 </li>
403 %endif
403 %endif
404 <li class="${is_active('gists')}">
404 <li class="${is_active('gists')}">
405 <a class="menulink childs" title="${_('Show Gists')}" href="${h.route_path('gists_show')}">
405 <a class="menulink childs" title="${_('Show Gists')}" href="${h.route_path('gists_show')}">
406 <div class="menulabel">${_('Gists')}</div>
406 <div class="menulabel">${_('Gists')}</div>
407 </a>
407 </a>
408 </li>
408 </li>
409 <li class="${is_active('search')}">
409 <li class="${is_active('search')}">
410 <a class="menulink" title="${_('Search in repositories you have access to')}" href="${h.route_path('search')}">
410 <a class="menulink" title="${_('Search in repositories you have access to')}" href="${h.route_path('search')}">
411 <div class="menulabel">${_('Search')}</div>
411 <div class="menulabel">${_('Search')}</div>
412 </a>
412 </a>
413 </li>
413 </li>
414 % if h.HasPermissionAll('hg.admin')('access admin main page'):
414 % if h.HasPermissionAll('hg.admin')('access admin main page'):
415 <li class="${is_active('admin')}">
415 <li class="${is_active('admin')}">
416 <a class="menulink childs" title="${_('Admin settings')}" href="#" onclick="return false;">
416 <a class="menulink childs" title="${_('Admin settings')}" href="#" onclick="return false;">
417 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
417 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
418 </a>
418 </a>
419 ${admin_menu()}
419 ${admin_menu()}
420 </li>
420 </li>
421 % elif c.rhodecode_user.repositories_admin or c.rhodecode_user.repository_groups_admin or c.rhodecode_user.user_groups_admin:
421 % elif c.rhodecode_user.repositories_admin or c.rhodecode_user.repository_groups_admin or c.rhodecode_user.user_groups_admin:
422 <li class="${is_active('admin')}">
422 <li class="${is_active('admin')}">
423 <a class="menulink childs" title="${_('Delegated Admin settings')}">
423 <a class="menulink childs" title="${_('Delegated Admin settings')}">
424 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
424 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
425 </a>
425 </a>
426 ${admin_menu_simple(c.rhodecode_user.repositories_admin,
426 ${admin_menu_simple(c.rhodecode_user.repositories_admin,
427 c.rhodecode_user.repository_groups_admin,
427 c.rhodecode_user.repository_groups_admin,
428 c.rhodecode_user.user_groups_admin or h.HasPermissionAny('hg.usergroup.create.true')())}
428 c.rhodecode_user.user_groups_admin or h.HasPermissionAny('hg.usergroup.create.true')())}
429 </li>
429 </li>
430 % endif
430 % endif
431 % if c.debug_style:
431 % if c.debug_style:
432 <li class="${is_active('debug_style')}">
432 <li class="${is_active('debug_style')}">
433 <a class="menulink" title="${_('Style')}" href="${h.route_path('debug_style_home')}">
433 <a class="menulink" title="${_('Style')}" href="${h.route_path('debug_style_home')}">
434 <div class="menulabel">${_('Style')}</div>
434 <div class="menulabel">${_('Style')}</div>
435 </a>
435 </a>
436 </li>
436 </li>
437 % endif
437 % endif
438 ## render extra user menu
438 ## render extra user menu
439 ${usermenu(active=(active=='my_account'))}
439 ${usermenu(active=(active=='my_account'))}
440 </ul>
440 </ul>
441
441
442 <script type="text/javascript">
442 <script type="text/javascript">
443 var visual_show_public_icon = "${c.visual.show_public_icon}" == "True";
443 var visual_show_public_icon = "${c.visual.show_public_icon}" == "True";
444
444
445 /*format the look of items in the list*/
445 /*format the look of items in the list*/
446 var format = function(state, escapeMarkup){
446 var format = function(state, escapeMarkup){
447 if (!state.id){
447 if (!state.id){
448 return state.text; // optgroup
448 return state.text; // optgroup
449 }
449 }
450 var obj_dict = state.obj;
450 var obj_dict = state.obj;
451 var tmpl = '';
451 var tmpl = '';
452
452
453 if(obj_dict && state.type == 'repo'){
453 if(obj_dict && state.type == 'repo'){
454 if(obj_dict['repo_type'] === 'hg'){
454 if(obj_dict['repo_type'] === 'hg'){
455 tmpl += '<i class="icon-hg"></i> ';
455 tmpl += '<i class="icon-hg"></i> ';
456 }
456 }
457 else if(obj_dict['repo_type'] === 'git'){
457 else if(obj_dict['repo_type'] === 'git'){
458 tmpl += '<i class="icon-git"></i> ';
458 tmpl += '<i class="icon-git"></i> ';
459 }
459 }
460 else if(obj_dict['repo_type'] === 'svn'){
460 else if(obj_dict['repo_type'] === 'svn'){
461 tmpl += '<i class="icon-svn"></i> ';
461 tmpl += '<i class="icon-svn"></i> ';
462 }
462 }
463 if(obj_dict['private']){
463 if(obj_dict['private']){
464 tmpl += '<i class="icon-lock" ></i> ';
464 tmpl += '<i class="icon-lock" ></i> ';
465 }
465 }
466 else if(visual_show_public_icon){
466 else if(visual_show_public_icon){
467 tmpl += '<i class="icon-unlock-alt"></i> ';
467 tmpl += '<i class="icon-unlock-alt"></i> ';
468 }
468 }
469 }
469 }
470 if(obj_dict && state.type == 'commit') {
470 if(obj_dict && state.type == 'commit') {
471 tmpl += '<i class="icon-tag"></i>';
471 tmpl += '<i class="icon-tag"></i>';
472 }
472 }
473 if(obj_dict && state.type == 'group'){
473 if(obj_dict && state.type == 'group'){
474 tmpl += '<i class="icon-folder-close"></i> ';
474 tmpl += '<i class="icon-folder-close"></i> ';
475 }
475 }
476 tmpl += escapeMarkup(state.text);
476 tmpl += escapeMarkup(state.text);
477 return tmpl;
477 return tmpl;
478 };
478 };
479
479
480 var formatResult = function(result, container, query, escapeMarkup) {
480 var formatResult = function(result, container, query, escapeMarkup) {
481 return format(result, escapeMarkup);
481 return format(result, escapeMarkup);
482 };
482 };
483
483
484 var formatSelection = function(data, container, escapeMarkup) {
484 var formatSelection = function(data, container, escapeMarkup) {
485 return format(data, escapeMarkup);
485 return format(data, escapeMarkup);
486 };
486 };
487
487
488 $("#repo_switcher").select2({
488 $("#repo_switcher").select2({
489 cachedDataSource: {},
489 cachedDataSource: {},
490 minimumInputLength: 2,
490 minimumInputLength: 2,
491 placeholder: '<div class="menulabel">${_('Go to')} <div class="show_more"></div></div>',
491 placeholder: '<div class="menulabel">${_('Go to')} <div class="show_more"></div></div>',
492 dropdownAutoWidth: true,
492 dropdownAutoWidth: true,
493 formatResult: formatResult,
493 formatResult: formatResult,
494 formatSelection: formatSelection,
494 formatSelection: formatSelection,
495 containerCssClass: "repo-switcher",
495 containerCssClass: "repo-switcher",
496 dropdownCssClass: "repo-switcher-dropdown",
496 dropdownCssClass: "repo-switcher-dropdown",
497 escapeMarkup: function(m){
497 escapeMarkup: function(m){
498 // don't escape our custom placeholder
498 // don't escape our custom placeholder
499 if(m.substr(0,23) == '<div class="menulabel">'){
499 if(m.substr(0,23) == '<div class="menulabel">'){
500 return m;
500 return m;
501 }
501 }
502
502
503 return Select2.util.escapeMarkup(m);
503 return Select2.util.escapeMarkup(m);
504 },
504 },
505 query: $.debounce(250, function(query){
505 query: $.debounce(250, function(query){
506 self = this;
506 self = this;
507 var cacheKey = query.term;
507 var cacheKey = query.term;
508 var cachedData = self.cachedDataSource[cacheKey];
508 var cachedData = self.cachedDataSource[cacheKey];
509
509
510 if (cachedData) {
510 if (cachedData) {
511 query.callback({results: cachedData.results});
511 query.callback({results: cachedData.results});
512 } else {
512 } else {
513 $.ajax({
513 $.ajax({
514 url: pyroutes.url('goto_switcher_data'),
514 url: pyroutes.url('goto_switcher_data'),
515 data: {'query': query.term},
515 data: {'query': query.term},
516 dataType: 'json',
516 dataType: 'json',
517 type: 'GET',
517 type: 'GET',
518 success: function(data) {
518 success: function(data) {
519 self.cachedDataSource[cacheKey] = data;
519 self.cachedDataSource[cacheKey] = data;
520 query.callback({results: data.results});
520 query.callback({results: data.results});
521 },
521 },
522 error: function(data, textStatus, errorThrown) {
522 error: function(data, textStatus, errorThrown) {
523 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
523 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
524 }
524 }
525 })
525 })
526 }
526 }
527 })
527 })
528 });
528 });
529
529
530 $("#repo_switcher").on('select2-selecting', function(e){
530 $("#repo_switcher").on('select2-selecting', function(e){
531 e.preventDefault();
531 e.preventDefault();
532 window.location = e.choice.url;
532 window.location = e.choice.url;
533 });
533 });
534
534
535 </script>
535 </script>
536 <script src="${h.asset('js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script>
536 <script src="${h.asset('js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script>
537 </%def>
537 </%def>
538
538
539 <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
539 <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
540 <div class="modal-dialog">
540 <div class="modal-dialog">
541 <div class="modal-content">
541 <div class="modal-content">
542 <div class="modal-header">
542 <div class="modal-header">
543 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
543 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
544 <h4 class="modal-title" id="myModalLabel">${_('Keyboard shortcuts')}</h4>
544 <h4 class="modal-title" id="myModalLabel">${_('Keyboard shortcuts')}</h4>
545 </div>
545 </div>
546 <div class="modal-body">
546 <div class="modal-body">
547 <div class="block-left">
547 <div class="block-left">
548 <table class="keyboard-mappings">
548 <table class="keyboard-mappings">
549 <tbody>
549 <tbody>
550 <tr>
550 <tr>
551 <th></th>
551 <th></th>
552 <th>${_('Site-wide shortcuts')}</th>
552 <th>${_('Site-wide shortcuts')}</th>
553 </tr>
553 </tr>
554 <%
554 <%
555 elems = [
555 elems = [
556 ('/', 'Open quick search box'),
556 ('/', 'Open quick search box'),
557 ('g h', 'Goto home page'),
557 ('g h', 'Goto home page'),
558 ('g g', 'Goto my private gists page'),
558 ('g g', 'Goto my private gists page'),
559 ('g G', 'Goto my public gists page'),
559 ('g G', 'Goto my public gists page'),
560 ('n r', 'New repository page'),
560 ('n r', 'New repository page'),
561 ('n g', 'New gist page'),
561 ('n g', 'New gist page'),
562 ]
562 ]
563 %>
563 %>
564 %for key, desc in elems:
564 %for key, desc in elems:
565 <tr>
565 <tr>
566 <td class="keys">
566 <td class="keys">
567 <span class="key tag">${key}</span>
567 <span class="key tag">${key}</span>
568 </td>
568 </td>
569 <td>${desc}</td>
569 <td>${desc}</td>
570 </tr>
570 </tr>
571 %endfor
571 %endfor
572 </tbody>
572 </tbody>
573 </table>
573 </table>
574 </div>
574 </div>
575 <div class="block-left">
575 <div class="block-left">
576 <table class="keyboard-mappings">
576 <table class="keyboard-mappings">
577 <tbody>
577 <tbody>
578 <tr>
578 <tr>
579 <th></th>
579 <th></th>
580 <th>${_('Repositories')}</th>
580 <th>${_('Repositories')}</th>
581 </tr>
581 </tr>
582 <%
582 <%
583 elems = [
583 elems = [
584 ('g s', 'Goto summary page'),
584 ('g s', 'Goto summary page'),
585 ('g c', 'Goto changelog page'),
585 ('g c', 'Goto changelog page'),
586 ('g f', 'Goto files page'),
586 ('g f', 'Goto files page'),
587 ('g F', 'Goto files page with file search activated'),
587 ('g F', 'Goto files page with file search activated'),
588 ('g p', 'Goto pull requests page'),
588 ('g p', 'Goto pull requests page'),
589 ('g o', 'Goto repository settings'),
589 ('g o', 'Goto repository settings'),
590 ('g O', 'Goto repository permissions settings'),
590 ('g O', 'Goto repository permissions settings'),
591 ]
591 ]
592 %>
592 %>
593 %for key, desc in elems:
593 %for key, desc in elems:
594 <tr>
594 <tr>
595 <td class="keys">
595 <td class="keys">
596 <span class="key tag">${key}</span>
596 <span class="key tag">${key}</span>
597 </td>
597 </td>
598 <td>${desc}</td>
598 <td>${desc}</td>
599 </tr>
599 </tr>
600 %endfor
600 %endfor
601 </tbody>
601 </tbody>
602 </table>
602 </table>
603 </div>
603 </div>
604 </div>
604 </div>
605 <div class="modal-footer">
605 <div class="modal-footer">
606 </div>
606 </div>
607 </div><!-- /.modal-content -->
607 </div><!-- /.modal-content -->
608 </div><!-- /.modal-dialog -->
608 </div><!-- /.modal-dialog -->
609 </div><!-- /.modal -->
609 </div><!-- /.modal -->
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
This diff has been collapsed as it changes many lines, (631 lines changed) Show them Hide them
General Comments 0
You need to be logged in to leave comments. Login now