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