##// END OF EJS Templates
session: moved session cleanup to pyramid views.
marcink -
r1301:adb14d6f default
parent child Browse files
Show More
@@ -1,43 +1,50 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 from rhodecode.admin.navigation import NavigationRegistry
22 from rhodecode.admin.navigation import NavigationRegistry
23 from rhodecode.config.routing import ADMIN_PREFIX
23 from rhodecode.config.routing import ADMIN_PREFIX
24 from rhodecode.lib.utils2 import str2bool
24 from rhodecode.lib.utils2 import str2bool
25
25
26
26
27 def includeme(config):
27 def includeme(config):
28 settings = config.get_settings()
28 settings = config.get_settings()
29
29
30 # Create admin navigation registry and add it to the pyramid registry.
30 # Create admin navigation registry and add it to the pyramid registry.
31 labs_active = str2bool(settings.get('labs_settings_active', False))
31 labs_active = str2bool(settings.get('labs_settings_active', False))
32 navigation_registry = NavigationRegistry(labs_active=labs_active)
32 navigation_registry = NavigationRegistry(labs_active=labs_active)
33 config.registry.registerUtility(navigation_registry)
33 config.registry.registerUtility(navigation_registry)
34
34
35 config.add_route(
35 config.add_route(
36 name='admin_settings_open_source',
36 name='admin_settings_open_source',
37 pattern=ADMIN_PREFIX + '/settings/open_source')
37 pattern=ADMIN_PREFIX + '/settings/open_source')
38 config.add_route(
38 config.add_route(
39 name='admin_settings_vcs_svn_generate_cfg',
39 name='admin_settings_vcs_svn_generate_cfg',
40 pattern=ADMIN_PREFIX + '/settings/vcs/svn_generate_cfg')
40 pattern=ADMIN_PREFIX + '/settings/vcs/svn_generate_cfg')
41
41
42 config.add_route(
43 name='admin_settings_sessions',
44 pattern=ADMIN_PREFIX + '/settings/sessions')
45 config.add_route(
46 name='admin_settings_sessions_cleanup',
47 pattern=ADMIN_PREFIX + '/settings/sessions/cleanup')
48
42 # Scan module for configuration decorators.
49 # Scan module for configuration decorators.
43 config.scan()
50 config.scan()
@@ -1,128 +1,133 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 import logging
22 import logging
23 import collections
23 import collections
24
24
25 from pylons import url
25 from pylons import url
26 from zope.interface import implementer
26 from zope.interface import implementer
27
27
28 from rhodecode.admin.interfaces import IAdminNavigationRegistry
28 from rhodecode.admin.interfaces import IAdminNavigationRegistry
29 from rhodecode.lib.utils import get_registry
29 from rhodecode.lib.utils import get_registry
30 from rhodecode.translation import _
30 from rhodecode.translation import _
31
31
32
32
33 log = logging.getLogger(__name__)
33 log = logging.getLogger(__name__)
34
34
35 NavListEntry = collections.namedtuple('NavListEntry', ['key', 'name', 'url'])
35 NavListEntry = collections.namedtuple('NavListEntry', ['key', 'name', 'url'])
36
36
37
37
38 class NavEntry(object):
38 class NavEntry(object):
39 """
39 """
40 Represents an entry in the admin navigation.
40 Represents an entry in the admin navigation.
41
41
42 :param key: Unique identifier used to store reference in an OrderedDict.
42 :param key: Unique identifier used to store reference in an OrderedDict.
43 :param name: Display name, usually a translation string.
43 :param name: Display name, usually a translation string.
44 :param view_name: Name of the view, used generate the URL.
44 :param view_name: Name of the view, used generate the URL.
45 :param pyramid: Indicator to use pyramid for URL generation. This should
45 :param pyramid: Indicator to use pyramid for URL generation. This should
46 be removed as soon as we are fully migrated to pyramid.
46 be removed as soon as we are fully migrated to pyramid.
47 """
47 """
48
48
49 def __init__(self, key, name, view_name, pyramid=False):
49 def __init__(self, key, name, view_name, pyramid=False):
50 self.key = key
50 self.key = key
51 self.name = name
51 self.name = name
52 self.view_name = view_name
52 self.view_name = view_name
53 self.pyramid = pyramid
53 self.pyramid = pyramid
54
54
55 def generate_url(self, request):
55 def generate_url(self, request):
56 if self.pyramid:
56 if self.pyramid:
57 if hasattr(request, 'route_path'):
57 if hasattr(request, 'route_path'):
58 return request.route_path(self.view_name)
58 return request.route_path(self.view_name)
59 else:
59 else:
60 # TODO: johbo: Remove this after migrating to pyramid.
60 # TODO: johbo: Remove this after migrating to pyramid.
61 # We need the pyramid request here to generate URLs to pyramid
61 # We need the pyramid request here to generate URLs to pyramid
62 # views from within pylons views.
62 # views from within pylons views.
63 from pyramid.threadlocal import get_current_request
63 from pyramid.threadlocal import get_current_request
64 pyramid_request = get_current_request()
64 pyramid_request = get_current_request()
65 return pyramid_request.route_path(self.view_name)
65 return pyramid_request.route_path(self.view_name)
66 else:
66 else:
67 return url(self.view_name)
67 return url(self.view_name)
68
68
69
69
70 @implementer(IAdminNavigationRegistry)
70 @implementer(IAdminNavigationRegistry)
71 class NavigationRegistry(object):
71 class NavigationRegistry(object):
72
72
73 _base_entries = [
73 _base_entries = [
74 NavEntry('global', _('Global'), 'admin_settings_global'),
74 NavEntry('global', _('Global'), 'admin_settings_global'),
75 NavEntry('vcs', _('VCS'), 'admin_settings_vcs'),
75 NavEntry('vcs', _('VCS'), 'admin_settings_vcs'),
76 NavEntry('visual', _('Visual'), 'admin_settings_visual'),
76 NavEntry('visual', _('Visual'), 'admin_settings_visual'),
77 NavEntry('mapping', _('Remap and Rescan'), 'admin_settings_mapping'),
77 NavEntry('mapping', _('Remap and Rescan'), 'admin_settings_mapping'),
78 NavEntry('issuetracker', _('Issue Tracker'),
78 NavEntry('issuetracker', _('Issue Tracker'),
79 'admin_settings_issuetracker'),
79 'admin_settings_issuetracker'),
80 NavEntry('email', _('Email'), 'admin_settings_email'),
80 NavEntry('email', _('Email'), 'admin_settings_email'),
81 NavEntry('hooks', _('Hooks'), 'admin_settings_hooks'),
81 NavEntry('hooks', _('Hooks'), 'admin_settings_hooks'),
82 NavEntry('search', _('Full Text Search'), 'admin_settings_search'),
82 NavEntry('search', _('Full Text Search'), 'admin_settings_search'),
83
84
83 NavEntry('integrations', _('Integrations'),
85 NavEntry('integrations', _('Integrations'),
84 'global_integrations_home', pyramid=True),
86 'global_integrations_home', pyramid=True),
85 NavEntry('system', _('System Info'), 'admin_settings_system'),
87 NavEntry('system', _('System Info'), 'admin_settings_system'),
86 NavEntry('session', _('User Sessions'), 'admin_settings_sessions'),
88
89
90 NavEntry('session', _('User Sessions'),
91 'admin_settings_sessions', pyramid=True),
87 NavEntry('open_source', _('Open Source Licenses'),
92 NavEntry('open_source', _('Open Source Licenses'),
88 'admin_settings_open_source', pyramid=True),
93 'admin_settings_open_source', pyramid=True),
89
94
90 # TODO: marcink: we disable supervisor now until the supervisor stats
95 # TODO: marcink: we disable supervisor now until the supervisor stats
91 # page is fixed in the nix configuration
96 # page is fixed in the nix configuration
92 # NavEntry('supervisor', _('Supervisor'), 'admin_settings_supervisor'),
97 # NavEntry('supervisor', _('Supervisor'), 'admin_settings_supervisor'),
93 ]
98 ]
94
99
95 _labs_entry = NavEntry('labs', _('Labs'),
100 _labs_entry = NavEntry('labs', _('Labs'),
96 'admin_settings_labs')
101 'admin_settings_labs')
97
102
98 def __init__(self, labs_active=False):
103 def __init__(self, labs_active=False):
99 self._registered_entries = collections.OrderedDict([
104 self._registered_entries = collections.OrderedDict([
100 (item.key, item) for item in self.__class__._base_entries
105 (item.key, item) for item in self.__class__._base_entries
101 ])
106 ])
102
107
103 if labs_active:
108 if labs_active:
104 self.add_entry(self._labs_entry)
109 self.add_entry(self._labs_entry)
105
110
106 def add_entry(self, entry):
111 def add_entry(self, entry):
107 self._registered_entries[entry.key] = entry
112 self._registered_entries[entry.key] = entry
108
113
109 def get_navlist(self, request):
114 def get_navlist(self, request):
110 navlist = [NavListEntry(i.key, i.name, i.generate_url(request))
115 navlist = [NavListEntry(i.key, i.name, i.generate_url(request))
111 for i in self._registered_entries.values()]
116 for i in self._registered_entries.values()]
112 return navlist
117 return navlist
113
118
114
119
115 def navigation_registry(request):
120 def navigation_registry(request):
116 """
121 """
117 Helper that returns the admin navigation registry.
122 Helper that returns the admin navigation registry.
118 """
123 """
119 pyramid_registry = get_registry(request)
124 pyramid_registry = get_registry(request)
120 nav_registry = pyramid_registry.queryUtility(IAdminNavigationRegistry)
125 nav_registry = pyramid_registry.queryUtility(IAdminNavigationRegistry)
121 return nav_registry
126 return nav_registry
122
127
123
128
124 def navigation_list(request):
129 def navigation_list(request):
125 """
130 """
126 Helper that returns the admin navigation as list of NavListEntry objects.
131 Helper that returns the admin navigation as list of NavListEntry objects.
127 """
132 """
128 return navigation_registry(request).get_navlist(request)
133 return navigation_registry(request).get_navlist(request)
@@ -1,82 +1,145 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import collections
21 import collections
22 import logging
22 import logging
23
23
24 from pylons import tmpl_context as c
24 from pylons import tmpl_context as c
25 from pyramid.view import view_config
25 from pyramid.view import view_config
26 from pyramid.httpexceptions import HTTPFound
27
28 from rhodecode.translation import _
29 from rhodecode.svn_support.utils import generate_mod_dav_svn_config
26
30
27 from rhodecode.lib.auth import (
31 from rhodecode.lib.auth import (
28 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
32 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
29 from rhodecode.lib.utils import read_opensource_licenses
33 from rhodecode.lib.utils import read_opensource_licenses
30 from rhodecode.svn_support.utils import generate_mod_dav_svn_config
34 from rhodecode.lib.utils2 import safe_int
31 from rhodecode.translation import _
35 from rhodecode.lib import system_info
36 from rhodecode.lib import user_sessions
37
32
38
33 from .navigation import navigation_list
39 from .navigation import navigation_list
34
40
35
41
36 log = logging.getLogger(__name__)
42 log = logging.getLogger(__name__)
37
43
38
44
39 class AdminSettingsView(object):
45 class AdminSettingsView(object):
40
46
41 def __init__(self, context, request):
47 def __init__(self, context, request):
42 self.request = request
48 self.request = request
43 self.context = context
49 self.context = context
44 self.session = request.session
50 self.session = request.session
45 self._rhodecode_user = request.user
51 self._rhodecode_user = request.user
46
52
47 @LoginRequired()
53 @LoginRequired()
48 @HasPermissionAllDecorator('hg.admin')
54 @HasPermissionAllDecorator('hg.admin')
49 @view_config(
55 @view_config(
50 route_name='admin_settings_open_source', request_method='GET',
56 route_name='admin_settings_open_source', request_method='GET',
51 renderer='rhodecode:templates/admin/settings/settings.mako')
57 renderer='rhodecode:templates/admin/settings/settings.mako')
52 def open_source_licenses(self):
58 def open_source_licenses(self):
53 c.active = 'open_source'
59 c.active = 'open_source'
54 c.navlist = navigation_list(self.request)
60 c.navlist = navigation_list(self.request)
55 c.opensource_licenses = collections.OrderedDict(
61 c.opensource_licenses = collections.OrderedDict(
56 sorted(read_opensource_licenses().items(), key=lambda t: t[0]))
62 sorted(read_opensource_licenses().items(), key=lambda t: t[0]))
57
63
58 return {}
64 return {}
59
65
60 @LoginRequired()
66 @LoginRequired()
61 @CSRFRequired()
67 @CSRFRequired()
62 @HasPermissionAllDecorator('hg.admin')
68 @HasPermissionAllDecorator('hg.admin')
63 @view_config(
69 @view_config(
64 route_name='admin_settings_vcs_svn_generate_cfg',
70 route_name='admin_settings_vcs_svn_generate_cfg',
65 request_method='POST', renderer='json')
71 request_method='POST', renderer='json')
66 def vcs_svn_generate_config(self):
72 def vcs_svn_generate_config(self):
67 try:
73 try:
68 generate_mod_dav_svn_config(self.request.registry)
74 generate_mod_dav_svn_config(self.request.registry)
69 msg = {
75 msg = {
70 'message': _('Apache configuration for Subversion generated.'),
76 'message': _('Apache configuration for Subversion generated.'),
71 'level': 'success',
77 'level': 'success',
72 }
78 }
73 except Exception:
79 except Exception:
74 log.exception(
80 log.exception(
75 'Exception while generating the Apache configuration for Subversion.')
81 'Exception while generating the Apache '
82 'configuration for Subversion.')
76 msg = {
83 msg = {
77 'message': _('Failed to generate the Apache configuration for Subversion.'),
84 'message': _('Failed to generate the Apache configuration for Subversion.'),
78 'level': 'error',
85 'level': 'error',
79 }
86 }
80
87
81 data = {'message': msg}
88 data = {'message': msg}
82 return data
89 return data
90
91 @LoginRequired()
92 @HasPermissionAllDecorator('hg.admin')
93 @view_config(
94 route_name='admin_settings_sessions', request_method='GET',
95 renderer='rhodecode:templates/admin/settings/settings.mako')
96 def settings_sessions(self):
97 c.active = 'sessions'
98 c.navlist = navigation_list(self.request)
99
100 c.cleanup_older_days = 60
101 older_than_seconds = 24 * 60 * 60 * 24 * c.cleanup_older_days
102
103 config = system_info.rhodecode_config().get_value()['value']['config']
104 c.session_model = user_sessions.get_session_handler(
105 config.get('beaker.session.type', 'memory'))(config)
106
107 c.session_conf = c.session_model.config
108 c.session_count = c.session_model.get_count()
109 c.session_expired_count = c.session_model.get_expired_count(
110 older_than_seconds)
111
112 return {}
113
114 @LoginRequired()
115 @HasPermissionAllDecorator('hg.admin')
116 @view_config(
117 route_name='admin_settings_sessions_cleanup', request_method='POST')
118 def settings_sessions_cleanup(self):
119
120 expire_days = safe_int(self.request.params.get('expire_days'))
121
122 if expire_days is None:
123 expire_days = 60
124
125 older_than_seconds = 24 * 60 * 60 * 24 * expire_days
126
127 config = system_info.rhodecode_config().get_value()['value']['config']
128 session_model = user_sessions.get_session_handler(
129 config.get('beaker.session.type', 'memory'))(config)
130
131 try:
132 session_model.clean_sessions(
133 older_than_seconds=older_than_seconds)
134 self.request.session.flash(
135 _('Cleaned up old sessions'), queue='success')
136 except user_sessions.CleanupCommand as msg:
137 self.request.session.flash(msg, category='warning')
138 except Exception as e:
139 log.exception('Failed session cleanup')
140 self.request.session.flash(
141 _('Failed to cleanup up old sessions'), queue='error')
142
143 redirect_to = self.request.resource_path(
144 self.context, route_name='admin_settings_sessions')
145 return HTTPFound(redirect_to)
@@ -1,1179 +1,1173 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 Routes configuration
22 Routes configuration
23
23
24 The more specific and detailed routes should be defined first so they
24 The more specific and detailed routes should be defined first so they
25 may take precedent over the more generic routes. For more information
25 may take precedent over the more generic routes. For more information
26 refer to the routes manual at http://routes.groovie.org/docs/
26 refer to the routes manual at http://routes.groovie.org/docs/
27
27
28 IMPORTANT: if you change any routing here, make sure to take a look at lib/base.py
28 IMPORTANT: if you change any routing here, make sure to take a look at lib/base.py
29 and _route_name variable which uses some of stored naming here to do redirects.
29 and _route_name variable which uses some of stored naming here to do redirects.
30 """
30 """
31 import os
31 import os
32 import re
32 import re
33 from routes import Mapper
33 from routes import Mapper
34
34
35 from rhodecode.config import routing_links
35 from rhodecode.config import routing_links
36
36
37 # prefix for non repository related links needs to be prefixed with `/`
37 # prefix for non repository related links needs to be prefixed with `/`
38 ADMIN_PREFIX = '/_admin'
38 ADMIN_PREFIX = '/_admin'
39 STATIC_FILE_PREFIX = '/_static'
39 STATIC_FILE_PREFIX = '/_static'
40
40
41 # Default requirements for URL parts
41 # Default requirements for URL parts
42 URL_NAME_REQUIREMENTS = {
42 URL_NAME_REQUIREMENTS = {
43 # group name can have a slash in them, but they must not end with a slash
43 # group name can have a slash in them, but they must not end with a slash
44 'group_name': r'.*?[^/]',
44 'group_name': r'.*?[^/]',
45 'repo_group_name': r'.*?[^/]',
45 'repo_group_name': r'.*?[^/]',
46 # repo names can have a slash in them, but they must not end with a slash
46 # repo names can have a slash in them, but they must not end with a slash
47 'repo_name': r'.*?[^/]',
47 'repo_name': r'.*?[^/]',
48 # file path eats up everything at the end
48 # file path eats up everything at the end
49 'f_path': r'.*',
49 'f_path': r'.*',
50 # reference types
50 # reference types
51 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
51 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
52 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
52 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
53 }
53 }
54
54
55
55
56 def add_route_requirements(route_path, requirements):
56 def add_route_requirements(route_path, requirements):
57 """
57 """
58 Adds regex requirements to pyramid routes using a mapping dict
58 Adds regex requirements to pyramid routes using a mapping dict
59
59
60 >>> add_route_requirements('/{action}/{id}', {'id': r'\d+'})
60 >>> add_route_requirements('/{action}/{id}', {'id': r'\d+'})
61 '/{action}/{id:\d+}'
61 '/{action}/{id:\d+}'
62
62
63 """
63 """
64 for key, regex in requirements.items():
64 for key, regex in requirements.items():
65 route_path = route_path.replace('{%s}' % key, '{%s:%s}' % (key, regex))
65 route_path = route_path.replace('{%s}' % key, '{%s:%s}' % (key, regex))
66 return route_path
66 return route_path
67
67
68
68
69 class JSRoutesMapper(Mapper):
69 class JSRoutesMapper(Mapper):
70 """
70 """
71 Wrapper for routes.Mapper to make pyroutes compatible url definitions
71 Wrapper for routes.Mapper to make pyroutes compatible url definitions
72 """
72 """
73 _named_route_regex = re.compile(r'^[a-z-_0-9A-Z]+$')
73 _named_route_regex = re.compile(r'^[a-z-_0-9A-Z]+$')
74 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
74 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
75 def __init__(self, *args, **kw):
75 def __init__(self, *args, **kw):
76 super(JSRoutesMapper, self).__init__(*args, **kw)
76 super(JSRoutesMapper, self).__init__(*args, **kw)
77 self._jsroutes = []
77 self._jsroutes = []
78
78
79 def connect(self, *args, **kw):
79 def connect(self, *args, **kw):
80 """
80 """
81 Wrapper for connect to take an extra argument jsroute=True
81 Wrapper for connect to take an extra argument jsroute=True
82
82
83 :param jsroute: boolean, if True will add the route to the pyroutes list
83 :param jsroute: boolean, if True will add the route to the pyroutes list
84 """
84 """
85 if kw.pop('jsroute', False):
85 if kw.pop('jsroute', False):
86 if not self._named_route_regex.match(args[0]):
86 if not self._named_route_regex.match(args[0]):
87 raise Exception('only named routes can be added to pyroutes')
87 raise Exception('only named routes can be added to pyroutes')
88 self._jsroutes.append(args[0])
88 self._jsroutes.append(args[0])
89
89
90 super(JSRoutesMapper, self).connect(*args, **kw)
90 super(JSRoutesMapper, self).connect(*args, **kw)
91
91
92 def _extract_route_information(self, route):
92 def _extract_route_information(self, route):
93 """
93 """
94 Convert a route into tuple(name, path, args), eg:
94 Convert a route into tuple(name, path, args), eg:
95 ('user_profile', '/profile/%(username)s', ['username'])
95 ('user_profile', '/profile/%(username)s', ['username'])
96 """
96 """
97 routepath = route.routepath
97 routepath = route.routepath
98 def replace(matchobj):
98 def replace(matchobj):
99 if matchobj.group(1):
99 if matchobj.group(1):
100 return "%%(%s)s" % matchobj.group(1).split(':')[0]
100 return "%%(%s)s" % matchobj.group(1).split(':')[0]
101 else:
101 else:
102 return "%%(%s)s" % matchobj.group(2)
102 return "%%(%s)s" % matchobj.group(2)
103
103
104 routepath = self._argument_prog.sub(replace, routepath)
104 routepath = self._argument_prog.sub(replace, routepath)
105 return (
105 return (
106 route.name,
106 route.name,
107 routepath,
107 routepath,
108 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
108 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
109 for arg in self._argument_prog.findall(route.routepath)]
109 for arg in self._argument_prog.findall(route.routepath)]
110 )
110 )
111
111
112 def jsroutes(self):
112 def jsroutes(self):
113 """
113 """
114 Return a list of pyroutes.js compatible routes
114 Return a list of pyroutes.js compatible routes
115 """
115 """
116 for route_name in self._jsroutes:
116 for route_name in self._jsroutes:
117 yield self._extract_route_information(self._routenames[route_name])
117 yield self._extract_route_information(self._routenames[route_name])
118
118
119
119
120 def make_map(config):
120 def make_map(config):
121 """Create, configure and return the routes Mapper"""
121 """Create, configure and return the routes Mapper"""
122 rmap = JSRoutesMapper(directory=config['pylons.paths']['controllers'],
122 rmap = JSRoutesMapper(directory=config['pylons.paths']['controllers'],
123 always_scan=config['debug'])
123 always_scan=config['debug'])
124 rmap.minimization = False
124 rmap.minimization = False
125 rmap.explicit = False
125 rmap.explicit = False
126
126
127 from rhodecode.lib.utils2 import str2bool
127 from rhodecode.lib.utils2 import str2bool
128 from rhodecode.model import repo, repo_group
128 from rhodecode.model import repo, repo_group
129
129
130 def check_repo(environ, match_dict):
130 def check_repo(environ, match_dict):
131 """
131 """
132 check for valid repository for proper 404 handling
132 check for valid repository for proper 404 handling
133
133
134 :param environ:
134 :param environ:
135 :param match_dict:
135 :param match_dict:
136 """
136 """
137 repo_name = match_dict.get('repo_name')
137 repo_name = match_dict.get('repo_name')
138
138
139 if match_dict.get('f_path'):
139 if match_dict.get('f_path'):
140 # fix for multiple initial slashes that causes errors
140 # fix for multiple initial slashes that causes errors
141 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
141 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
142 repo_model = repo.RepoModel()
142 repo_model = repo.RepoModel()
143 by_name_match = repo_model.get_by_repo_name(repo_name)
143 by_name_match = repo_model.get_by_repo_name(repo_name)
144 # if we match quickly from database, short circuit the operation,
144 # if we match quickly from database, short circuit the operation,
145 # and validate repo based on the type.
145 # and validate repo based on the type.
146 if by_name_match:
146 if by_name_match:
147 return True
147 return True
148
148
149 by_id_match = repo_model.get_repo_by_id(repo_name)
149 by_id_match = repo_model.get_repo_by_id(repo_name)
150 if by_id_match:
150 if by_id_match:
151 repo_name = by_id_match.repo_name
151 repo_name = by_id_match.repo_name
152 match_dict['repo_name'] = repo_name
152 match_dict['repo_name'] = repo_name
153 return True
153 return True
154
154
155 return False
155 return False
156
156
157 def check_group(environ, match_dict):
157 def check_group(environ, match_dict):
158 """
158 """
159 check for valid repository group path for proper 404 handling
159 check for valid repository group path for proper 404 handling
160
160
161 :param environ:
161 :param environ:
162 :param match_dict:
162 :param match_dict:
163 """
163 """
164 repo_group_name = match_dict.get('group_name')
164 repo_group_name = match_dict.get('group_name')
165 repo_group_model = repo_group.RepoGroupModel()
165 repo_group_model = repo_group.RepoGroupModel()
166 by_name_match = repo_group_model.get_by_group_name(repo_group_name)
166 by_name_match = repo_group_model.get_by_group_name(repo_group_name)
167 if by_name_match:
167 if by_name_match:
168 return True
168 return True
169
169
170 return False
170 return False
171
171
172 def check_user_group(environ, match_dict):
172 def check_user_group(environ, match_dict):
173 """
173 """
174 check for valid user group for proper 404 handling
174 check for valid user group for proper 404 handling
175
175
176 :param environ:
176 :param environ:
177 :param match_dict:
177 :param match_dict:
178 """
178 """
179 return True
179 return True
180
180
181 def check_int(environ, match_dict):
181 def check_int(environ, match_dict):
182 return match_dict.get('id').isdigit()
182 return match_dict.get('id').isdigit()
183
183
184
184
185 #==========================================================================
185 #==========================================================================
186 # CUSTOM ROUTES HERE
186 # CUSTOM ROUTES HERE
187 #==========================================================================
187 #==========================================================================
188
188
189 # MAIN PAGE
189 # MAIN PAGE
190 rmap.connect('home', '/', controller='home', action='index', jsroute=True)
190 rmap.connect('home', '/', controller='home', action='index', jsroute=True)
191 rmap.connect('goto_switcher_data', '/_goto_data', controller='home',
191 rmap.connect('goto_switcher_data', '/_goto_data', controller='home',
192 action='goto_switcher_data')
192 action='goto_switcher_data')
193 rmap.connect('repo_list_data', '/_repos', controller='home',
193 rmap.connect('repo_list_data', '/_repos', controller='home',
194 action='repo_list_data')
194 action='repo_list_data')
195
195
196 rmap.connect('user_autocomplete_data', '/_users', controller='home',
196 rmap.connect('user_autocomplete_data', '/_users', controller='home',
197 action='user_autocomplete_data', jsroute=True)
197 action='user_autocomplete_data', jsroute=True)
198 rmap.connect('user_group_autocomplete_data', '/_user_groups', controller='home',
198 rmap.connect('user_group_autocomplete_data', '/_user_groups', controller='home',
199 action='user_group_autocomplete_data', jsroute=True)
199 action='user_group_autocomplete_data', jsroute=True)
200
200
201 rmap.connect(
201 rmap.connect(
202 'user_profile', '/_profiles/{username}', controller='users',
202 'user_profile', '/_profiles/{username}', controller='users',
203 action='user_profile')
203 action='user_profile')
204
204
205 # TODO: johbo: Static links, to be replaced by our redirection mechanism
205 # TODO: johbo: Static links, to be replaced by our redirection mechanism
206 rmap.connect('rst_help',
206 rmap.connect('rst_help',
207 'http://docutils.sourceforge.net/docs/user/rst/quickref.html',
207 'http://docutils.sourceforge.net/docs/user/rst/quickref.html',
208 _static=True)
208 _static=True)
209 rmap.connect('markdown_help',
209 rmap.connect('markdown_help',
210 'http://daringfireball.net/projects/markdown/syntax',
210 'http://daringfireball.net/projects/markdown/syntax',
211 _static=True)
211 _static=True)
212 rmap.connect('rhodecode_official', 'https://rhodecode.com', _static=True)
212 rmap.connect('rhodecode_official', 'https://rhodecode.com', _static=True)
213 rmap.connect('rhodecode_support', 'https://rhodecode.com/help/', _static=True)
213 rmap.connect('rhodecode_support', 'https://rhodecode.com/help/', _static=True)
214 rmap.connect('rhodecode_translations', 'https://rhodecode.com/translate/enterprise', _static=True)
214 rmap.connect('rhodecode_translations', 'https://rhodecode.com/translate/enterprise', _static=True)
215 # TODO: anderson - making this a static link since redirect won't play
215 # TODO: anderson - making this a static link since redirect won't play
216 # nice with POST requests
216 # nice with POST requests
217 rmap.connect('enterprise_license_convert_from_old',
217 rmap.connect('enterprise_license_convert_from_old',
218 'https://rhodecode.com/u/license-upgrade',
218 'https://rhodecode.com/u/license-upgrade',
219 _static=True)
219 _static=True)
220
220
221 routing_links.connect_redirection_links(rmap)
221 routing_links.connect_redirection_links(rmap)
222
222
223 rmap.connect('ping', '%s/ping' % (ADMIN_PREFIX,), controller='home', action='ping')
223 rmap.connect('ping', '%s/ping' % (ADMIN_PREFIX,), controller='home', action='ping')
224 rmap.connect('error_test', '%s/error_test' % (ADMIN_PREFIX,), controller='home', action='error_test')
224 rmap.connect('error_test', '%s/error_test' % (ADMIN_PREFIX,), controller='home', action='error_test')
225
225
226 # ADMIN REPOSITORY ROUTES
226 # ADMIN REPOSITORY ROUTES
227 with rmap.submapper(path_prefix=ADMIN_PREFIX,
227 with rmap.submapper(path_prefix=ADMIN_PREFIX,
228 controller='admin/repos') as m:
228 controller='admin/repos') as m:
229 m.connect('repos', '/repos',
229 m.connect('repos', '/repos',
230 action='create', conditions={'method': ['POST']})
230 action='create', conditions={'method': ['POST']})
231 m.connect('repos', '/repos',
231 m.connect('repos', '/repos',
232 action='index', conditions={'method': ['GET']})
232 action='index', conditions={'method': ['GET']})
233 m.connect('new_repo', '/create_repository', jsroute=True,
233 m.connect('new_repo', '/create_repository', jsroute=True,
234 action='create_repository', conditions={'method': ['GET']})
234 action='create_repository', conditions={'method': ['GET']})
235 m.connect('/repos/{repo_name}',
235 m.connect('/repos/{repo_name}',
236 action='update', conditions={'method': ['PUT'],
236 action='update', conditions={'method': ['PUT'],
237 'function': check_repo},
237 'function': check_repo},
238 requirements=URL_NAME_REQUIREMENTS)
238 requirements=URL_NAME_REQUIREMENTS)
239 m.connect('delete_repo', '/repos/{repo_name}',
239 m.connect('delete_repo', '/repos/{repo_name}',
240 action='delete', conditions={'method': ['DELETE']},
240 action='delete', conditions={'method': ['DELETE']},
241 requirements=URL_NAME_REQUIREMENTS)
241 requirements=URL_NAME_REQUIREMENTS)
242 m.connect('repo', '/repos/{repo_name}',
242 m.connect('repo', '/repos/{repo_name}',
243 action='show', conditions={'method': ['GET'],
243 action='show', conditions={'method': ['GET'],
244 'function': check_repo},
244 'function': check_repo},
245 requirements=URL_NAME_REQUIREMENTS)
245 requirements=URL_NAME_REQUIREMENTS)
246
246
247 # ADMIN REPOSITORY GROUPS ROUTES
247 # ADMIN REPOSITORY GROUPS ROUTES
248 with rmap.submapper(path_prefix=ADMIN_PREFIX,
248 with rmap.submapper(path_prefix=ADMIN_PREFIX,
249 controller='admin/repo_groups') as m:
249 controller='admin/repo_groups') as m:
250 m.connect('repo_groups', '/repo_groups',
250 m.connect('repo_groups', '/repo_groups',
251 action='create', conditions={'method': ['POST']})
251 action='create', conditions={'method': ['POST']})
252 m.connect('repo_groups', '/repo_groups',
252 m.connect('repo_groups', '/repo_groups',
253 action='index', conditions={'method': ['GET']})
253 action='index', conditions={'method': ['GET']})
254 m.connect('new_repo_group', '/repo_groups/new',
254 m.connect('new_repo_group', '/repo_groups/new',
255 action='new', conditions={'method': ['GET']})
255 action='new', conditions={'method': ['GET']})
256 m.connect('update_repo_group', '/repo_groups/{group_name}',
256 m.connect('update_repo_group', '/repo_groups/{group_name}',
257 action='update', conditions={'method': ['PUT'],
257 action='update', conditions={'method': ['PUT'],
258 'function': check_group},
258 'function': check_group},
259 requirements=URL_NAME_REQUIREMENTS)
259 requirements=URL_NAME_REQUIREMENTS)
260
260
261 # EXTRAS REPO GROUP ROUTES
261 # EXTRAS REPO GROUP ROUTES
262 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
262 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
263 action='edit',
263 action='edit',
264 conditions={'method': ['GET'], 'function': check_group},
264 conditions={'method': ['GET'], 'function': check_group},
265 requirements=URL_NAME_REQUIREMENTS)
265 requirements=URL_NAME_REQUIREMENTS)
266 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
266 m.connect('edit_repo_group', '/repo_groups/{group_name}/edit',
267 action='edit',
267 action='edit',
268 conditions={'method': ['PUT'], 'function': check_group},
268 conditions={'method': ['PUT'], 'function': check_group},
269 requirements=URL_NAME_REQUIREMENTS)
269 requirements=URL_NAME_REQUIREMENTS)
270
270
271 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
271 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
272 action='edit_repo_group_advanced',
272 action='edit_repo_group_advanced',
273 conditions={'method': ['GET'], 'function': check_group},
273 conditions={'method': ['GET'], 'function': check_group},
274 requirements=URL_NAME_REQUIREMENTS)
274 requirements=URL_NAME_REQUIREMENTS)
275 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
275 m.connect('edit_repo_group_advanced', '/repo_groups/{group_name}/edit/advanced',
276 action='edit_repo_group_advanced',
276 action='edit_repo_group_advanced',
277 conditions={'method': ['PUT'], 'function': check_group},
277 conditions={'method': ['PUT'], 'function': check_group},
278 requirements=URL_NAME_REQUIREMENTS)
278 requirements=URL_NAME_REQUIREMENTS)
279
279
280 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
280 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
281 action='edit_repo_group_perms',
281 action='edit_repo_group_perms',
282 conditions={'method': ['GET'], 'function': check_group},
282 conditions={'method': ['GET'], 'function': check_group},
283 requirements=URL_NAME_REQUIREMENTS)
283 requirements=URL_NAME_REQUIREMENTS)
284 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
284 m.connect('edit_repo_group_perms', '/repo_groups/{group_name}/edit/permissions',
285 action='update_perms',
285 action='update_perms',
286 conditions={'method': ['PUT'], 'function': check_group},
286 conditions={'method': ['PUT'], 'function': check_group},
287 requirements=URL_NAME_REQUIREMENTS)
287 requirements=URL_NAME_REQUIREMENTS)
288
288
289 m.connect('delete_repo_group', '/repo_groups/{group_name}',
289 m.connect('delete_repo_group', '/repo_groups/{group_name}',
290 action='delete', conditions={'method': ['DELETE'],
290 action='delete', conditions={'method': ['DELETE'],
291 'function': check_group},
291 'function': check_group},
292 requirements=URL_NAME_REQUIREMENTS)
292 requirements=URL_NAME_REQUIREMENTS)
293
293
294 # ADMIN USER ROUTES
294 # ADMIN USER ROUTES
295 with rmap.submapper(path_prefix=ADMIN_PREFIX,
295 with rmap.submapper(path_prefix=ADMIN_PREFIX,
296 controller='admin/users') as m:
296 controller='admin/users') as m:
297 m.connect('users', '/users',
297 m.connect('users', '/users',
298 action='create', conditions={'method': ['POST']})
298 action='create', conditions={'method': ['POST']})
299 m.connect('users', '/users',
299 m.connect('users', '/users',
300 action='index', conditions={'method': ['GET']})
300 action='index', conditions={'method': ['GET']})
301 m.connect('new_user', '/users/new',
301 m.connect('new_user', '/users/new',
302 action='new', conditions={'method': ['GET']})
302 action='new', conditions={'method': ['GET']})
303 m.connect('update_user', '/users/{user_id}',
303 m.connect('update_user', '/users/{user_id}',
304 action='update', conditions={'method': ['PUT']})
304 action='update', conditions={'method': ['PUT']})
305 m.connect('delete_user', '/users/{user_id}',
305 m.connect('delete_user', '/users/{user_id}',
306 action='delete', conditions={'method': ['DELETE']})
306 action='delete', conditions={'method': ['DELETE']})
307 m.connect('edit_user', '/users/{user_id}/edit',
307 m.connect('edit_user', '/users/{user_id}/edit',
308 action='edit', conditions={'method': ['GET']}, jsroute=True)
308 action='edit', conditions={'method': ['GET']}, jsroute=True)
309 m.connect('user', '/users/{user_id}',
309 m.connect('user', '/users/{user_id}',
310 action='show', conditions={'method': ['GET']})
310 action='show', conditions={'method': ['GET']})
311 m.connect('force_password_reset_user', '/users/{user_id}/password_reset',
311 m.connect('force_password_reset_user', '/users/{user_id}/password_reset',
312 action='reset_password', conditions={'method': ['POST']})
312 action='reset_password', conditions={'method': ['POST']})
313 m.connect('create_personal_repo_group', '/users/{user_id}/create_repo_group',
313 m.connect('create_personal_repo_group', '/users/{user_id}/create_repo_group',
314 action='create_personal_repo_group', conditions={'method': ['POST']})
314 action='create_personal_repo_group', conditions={'method': ['POST']})
315
315
316 # EXTRAS USER ROUTES
316 # EXTRAS USER ROUTES
317 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
317 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
318 action='edit_advanced', conditions={'method': ['GET']})
318 action='edit_advanced', conditions={'method': ['GET']})
319 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
319 m.connect('edit_user_advanced', '/users/{user_id}/edit/advanced',
320 action='update_advanced', conditions={'method': ['PUT']})
320 action='update_advanced', conditions={'method': ['PUT']})
321
321
322 m.connect('edit_user_auth_tokens', '/users/{user_id}/edit/auth_tokens',
322 m.connect('edit_user_auth_tokens', '/users/{user_id}/edit/auth_tokens',
323 action='edit_auth_tokens', conditions={'method': ['GET']})
323 action='edit_auth_tokens', conditions={'method': ['GET']})
324 m.connect('edit_user_auth_tokens', '/users/{user_id}/edit/auth_tokens',
324 m.connect('edit_user_auth_tokens', '/users/{user_id}/edit/auth_tokens',
325 action='add_auth_token', conditions={'method': ['PUT']})
325 action='add_auth_token', conditions={'method': ['PUT']})
326 m.connect('edit_user_auth_tokens', '/users/{user_id}/edit/auth_tokens',
326 m.connect('edit_user_auth_tokens', '/users/{user_id}/edit/auth_tokens',
327 action='delete_auth_token', conditions={'method': ['DELETE']})
327 action='delete_auth_token', conditions={'method': ['DELETE']})
328
328
329 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
329 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
330 action='edit_global_perms', conditions={'method': ['GET']})
330 action='edit_global_perms', conditions={'method': ['GET']})
331 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
331 m.connect('edit_user_global_perms', '/users/{user_id}/edit/global_permissions',
332 action='update_global_perms', conditions={'method': ['PUT']})
332 action='update_global_perms', conditions={'method': ['PUT']})
333
333
334 m.connect('edit_user_perms_summary', '/users/{user_id}/edit/permissions_summary',
334 m.connect('edit_user_perms_summary', '/users/{user_id}/edit/permissions_summary',
335 action='edit_perms_summary', conditions={'method': ['GET']})
335 action='edit_perms_summary', conditions={'method': ['GET']})
336
336
337 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
337 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
338 action='edit_emails', conditions={'method': ['GET']})
338 action='edit_emails', conditions={'method': ['GET']})
339 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
339 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
340 action='add_email', conditions={'method': ['PUT']})
340 action='add_email', conditions={'method': ['PUT']})
341 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
341 m.connect('edit_user_emails', '/users/{user_id}/edit/emails',
342 action='delete_email', conditions={'method': ['DELETE']})
342 action='delete_email', conditions={'method': ['DELETE']})
343
343
344 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
344 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
345 action='edit_ips', conditions={'method': ['GET']})
345 action='edit_ips', conditions={'method': ['GET']})
346 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
346 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
347 action='add_ip', conditions={'method': ['PUT']})
347 action='add_ip', conditions={'method': ['PUT']})
348 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
348 m.connect('edit_user_ips', '/users/{user_id}/edit/ips',
349 action='delete_ip', conditions={'method': ['DELETE']})
349 action='delete_ip', conditions={'method': ['DELETE']})
350
350
351 # ADMIN USER GROUPS REST ROUTES
351 # ADMIN USER GROUPS REST ROUTES
352 with rmap.submapper(path_prefix=ADMIN_PREFIX,
352 with rmap.submapper(path_prefix=ADMIN_PREFIX,
353 controller='admin/user_groups') as m:
353 controller='admin/user_groups') as m:
354 m.connect('users_groups', '/user_groups',
354 m.connect('users_groups', '/user_groups',
355 action='create', conditions={'method': ['POST']})
355 action='create', conditions={'method': ['POST']})
356 m.connect('users_groups', '/user_groups',
356 m.connect('users_groups', '/user_groups',
357 action='index', conditions={'method': ['GET']})
357 action='index', conditions={'method': ['GET']})
358 m.connect('new_users_group', '/user_groups/new',
358 m.connect('new_users_group', '/user_groups/new',
359 action='new', conditions={'method': ['GET']})
359 action='new', conditions={'method': ['GET']})
360 m.connect('update_users_group', '/user_groups/{user_group_id}',
360 m.connect('update_users_group', '/user_groups/{user_group_id}',
361 action='update', conditions={'method': ['PUT']})
361 action='update', conditions={'method': ['PUT']})
362 m.connect('delete_users_group', '/user_groups/{user_group_id}',
362 m.connect('delete_users_group', '/user_groups/{user_group_id}',
363 action='delete', conditions={'method': ['DELETE']})
363 action='delete', conditions={'method': ['DELETE']})
364 m.connect('edit_users_group', '/user_groups/{user_group_id}/edit',
364 m.connect('edit_users_group', '/user_groups/{user_group_id}/edit',
365 action='edit', conditions={'method': ['GET']},
365 action='edit', conditions={'method': ['GET']},
366 function=check_user_group)
366 function=check_user_group)
367
367
368 # EXTRAS USER GROUP ROUTES
368 # EXTRAS USER GROUP ROUTES
369 m.connect('edit_user_group_global_perms',
369 m.connect('edit_user_group_global_perms',
370 '/user_groups/{user_group_id}/edit/global_permissions',
370 '/user_groups/{user_group_id}/edit/global_permissions',
371 action='edit_global_perms', conditions={'method': ['GET']})
371 action='edit_global_perms', conditions={'method': ['GET']})
372 m.connect('edit_user_group_global_perms',
372 m.connect('edit_user_group_global_perms',
373 '/user_groups/{user_group_id}/edit/global_permissions',
373 '/user_groups/{user_group_id}/edit/global_permissions',
374 action='update_global_perms', conditions={'method': ['PUT']})
374 action='update_global_perms', conditions={'method': ['PUT']})
375 m.connect('edit_user_group_perms_summary',
375 m.connect('edit_user_group_perms_summary',
376 '/user_groups/{user_group_id}/edit/permissions_summary',
376 '/user_groups/{user_group_id}/edit/permissions_summary',
377 action='edit_perms_summary', conditions={'method': ['GET']})
377 action='edit_perms_summary', conditions={'method': ['GET']})
378
378
379 m.connect('edit_user_group_perms',
379 m.connect('edit_user_group_perms',
380 '/user_groups/{user_group_id}/edit/permissions',
380 '/user_groups/{user_group_id}/edit/permissions',
381 action='edit_perms', conditions={'method': ['GET']})
381 action='edit_perms', conditions={'method': ['GET']})
382 m.connect('edit_user_group_perms',
382 m.connect('edit_user_group_perms',
383 '/user_groups/{user_group_id}/edit/permissions',
383 '/user_groups/{user_group_id}/edit/permissions',
384 action='update_perms', conditions={'method': ['PUT']})
384 action='update_perms', conditions={'method': ['PUT']})
385
385
386 m.connect('edit_user_group_advanced',
386 m.connect('edit_user_group_advanced',
387 '/user_groups/{user_group_id}/edit/advanced',
387 '/user_groups/{user_group_id}/edit/advanced',
388 action='edit_advanced', conditions={'method': ['GET']})
388 action='edit_advanced', conditions={'method': ['GET']})
389
389
390 m.connect('edit_user_group_members',
390 m.connect('edit_user_group_members',
391 '/user_groups/{user_group_id}/edit/members', jsroute=True,
391 '/user_groups/{user_group_id}/edit/members', jsroute=True,
392 action='user_group_members', conditions={'method': ['GET']})
392 action='user_group_members', conditions={'method': ['GET']})
393
393
394 # ADMIN PERMISSIONS ROUTES
394 # ADMIN PERMISSIONS ROUTES
395 with rmap.submapper(path_prefix=ADMIN_PREFIX,
395 with rmap.submapper(path_prefix=ADMIN_PREFIX,
396 controller='admin/permissions') as m:
396 controller='admin/permissions') as m:
397 m.connect('admin_permissions_application', '/permissions/application',
397 m.connect('admin_permissions_application', '/permissions/application',
398 action='permission_application_update', conditions={'method': ['POST']})
398 action='permission_application_update', conditions={'method': ['POST']})
399 m.connect('admin_permissions_application', '/permissions/application',
399 m.connect('admin_permissions_application', '/permissions/application',
400 action='permission_application', conditions={'method': ['GET']})
400 action='permission_application', conditions={'method': ['GET']})
401
401
402 m.connect('admin_permissions_global', '/permissions/global',
402 m.connect('admin_permissions_global', '/permissions/global',
403 action='permission_global_update', conditions={'method': ['POST']})
403 action='permission_global_update', conditions={'method': ['POST']})
404 m.connect('admin_permissions_global', '/permissions/global',
404 m.connect('admin_permissions_global', '/permissions/global',
405 action='permission_global', conditions={'method': ['GET']})
405 action='permission_global', conditions={'method': ['GET']})
406
406
407 m.connect('admin_permissions_object', '/permissions/object',
407 m.connect('admin_permissions_object', '/permissions/object',
408 action='permission_objects_update', conditions={'method': ['POST']})
408 action='permission_objects_update', conditions={'method': ['POST']})
409 m.connect('admin_permissions_object', '/permissions/object',
409 m.connect('admin_permissions_object', '/permissions/object',
410 action='permission_objects', conditions={'method': ['GET']})
410 action='permission_objects', conditions={'method': ['GET']})
411
411
412 m.connect('admin_permissions_ips', '/permissions/ips',
412 m.connect('admin_permissions_ips', '/permissions/ips',
413 action='permission_ips', conditions={'method': ['POST']})
413 action='permission_ips', conditions={'method': ['POST']})
414 m.connect('admin_permissions_ips', '/permissions/ips',
414 m.connect('admin_permissions_ips', '/permissions/ips',
415 action='permission_ips', conditions={'method': ['GET']})
415 action='permission_ips', conditions={'method': ['GET']})
416
416
417 m.connect('admin_permissions_overview', '/permissions/overview',
417 m.connect('admin_permissions_overview', '/permissions/overview',
418 action='permission_perms', conditions={'method': ['GET']})
418 action='permission_perms', conditions={'method': ['GET']})
419
419
420 # ADMIN DEFAULTS REST ROUTES
420 # ADMIN DEFAULTS REST ROUTES
421 with rmap.submapper(path_prefix=ADMIN_PREFIX,
421 with rmap.submapper(path_prefix=ADMIN_PREFIX,
422 controller='admin/defaults') as m:
422 controller='admin/defaults') as m:
423 m.connect('admin_defaults_repositories', '/defaults/repositories',
423 m.connect('admin_defaults_repositories', '/defaults/repositories',
424 action='update_repository_defaults', conditions={'method': ['POST']})
424 action='update_repository_defaults', conditions={'method': ['POST']})
425 m.connect('admin_defaults_repositories', '/defaults/repositories',
425 m.connect('admin_defaults_repositories', '/defaults/repositories',
426 action='index', conditions={'method': ['GET']})
426 action='index', conditions={'method': ['GET']})
427
427
428 # ADMIN DEBUG STYLE ROUTES
428 # ADMIN DEBUG STYLE ROUTES
429 if str2bool(config.get('debug_style')):
429 if str2bool(config.get('debug_style')):
430 with rmap.submapper(path_prefix=ADMIN_PREFIX + '/debug_style',
430 with rmap.submapper(path_prefix=ADMIN_PREFIX + '/debug_style',
431 controller='debug_style') as m:
431 controller='debug_style') as m:
432 m.connect('debug_style_home', '',
432 m.connect('debug_style_home', '',
433 action='index', conditions={'method': ['GET']})
433 action='index', conditions={'method': ['GET']})
434 m.connect('debug_style_template', '/t/{t_path}',
434 m.connect('debug_style_template', '/t/{t_path}',
435 action='template', conditions={'method': ['GET']})
435 action='template', conditions={'method': ['GET']})
436
436
437 # ADMIN SETTINGS ROUTES
437 # ADMIN SETTINGS ROUTES
438 with rmap.submapper(path_prefix=ADMIN_PREFIX,
438 with rmap.submapper(path_prefix=ADMIN_PREFIX,
439 controller='admin/settings') as m:
439 controller='admin/settings') as m:
440
440
441 # default
441 # default
442 m.connect('admin_settings', '/settings',
442 m.connect('admin_settings', '/settings',
443 action='settings_global_update',
443 action='settings_global_update',
444 conditions={'method': ['POST']})
444 conditions={'method': ['POST']})
445 m.connect('admin_settings', '/settings',
445 m.connect('admin_settings', '/settings',
446 action='settings_global', conditions={'method': ['GET']})
446 action='settings_global', conditions={'method': ['GET']})
447
447
448 m.connect('admin_settings_vcs', '/settings/vcs',
448 m.connect('admin_settings_vcs', '/settings/vcs',
449 action='settings_vcs_update',
449 action='settings_vcs_update',
450 conditions={'method': ['POST']})
450 conditions={'method': ['POST']})
451 m.connect('admin_settings_vcs', '/settings/vcs',
451 m.connect('admin_settings_vcs', '/settings/vcs',
452 action='settings_vcs',
452 action='settings_vcs',
453 conditions={'method': ['GET']})
453 conditions={'method': ['GET']})
454 m.connect('admin_settings_vcs', '/settings/vcs',
454 m.connect('admin_settings_vcs', '/settings/vcs',
455 action='delete_svn_pattern',
455 action='delete_svn_pattern',
456 conditions={'method': ['DELETE']})
456 conditions={'method': ['DELETE']})
457
457
458 m.connect('admin_settings_mapping', '/settings/mapping',
458 m.connect('admin_settings_mapping', '/settings/mapping',
459 action='settings_mapping_update',
459 action='settings_mapping_update',
460 conditions={'method': ['POST']})
460 conditions={'method': ['POST']})
461 m.connect('admin_settings_mapping', '/settings/mapping',
461 m.connect('admin_settings_mapping', '/settings/mapping',
462 action='settings_mapping', conditions={'method': ['GET']})
462 action='settings_mapping', conditions={'method': ['GET']})
463
463
464 m.connect('admin_settings_global', '/settings/global',
464 m.connect('admin_settings_global', '/settings/global',
465 action='settings_global_update',
465 action='settings_global_update',
466 conditions={'method': ['POST']})
466 conditions={'method': ['POST']})
467 m.connect('admin_settings_global', '/settings/global',
467 m.connect('admin_settings_global', '/settings/global',
468 action='settings_global', conditions={'method': ['GET']})
468 action='settings_global', conditions={'method': ['GET']})
469
469
470 m.connect('admin_settings_visual', '/settings/visual',
470 m.connect('admin_settings_visual', '/settings/visual',
471 action='settings_visual_update',
471 action='settings_visual_update',
472 conditions={'method': ['POST']})
472 conditions={'method': ['POST']})
473 m.connect('admin_settings_visual', '/settings/visual',
473 m.connect('admin_settings_visual', '/settings/visual',
474 action='settings_visual', conditions={'method': ['GET']})
474 action='settings_visual', conditions={'method': ['GET']})
475
475
476 m.connect('admin_settings_issuetracker',
476 m.connect('admin_settings_issuetracker',
477 '/settings/issue-tracker', action='settings_issuetracker',
477 '/settings/issue-tracker', action='settings_issuetracker',
478 conditions={'method': ['GET']})
478 conditions={'method': ['GET']})
479 m.connect('admin_settings_issuetracker_save',
479 m.connect('admin_settings_issuetracker_save',
480 '/settings/issue-tracker/save',
480 '/settings/issue-tracker/save',
481 action='settings_issuetracker_save',
481 action='settings_issuetracker_save',
482 conditions={'method': ['POST']})
482 conditions={'method': ['POST']})
483 m.connect('admin_issuetracker_test', '/settings/issue-tracker/test',
483 m.connect('admin_issuetracker_test', '/settings/issue-tracker/test',
484 action='settings_issuetracker_test',
484 action='settings_issuetracker_test',
485 conditions={'method': ['POST']})
485 conditions={'method': ['POST']})
486 m.connect('admin_issuetracker_delete',
486 m.connect('admin_issuetracker_delete',
487 '/settings/issue-tracker/delete',
487 '/settings/issue-tracker/delete',
488 action='settings_issuetracker_delete',
488 action='settings_issuetracker_delete',
489 conditions={'method': ['DELETE']})
489 conditions={'method': ['DELETE']})
490
490
491 m.connect('admin_settings_email', '/settings/email',
491 m.connect('admin_settings_email', '/settings/email',
492 action='settings_email_update',
492 action='settings_email_update',
493 conditions={'method': ['POST']})
493 conditions={'method': ['POST']})
494 m.connect('admin_settings_email', '/settings/email',
494 m.connect('admin_settings_email', '/settings/email',
495 action='settings_email', conditions={'method': ['GET']})
495 action='settings_email', conditions={'method': ['GET']})
496
496
497 m.connect('admin_settings_hooks', '/settings/hooks',
497 m.connect('admin_settings_hooks', '/settings/hooks',
498 action='settings_hooks_update',
498 action='settings_hooks_update',
499 conditions={'method': ['POST', 'DELETE']})
499 conditions={'method': ['POST', 'DELETE']})
500 m.connect('admin_settings_hooks', '/settings/hooks',
500 m.connect('admin_settings_hooks', '/settings/hooks',
501 action='settings_hooks', conditions={'method': ['GET']})
501 action='settings_hooks', conditions={'method': ['GET']})
502
502
503 m.connect('admin_settings_search', '/settings/search',
503 m.connect('admin_settings_search', '/settings/search',
504 action='settings_search', conditions={'method': ['GET']})
504 action='settings_search', conditions={'method': ['GET']})
505
505
506 m.connect('admin_settings_system', '/settings/system',
506 m.connect('admin_settings_system', '/settings/system',
507 action='settings_system', conditions={'method': ['GET']})
507 action='settings_system', conditions={'method': ['GET']})
508
508
509 m.connect('admin_settings_system_update', '/settings/system/updates',
509 m.connect('admin_settings_system_update', '/settings/system/updates',
510 action='settings_system_update', conditions={'method': ['GET']})
510 action='settings_system_update', conditions={'method': ['GET']})
511
511
512 m.connect('admin_settings_sessions', '/settings/sessions',
513 action='settings_sessions', conditions={'method': ['GET']})
514
515 m.connect('admin_settings_sessions_cleanup', '/settings/sessions/cleanup',
516 action='settings_sessions_cleanup', conditions={'method': ['POST']})
517
518 m.connect('admin_settings_supervisor', '/settings/supervisor',
512 m.connect('admin_settings_supervisor', '/settings/supervisor',
519 action='settings_supervisor', conditions={'method': ['GET']})
513 action='settings_supervisor', conditions={'method': ['GET']})
520 m.connect('admin_settings_supervisor_log', '/settings/supervisor/{procid}/log',
514 m.connect('admin_settings_supervisor_log', '/settings/supervisor/{procid}/log',
521 action='settings_supervisor_log', conditions={'method': ['GET']})
515 action='settings_supervisor_log', conditions={'method': ['GET']})
522
516
523 m.connect('admin_settings_labs', '/settings/labs',
517 m.connect('admin_settings_labs', '/settings/labs',
524 action='settings_labs_update',
518 action='settings_labs_update',
525 conditions={'method': ['POST']})
519 conditions={'method': ['POST']})
526 m.connect('admin_settings_labs', '/settings/labs',
520 m.connect('admin_settings_labs', '/settings/labs',
527 action='settings_labs', conditions={'method': ['GET']})
521 action='settings_labs', conditions={'method': ['GET']})
528
522
529 # ADMIN MY ACCOUNT
523 # ADMIN MY ACCOUNT
530 with rmap.submapper(path_prefix=ADMIN_PREFIX,
524 with rmap.submapper(path_prefix=ADMIN_PREFIX,
531 controller='admin/my_account') as m:
525 controller='admin/my_account') as m:
532
526
533 m.connect('my_account', '/my_account',
527 m.connect('my_account', '/my_account',
534 action='my_account', conditions={'method': ['GET']})
528 action='my_account', conditions={'method': ['GET']})
535 m.connect('my_account_edit', '/my_account/edit',
529 m.connect('my_account_edit', '/my_account/edit',
536 action='my_account_edit', conditions={'method': ['GET']})
530 action='my_account_edit', conditions={'method': ['GET']})
537 m.connect('my_account', '/my_account',
531 m.connect('my_account', '/my_account',
538 action='my_account_update', conditions={'method': ['POST']})
532 action='my_account_update', conditions={'method': ['POST']})
539
533
540 m.connect('my_account_password', '/my_account/password',
534 m.connect('my_account_password', '/my_account/password',
541 action='my_account_password', conditions={'method': ['GET', 'POST']})
535 action='my_account_password', conditions={'method': ['GET', 'POST']})
542
536
543 m.connect('my_account_repos', '/my_account/repos',
537 m.connect('my_account_repos', '/my_account/repos',
544 action='my_account_repos', conditions={'method': ['GET']})
538 action='my_account_repos', conditions={'method': ['GET']})
545
539
546 m.connect('my_account_watched', '/my_account/watched',
540 m.connect('my_account_watched', '/my_account/watched',
547 action='my_account_watched', conditions={'method': ['GET']})
541 action='my_account_watched', conditions={'method': ['GET']})
548
542
549 m.connect('my_account_pullrequests', '/my_account/pull_requests',
543 m.connect('my_account_pullrequests', '/my_account/pull_requests',
550 action='my_account_pullrequests', conditions={'method': ['GET']})
544 action='my_account_pullrequests', conditions={'method': ['GET']})
551
545
552 m.connect('my_account_perms', '/my_account/perms',
546 m.connect('my_account_perms', '/my_account/perms',
553 action='my_account_perms', conditions={'method': ['GET']})
547 action='my_account_perms', conditions={'method': ['GET']})
554
548
555 m.connect('my_account_emails', '/my_account/emails',
549 m.connect('my_account_emails', '/my_account/emails',
556 action='my_account_emails', conditions={'method': ['GET']})
550 action='my_account_emails', conditions={'method': ['GET']})
557 m.connect('my_account_emails', '/my_account/emails',
551 m.connect('my_account_emails', '/my_account/emails',
558 action='my_account_emails_add', conditions={'method': ['POST']})
552 action='my_account_emails_add', conditions={'method': ['POST']})
559 m.connect('my_account_emails', '/my_account/emails',
553 m.connect('my_account_emails', '/my_account/emails',
560 action='my_account_emails_delete', conditions={'method': ['DELETE']})
554 action='my_account_emails_delete', conditions={'method': ['DELETE']})
561
555
562 m.connect('my_account_auth_tokens', '/my_account/auth_tokens',
556 m.connect('my_account_auth_tokens', '/my_account/auth_tokens',
563 action='my_account_auth_tokens', conditions={'method': ['GET']})
557 action='my_account_auth_tokens', conditions={'method': ['GET']})
564 m.connect('my_account_auth_tokens', '/my_account/auth_tokens',
558 m.connect('my_account_auth_tokens', '/my_account/auth_tokens',
565 action='my_account_auth_tokens_add', conditions={'method': ['POST']})
559 action='my_account_auth_tokens_add', conditions={'method': ['POST']})
566 m.connect('my_account_auth_tokens', '/my_account/auth_tokens',
560 m.connect('my_account_auth_tokens', '/my_account/auth_tokens',
567 action='my_account_auth_tokens_delete', conditions={'method': ['DELETE']})
561 action='my_account_auth_tokens_delete', conditions={'method': ['DELETE']})
568 m.connect('my_account_notifications', '/my_account/notifications',
562 m.connect('my_account_notifications', '/my_account/notifications',
569 action='my_notifications',
563 action='my_notifications',
570 conditions={'method': ['GET']})
564 conditions={'method': ['GET']})
571 m.connect('my_account_notifications_toggle_visibility',
565 m.connect('my_account_notifications_toggle_visibility',
572 '/my_account/toggle_visibility',
566 '/my_account/toggle_visibility',
573 action='my_notifications_toggle_visibility',
567 action='my_notifications_toggle_visibility',
574 conditions={'method': ['POST']})
568 conditions={'method': ['POST']})
575 m.connect('my_account_notifications_test_channelstream',
569 m.connect('my_account_notifications_test_channelstream',
576 '/my_account/test_channelstream',
570 '/my_account/test_channelstream',
577 action='my_account_notifications_test_channelstream',
571 action='my_account_notifications_test_channelstream',
578 conditions={'method': ['POST']})
572 conditions={'method': ['POST']})
579
573
580 # NOTIFICATION REST ROUTES
574 # NOTIFICATION REST ROUTES
581 with rmap.submapper(path_prefix=ADMIN_PREFIX,
575 with rmap.submapper(path_prefix=ADMIN_PREFIX,
582 controller='admin/notifications') as m:
576 controller='admin/notifications') as m:
583 m.connect('notifications', '/notifications',
577 m.connect('notifications', '/notifications',
584 action='index', conditions={'method': ['GET']})
578 action='index', conditions={'method': ['GET']})
585 m.connect('notifications_mark_all_read', '/notifications/mark_all_read',
579 m.connect('notifications_mark_all_read', '/notifications/mark_all_read',
586 action='mark_all_read', conditions={'method': ['POST']})
580 action='mark_all_read', conditions={'method': ['POST']})
587 m.connect('/notifications/{notification_id}',
581 m.connect('/notifications/{notification_id}',
588 action='update', conditions={'method': ['PUT']})
582 action='update', conditions={'method': ['PUT']})
589 m.connect('/notifications/{notification_id}',
583 m.connect('/notifications/{notification_id}',
590 action='delete', conditions={'method': ['DELETE']})
584 action='delete', conditions={'method': ['DELETE']})
591 m.connect('notification', '/notifications/{notification_id}',
585 m.connect('notification', '/notifications/{notification_id}',
592 action='show', conditions={'method': ['GET']})
586 action='show', conditions={'method': ['GET']})
593
587
594 # ADMIN GIST
588 # ADMIN GIST
595 with rmap.submapper(path_prefix=ADMIN_PREFIX,
589 with rmap.submapper(path_prefix=ADMIN_PREFIX,
596 controller='admin/gists') as m:
590 controller='admin/gists') as m:
597 m.connect('gists', '/gists',
591 m.connect('gists', '/gists',
598 action='create', conditions={'method': ['POST']})
592 action='create', conditions={'method': ['POST']})
599 m.connect('gists', '/gists', jsroute=True,
593 m.connect('gists', '/gists', jsroute=True,
600 action='index', conditions={'method': ['GET']})
594 action='index', conditions={'method': ['GET']})
601 m.connect('new_gist', '/gists/new', jsroute=True,
595 m.connect('new_gist', '/gists/new', jsroute=True,
602 action='new', conditions={'method': ['GET']})
596 action='new', conditions={'method': ['GET']})
603
597
604 m.connect('/gists/{gist_id}',
598 m.connect('/gists/{gist_id}',
605 action='delete', conditions={'method': ['DELETE']})
599 action='delete', conditions={'method': ['DELETE']})
606 m.connect('edit_gist', '/gists/{gist_id}/edit',
600 m.connect('edit_gist', '/gists/{gist_id}/edit',
607 action='edit_form', conditions={'method': ['GET']})
601 action='edit_form', conditions={'method': ['GET']})
608 m.connect('edit_gist', '/gists/{gist_id}/edit',
602 m.connect('edit_gist', '/gists/{gist_id}/edit',
609 action='edit', conditions={'method': ['POST']})
603 action='edit', conditions={'method': ['POST']})
610 m.connect(
604 m.connect(
611 'edit_gist_check_revision', '/gists/{gist_id}/edit/check_revision',
605 'edit_gist_check_revision', '/gists/{gist_id}/edit/check_revision',
612 action='check_revision', conditions={'method': ['GET']})
606 action='check_revision', conditions={'method': ['GET']})
613
607
614 m.connect('gist', '/gists/{gist_id}',
608 m.connect('gist', '/gists/{gist_id}',
615 action='show', conditions={'method': ['GET']})
609 action='show', conditions={'method': ['GET']})
616 m.connect('gist_rev', '/gists/{gist_id}/{revision}',
610 m.connect('gist_rev', '/gists/{gist_id}/{revision}',
617 revision='tip',
611 revision='tip',
618 action='show', conditions={'method': ['GET']})
612 action='show', conditions={'method': ['GET']})
619 m.connect('formatted_gist', '/gists/{gist_id}/{revision}/{format}',
613 m.connect('formatted_gist', '/gists/{gist_id}/{revision}/{format}',
620 revision='tip',
614 revision='tip',
621 action='show', conditions={'method': ['GET']})
615 action='show', conditions={'method': ['GET']})
622 m.connect('formatted_gist_file', '/gists/{gist_id}/{revision}/{format}/{f_path}',
616 m.connect('formatted_gist_file', '/gists/{gist_id}/{revision}/{format}/{f_path}',
623 revision='tip',
617 revision='tip',
624 action='show', conditions={'method': ['GET']},
618 action='show', conditions={'method': ['GET']},
625 requirements=URL_NAME_REQUIREMENTS)
619 requirements=URL_NAME_REQUIREMENTS)
626
620
627 # ADMIN MAIN PAGES
621 # ADMIN MAIN PAGES
628 with rmap.submapper(path_prefix=ADMIN_PREFIX,
622 with rmap.submapper(path_prefix=ADMIN_PREFIX,
629 controller='admin/admin') as m:
623 controller='admin/admin') as m:
630 m.connect('admin_home', '', action='index')
624 m.connect('admin_home', '', action='index')
631 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
625 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
632 action='add_repo')
626 action='add_repo')
633 m.connect(
627 m.connect(
634 'pull_requests_global_0', '/pull_requests/{pull_request_id:[0-9]+}',
628 'pull_requests_global_0', '/pull_requests/{pull_request_id:[0-9]+}',
635 action='pull_requests')
629 action='pull_requests')
636 m.connect(
630 m.connect(
637 'pull_requests_global_1', '/pull-requests/{pull_request_id:[0-9]+}',
631 'pull_requests_global_1', '/pull-requests/{pull_request_id:[0-9]+}',
638 action='pull_requests')
632 action='pull_requests')
639 m.connect(
633 m.connect(
640 'pull_requests_global', '/pull-request/{pull_request_id:[0-9]+}',
634 'pull_requests_global', '/pull-request/{pull_request_id:[0-9]+}',
641 action='pull_requests')
635 action='pull_requests')
642
636
643 # USER JOURNAL
637 # USER JOURNAL
644 rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,),
638 rmap.connect('journal', '%s/journal' % (ADMIN_PREFIX,),
645 controller='journal', action='index')
639 controller='journal', action='index')
646 rmap.connect('journal_rss', '%s/journal/rss' % (ADMIN_PREFIX,),
640 rmap.connect('journal_rss', '%s/journal/rss' % (ADMIN_PREFIX,),
647 controller='journal', action='journal_rss')
641 controller='journal', action='journal_rss')
648 rmap.connect('journal_atom', '%s/journal/atom' % (ADMIN_PREFIX,),
642 rmap.connect('journal_atom', '%s/journal/atom' % (ADMIN_PREFIX,),
649 controller='journal', action='journal_atom')
643 controller='journal', action='journal_atom')
650
644
651 rmap.connect('public_journal', '%s/public_journal' % (ADMIN_PREFIX,),
645 rmap.connect('public_journal', '%s/public_journal' % (ADMIN_PREFIX,),
652 controller='journal', action='public_journal')
646 controller='journal', action='public_journal')
653
647
654 rmap.connect('public_journal_rss', '%s/public_journal/rss' % (ADMIN_PREFIX,),
648 rmap.connect('public_journal_rss', '%s/public_journal/rss' % (ADMIN_PREFIX,),
655 controller='journal', action='public_journal_rss')
649 controller='journal', action='public_journal_rss')
656
650
657 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % (ADMIN_PREFIX,),
651 rmap.connect('public_journal_rss_old', '%s/public_journal_rss' % (ADMIN_PREFIX,),
658 controller='journal', action='public_journal_rss')
652 controller='journal', action='public_journal_rss')
659
653
660 rmap.connect('public_journal_atom',
654 rmap.connect('public_journal_atom',
661 '%s/public_journal/atom' % (ADMIN_PREFIX,), controller='journal',
655 '%s/public_journal/atom' % (ADMIN_PREFIX,), controller='journal',
662 action='public_journal_atom')
656 action='public_journal_atom')
663
657
664 rmap.connect('public_journal_atom_old',
658 rmap.connect('public_journal_atom_old',
665 '%s/public_journal_atom' % (ADMIN_PREFIX,), controller='journal',
659 '%s/public_journal_atom' % (ADMIN_PREFIX,), controller='journal',
666 action='public_journal_atom')
660 action='public_journal_atom')
667
661
668 rmap.connect('toggle_following', '%s/toggle_following' % (ADMIN_PREFIX,),
662 rmap.connect('toggle_following', '%s/toggle_following' % (ADMIN_PREFIX,),
669 controller='journal', action='toggle_following', jsroute=True,
663 controller='journal', action='toggle_following', jsroute=True,
670 conditions={'method': ['POST']})
664 conditions={'method': ['POST']})
671
665
672 # FULL TEXT SEARCH
666 # FULL TEXT SEARCH
673 rmap.connect('search', '%s/search' % (ADMIN_PREFIX,),
667 rmap.connect('search', '%s/search' % (ADMIN_PREFIX,),
674 controller='search')
668 controller='search')
675 rmap.connect('search_repo_home', '/{repo_name}/search',
669 rmap.connect('search_repo_home', '/{repo_name}/search',
676 controller='search',
670 controller='search',
677 action='index',
671 action='index',
678 conditions={'function': check_repo},
672 conditions={'function': check_repo},
679 requirements=URL_NAME_REQUIREMENTS)
673 requirements=URL_NAME_REQUIREMENTS)
680
674
681 # FEEDS
675 # FEEDS
682 rmap.connect('rss_feed_home', '/{repo_name}/feed/rss',
676 rmap.connect('rss_feed_home', '/{repo_name}/feed/rss',
683 controller='feed', action='rss',
677 controller='feed', action='rss',
684 conditions={'function': check_repo},
678 conditions={'function': check_repo},
685 requirements=URL_NAME_REQUIREMENTS)
679 requirements=URL_NAME_REQUIREMENTS)
686
680
687 rmap.connect('atom_feed_home', '/{repo_name}/feed/atom',
681 rmap.connect('atom_feed_home', '/{repo_name}/feed/atom',
688 controller='feed', action='atom',
682 controller='feed', action='atom',
689 conditions={'function': check_repo},
683 conditions={'function': check_repo},
690 requirements=URL_NAME_REQUIREMENTS)
684 requirements=URL_NAME_REQUIREMENTS)
691
685
692 #==========================================================================
686 #==========================================================================
693 # REPOSITORY ROUTES
687 # REPOSITORY ROUTES
694 #==========================================================================
688 #==========================================================================
695
689
696 rmap.connect('repo_creating_home', '/{repo_name}/repo_creating',
690 rmap.connect('repo_creating_home', '/{repo_name}/repo_creating',
697 controller='admin/repos', action='repo_creating',
691 controller='admin/repos', action='repo_creating',
698 requirements=URL_NAME_REQUIREMENTS)
692 requirements=URL_NAME_REQUIREMENTS)
699 rmap.connect('repo_check_home', '/{repo_name}/crepo_check',
693 rmap.connect('repo_check_home', '/{repo_name}/crepo_check',
700 controller='admin/repos', action='repo_check',
694 controller='admin/repos', action='repo_check',
701 requirements=URL_NAME_REQUIREMENTS)
695 requirements=URL_NAME_REQUIREMENTS)
702
696
703 rmap.connect('repo_stats', '/{repo_name}/repo_stats/{commit_id}',
697 rmap.connect('repo_stats', '/{repo_name}/repo_stats/{commit_id}',
704 controller='summary', action='repo_stats',
698 controller='summary', action='repo_stats',
705 conditions={'function': check_repo},
699 conditions={'function': check_repo},
706 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
700 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
707
701
708 rmap.connect('repo_refs_data', '/{repo_name}/refs-data',
702 rmap.connect('repo_refs_data', '/{repo_name}/refs-data',
709 controller='summary', action='repo_refs_data', jsroute=True,
703 controller='summary', action='repo_refs_data', jsroute=True,
710 requirements=URL_NAME_REQUIREMENTS)
704 requirements=URL_NAME_REQUIREMENTS)
711 rmap.connect('repo_refs_changelog_data', '/{repo_name}/refs-data-changelog',
705 rmap.connect('repo_refs_changelog_data', '/{repo_name}/refs-data-changelog',
712 controller='summary', action='repo_refs_changelog_data',
706 controller='summary', action='repo_refs_changelog_data',
713 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
707 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
714 rmap.connect('repo_default_reviewers_data', '/{repo_name}/default-reviewers',
708 rmap.connect('repo_default_reviewers_data', '/{repo_name}/default-reviewers',
715 controller='summary', action='repo_default_reviewers_data',
709 controller='summary', action='repo_default_reviewers_data',
716 jsroute=True, requirements=URL_NAME_REQUIREMENTS)
710 jsroute=True, requirements=URL_NAME_REQUIREMENTS)
717
711
718 rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}',
712 rmap.connect('changeset_home', '/{repo_name}/changeset/{revision}',
719 controller='changeset', revision='tip', jsroute=True,
713 controller='changeset', revision='tip', jsroute=True,
720 conditions={'function': check_repo},
714 conditions={'function': check_repo},
721 requirements=URL_NAME_REQUIREMENTS)
715 requirements=URL_NAME_REQUIREMENTS)
722 rmap.connect('changeset_children', '/{repo_name}/changeset_children/{revision}',
716 rmap.connect('changeset_children', '/{repo_name}/changeset_children/{revision}',
723 controller='changeset', revision='tip', action='changeset_children',
717 controller='changeset', revision='tip', action='changeset_children',
724 conditions={'function': check_repo},
718 conditions={'function': check_repo},
725 requirements=URL_NAME_REQUIREMENTS)
719 requirements=URL_NAME_REQUIREMENTS)
726 rmap.connect('changeset_parents', '/{repo_name}/changeset_parents/{revision}',
720 rmap.connect('changeset_parents', '/{repo_name}/changeset_parents/{revision}',
727 controller='changeset', revision='tip', action='changeset_parents',
721 controller='changeset', revision='tip', action='changeset_parents',
728 conditions={'function': check_repo},
722 conditions={'function': check_repo},
729 requirements=URL_NAME_REQUIREMENTS)
723 requirements=URL_NAME_REQUIREMENTS)
730
724
731 # repo edit options
725 # repo edit options
732 rmap.connect('edit_repo', '/{repo_name}/settings', jsroute=True,
726 rmap.connect('edit_repo', '/{repo_name}/settings', jsroute=True,
733 controller='admin/repos', action='edit',
727 controller='admin/repos', action='edit',
734 conditions={'method': ['GET'], 'function': check_repo},
728 conditions={'method': ['GET'], 'function': check_repo},
735 requirements=URL_NAME_REQUIREMENTS)
729 requirements=URL_NAME_REQUIREMENTS)
736
730
737 rmap.connect('edit_repo_perms', '/{repo_name}/settings/permissions',
731 rmap.connect('edit_repo_perms', '/{repo_name}/settings/permissions',
738 jsroute=True,
732 jsroute=True,
739 controller='admin/repos', action='edit_permissions',
733 controller='admin/repos', action='edit_permissions',
740 conditions={'method': ['GET'], 'function': check_repo},
734 conditions={'method': ['GET'], 'function': check_repo},
741 requirements=URL_NAME_REQUIREMENTS)
735 requirements=URL_NAME_REQUIREMENTS)
742 rmap.connect('edit_repo_perms_update', '/{repo_name}/settings/permissions',
736 rmap.connect('edit_repo_perms_update', '/{repo_name}/settings/permissions',
743 controller='admin/repos', action='edit_permissions_update',
737 controller='admin/repos', action='edit_permissions_update',
744 conditions={'method': ['PUT'], 'function': check_repo},
738 conditions={'method': ['PUT'], 'function': check_repo},
745 requirements=URL_NAME_REQUIREMENTS)
739 requirements=URL_NAME_REQUIREMENTS)
746
740
747 rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields',
741 rmap.connect('edit_repo_fields', '/{repo_name}/settings/fields',
748 controller='admin/repos', action='edit_fields',
742 controller='admin/repos', action='edit_fields',
749 conditions={'method': ['GET'], 'function': check_repo},
743 conditions={'method': ['GET'], 'function': check_repo},
750 requirements=URL_NAME_REQUIREMENTS)
744 requirements=URL_NAME_REQUIREMENTS)
751 rmap.connect('create_repo_fields', '/{repo_name}/settings/fields/new',
745 rmap.connect('create_repo_fields', '/{repo_name}/settings/fields/new',
752 controller='admin/repos', action='create_repo_field',
746 controller='admin/repos', action='create_repo_field',
753 conditions={'method': ['PUT'], 'function': check_repo},
747 conditions={'method': ['PUT'], 'function': check_repo},
754 requirements=URL_NAME_REQUIREMENTS)
748 requirements=URL_NAME_REQUIREMENTS)
755 rmap.connect('delete_repo_fields', '/{repo_name}/settings/fields/{field_id}',
749 rmap.connect('delete_repo_fields', '/{repo_name}/settings/fields/{field_id}',
756 controller='admin/repos', action='delete_repo_field',
750 controller='admin/repos', action='delete_repo_field',
757 conditions={'method': ['DELETE'], 'function': check_repo},
751 conditions={'method': ['DELETE'], 'function': check_repo},
758 requirements=URL_NAME_REQUIREMENTS)
752 requirements=URL_NAME_REQUIREMENTS)
759
753
760 rmap.connect('edit_repo_advanced', '/{repo_name}/settings/advanced',
754 rmap.connect('edit_repo_advanced', '/{repo_name}/settings/advanced',
761 controller='admin/repos', action='edit_advanced',
755 controller='admin/repos', action='edit_advanced',
762 conditions={'method': ['GET'], 'function': check_repo},
756 conditions={'method': ['GET'], 'function': check_repo},
763 requirements=URL_NAME_REQUIREMENTS)
757 requirements=URL_NAME_REQUIREMENTS)
764
758
765 rmap.connect('edit_repo_advanced_locking', '/{repo_name}/settings/advanced/locking',
759 rmap.connect('edit_repo_advanced_locking', '/{repo_name}/settings/advanced/locking',
766 controller='admin/repos', action='edit_advanced_locking',
760 controller='admin/repos', action='edit_advanced_locking',
767 conditions={'method': ['PUT'], 'function': check_repo},
761 conditions={'method': ['PUT'], 'function': check_repo},
768 requirements=URL_NAME_REQUIREMENTS)
762 requirements=URL_NAME_REQUIREMENTS)
769 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
763 rmap.connect('toggle_locking', '/{repo_name}/settings/advanced/locking_toggle',
770 controller='admin/repos', action='toggle_locking',
764 controller='admin/repos', action='toggle_locking',
771 conditions={'method': ['GET'], 'function': check_repo},
765 conditions={'method': ['GET'], 'function': check_repo},
772 requirements=URL_NAME_REQUIREMENTS)
766 requirements=URL_NAME_REQUIREMENTS)
773
767
774 rmap.connect('edit_repo_advanced_journal', '/{repo_name}/settings/advanced/journal',
768 rmap.connect('edit_repo_advanced_journal', '/{repo_name}/settings/advanced/journal',
775 controller='admin/repos', action='edit_advanced_journal',
769 controller='admin/repos', action='edit_advanced_journal',
776 conditions={'method': ['PUT'], 'function': check_repo},
770 conditions={'method': ['PUT'], 'function': check_repo},
777 requirements=URL_NAME_REQUIREMENTS)
771 requirements=URL_NAME_REQUIREMENTS)
778
772
779 rmap.connect('edit_repo_advanced_fork', '/{repo_name}/settings/advanced/fork',
773 rmap.connect('edit_repo_advanced_fork', '/{repo_name}/settings/advanced/fork',
780 controller='admin/repos', action='edit_advanced_fork',
774 controller='admin/repos', action='edit_advanced_fork',
781 conditions={'method': ['PUT'], 'function': check_repo},
775 conditions={'method': ['PUT'], 'function': check_repo},
782 requirements=URL_NAME_REQUIREMENTS)
776 requirements=URL_NAME_REQUIREMENTS)
783
777
784 rmap.connect('edit_repo_caches', '/{repo_name}/settings/caches',
778 rmap.connect('edit_repo_caches', '/{repo_name}/settings/caches',
785 controller='admin/repos', action='edit_caches_form',
779 controller='admin/repos', action='edit_caches_form',
786 conditions={'method': ['GET'], 'function': check_repo},
780 conditions={'method': ['GET'], 'function': check_repo},
787 requirements=URL_NAME_REQUIREMENTS)
781 requirements=URL_NAME_REQUIREMENTS)
788 rmap.connect('edit_repo_caches', '/{repo_name}/settings/caches',
782 rmap.connect('edit_repo_caches', '/{repo_name}/settings/caches',
789 controller='admin/repos', action='edit_caches',
783 controller='admin/repos', action='edit_caches',
790 conditions={'method': ['PUT'], 'function': check_repo},
784 conditions={'method': ['PUT'], 'function': check_repo},
791 requirements=URL_NAME_REQUIREMENTS)
785 requirements=URL_NAME_REQUIREMENTS)
792
786
793 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
787 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
794 controller='admin/repos', action='edit_remote_form',
788 controller='admin/repos', action='edit_remote_form',
795 conditions={'method': ['GET'], 'function': check_repo},
789 conditions={'method': ['GET'], 'function': check_repo},
796 requirements=URL_NAME_REQUIREMENTS)
790 requirements=URL_NAME_REQUIREMENTS)
797 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
791 rmap.connect('edit_repo_remote', '/{repo_name}/settings/remote',
798 controller='admin/repos', action='edit_remote',
792 controller='admin/repos', action='edit_remote',
799 conditions={'method': ['PUT'], 'function': check_repo},
793 conditions={'method': ['PUT'], 'function': check_repo},
800 requirements=URL_NAME_REQUIREMENTS)
794 requirements=URL_NAME_REQUIREMENTS)
801
795
802 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
796 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
803 controller='admin/repos', action='edit_statistics_form',
797 controller='admin/repos', action='edit_statistics_form',
804 conditions={'method': ['GET'], 'function': check_repo},
798 conditions={'method': ['GET'], 'function': check_repo},
805 requirements=URL_NAME_REQUIREMENTS)
799 requirements=URL_NAME_REQUIREMENTS)
806 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
800 rmap.connect('edit_repo_statistics', '/{repo_name}/settings/statistics',
807 controller='admin/repos', action='edit_statistics',
801 controller='admin/repos', action='edit_statistics',
808 conditions={'method': ['PUT'], 'function': check_repo},
802 conditions={'method': ['PUT'], 'function': check_repo},
809 requirements=URL_NAME_REQUIREMENTS)
803 requirements=URL_NAME_REQUIREMENTS)
810 rmap.connect('repo_settings_issuetracker',
804 rmap.connect('repo_settings_issuetracker',
811 '/{repo_name}/settings/issue-tracker',
805 '/{repo_name}/settings/issue-tracker',
812 controller='admin/repos', action='repo_issuetracker',
806 controller='admin/repos', action='repo_issuetracker',
813 conditions={'method': ['GET'], 'function': check_repo},
807 conditions={'method': ['GET'], 'function': check_repo},
814 requirements=URL_NAME_REQUIREMENTS)
808 requirements=URL_NAME_REQUIREMENTS)
815 rmap.connect('repo_issuetracker_test',
809 rmap.connect('repo_issuetracker_test',
816 '/{repo_name}/settings/issue-tracker/test',
810 '/{repo_name}/settings/issue-tracker/test',
817 controller='admin/repos', action='repo_issuetracker_test',
811 controller='admin/repos', action='repo_issuetracker_test',
818 conditions={'method': ['POST'], 'function': check_repo},
812 conditions={'method': ['POST'], 'function': check_repo},
819 requirements=URL_NAME_REQUIREMENTS)
813 requirements=URL_NAME_REQUIREMENTS)
820 rmap.connect('repo_issuetracker_delete',
814 rmap.connect('repo_issuetracker_delete',
821 '/{repo_name}/settings/issue-tracker/delete',
815 '/{repo_name}/settings/issue-tracker/delete',
822 controller='admin/repos', action='repo_issuetracker_delete',
816 controller='admin/repos', action='repo_issuetracker_delete',
823 conditions={'method': ['DELETE'], 'function': check_repo},
817 conditions={'method': ['DELETE'], 'function': check_repo},
824 requirements=URL_NAME_REQUIREMENTS)
818 requirements=URL_NAME_REQUIREMENTS)
825 rmap.connect('repo_issuetracker_save',
819 rmap.connect('repo_issuetracker_save',
826 '/{repo_name}/settings/issue-tracker/save',
820 '/{repo_name}/settings/issue-tracker/save',
827 controller='admin/repos', action='repo_issuetracker_save',
821 controller='admin/repos', action='repo_issuetracker_save',
828 conditions={'method': ['POST'], 'function': check_repo},
822 conditions={'method': ['POST'], 'function': check_repo},
829 requirements=URL_NAME_REQUIREMENTS)
823 requirements=URL_NAME_REQUIREMENTS)
830 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
824 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
831 controller='admin/repos', action='repo_settings_vcs_update',
825 controller='admin/repos', action='repo_settings_vcs_update',
832 conditions={'method': ['POST'], 'function': check_repo},
826 conditions={'method': ['POST'], 'function': check_repo},
833 requirements=URL_NAME_REQUIREMENTS)
827 requirements=URL_NAME_REQUIREMENTS)
834 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
828 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
835 controller='admin/repos', action='repo_settings_vcs',
829 controller='admin/repos', action='repo_settings_vcs',
836 conditions={'method': ['GET'], 'function': check_repo},
830 conditions={'method': ['GET'], 'function': check_repo},
837 requirements=URL_NAME_REQUIREMENTS)
831 requirements=URL_NAME_REQUIREMENTS)
838 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
832 rmap.connect('repo_vcs_settings', '/{repo_name}/settings/vcs',
839 controller='admin/repos', action='repo_delete_svn_pattern',
833 controller='admin/repos', action='repo_delete_svn_pattern',
840 conditions={'method': ['DELETE'], 'function': check_repo},
834 conditions={'method': ['DELETE'], 'function': check_repo},
841 requirements=URL_NAME_REQUIREMENTS)
835 requirements=URL_NAME_REQUIREMENTS)
842 rmap.connect('repo_pullrequest_settings', '/{repo_name}/settings/pullrequest',
836 rmap.connect('repo_pullrequest_settings', '/{repo_name}/settings/pullrequest',
843 controller='admin/repos', action='repo_settings_pullrequest',
837 controller='admin/repos', action='repo_settings_pullrequest',
844 conditions={'method': ['GET', 'POST'], 'function': check_repo},
838 conditions={'method': ['GET', 'POST'], 'function': check_repo},
845 requirements=URL_NAME_REQUIREMENTS)
839 requirements=URL_NAME_REQUIREMENTS)
846
840
847 # still working url for backward compat.
841 # still working url for backward compat.
848 rmap.connect('raw_changeset_home_depraced',
842 rmap.connect('raw_changeset_home_depraced',
849 '/{repo_name}/raw-changeset/{revision}',
843 '/{repo_name}/raw-changeset/{revision}',
850 controller='changeset', action='changeset_raw',
844 controller='changeset', action='changeset_raw',
851 revision='tip', conditions={'function': check_repo},
845 revision='tip', conditions={'function': check_repo},
852 requirements=URL_NAME_REQUIREMENTS)
846 requirements=URL_NAME_REQUIREMENTS)
853
847
854 # new URLs
848 # new URLs
855 rmap.connect('changeset_raw_home',
849 rmap.connect('changeset_raw_home',
856 '/{repo_name}/changeset-diff/{revision}',
850 '/{repo_name}/changeset-diff/{revision}',
857 controller='changeset', action='changeset_raw',
851 controller='changeset', action='changeset_raw',
858 revision='tip', conditions={'function': check_repo},
852 revision='tip', conditions={'function': check_repo},
859 requirements=URL_NAME_REQUIREMENTS)
853 requirements=URL_NAME_REQUIREMENTS)
860
854
861 rmap.connect('changeset_patch_home',
855 rmap.connect('changeset_patch_home',
862 '/{repo_name}/changeset-patch/{revision}',
856 '/{repo_name}/changeset-patch/{revision}',
863 controller='changeset', action='changeset_patch',
857 controller='changeset', action='changeset_patch',
864 revision='tip', conditions={'function': check_repo},
858 revision='tip', conditions={'function': check_repo},
865 requirements=URL_NAME_REQUIREMENTS)
859 requirements=URL_NAME_REQUIREMENTS)
866
860
867 rmap.connect('changeset_download_home',
861 rmap.connect('changeset_download_home',
868 '/{repo_name}/changeset-download/{revision}',
862 '/{repo_name}/changeset-download/{revision}',
869 controller='changeset', action='changeset_download',
863 controller='changeset', action='changeset_download',
870 revision='tip', conditions={'function': check_repo},
864 revision='tip', conditions={'function': check_repo},
871 requirements=URL_NAME_REQUIREMENTS)
865 requirements=URL_NAME_REQUIREMENTS)
872
866
873 rmap.connect('changeset_comment',
867 rmap.connect('changeset_comment',
874 '/{repo_name}/changeset/{revision}/comment', jsroute=True,
868 '/{repo_name}/changeset/{revision}/comment', jsroute=True,
875 controller='changeset', revision='tip', action='comment',
869 controller='changeset', revision='tip', action='comment',
876 conditions={'function': check_repo},
870 conditions={'function': check_repo},
877 requirements=URL_NAME_REQUIREMENTS)
871 requirements=URL_NAME_REQUIREMENTS)
878
872
879 rmap.connect('changeset_comment_preview',
873 rmap.connect('changeset_comment_preview',
880 '/{repo_name}/changeset/comment/preview', jsroute=True,
874 '/{repo_name}/changeset/comment/preview', jsroute=True,
881 controller='changeset', action='preview_comment',
875 controller='changeset', action='preview_comment',
882 conditions={'function': check_repo, 'method': ['POST']},
876 conditions={'function': check_repo, 'method': ['POST']},
883 requirements=URL_NAME_REQUIREMENTS)
877 requirements=URL_NAME_REQUIREMENTS)
884
878
885 rmap.connect('changeset_comment_delete',
879 rmap.connect('changeset_comment_delete',
886 '/{repo_name}/changeset/comment/{comment_id}/delete',
880 '/{repo_name}/changeset/comment/{comment_id}/delete',
887 controller='changeset', action='delete_comment',
881 controller='changeset', action='delete_comment',
888 conditions={'function': check_repo, 'method': ['DELETE']},
882 conditions={'function': check_repo, 'method': ['DELETE']},
889 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
883 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
890
884
891 rmap.connect('changeset_info', '/{repo_name}/changeset_info/{revision}',
885 rmap.connect('changeset_info', '/{repo_name}/changeset_info/{revision}',
892 controller='changeset', action='changeset_info',
886 controller='changeset', action='changeset_info',
893 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
887 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
894
888
895 rmap.connect('compare_home',
889 rmap.connect('compare_home',
896 '/{repo_name}/compare',
890 '/{repo_name}/compare',
897 controller='compare', action='index',
891 controller='compare', action='index',
898 conditions={'function': check_repo},
892 conditions={'function': check_repo},
899 requirements=URL_NAME_REQUIREMENTS)
893 requirements=URL_NAME_REQUIREMENTS)
900
894
901 rmap.connect('compare_url',
895 rmap.connect('compare_url',
902 '/{repo_name}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}',
896 '/{repo_name}/compare/{source_ref_type}@{source_ref:.*?}...{target_ref_type}@{target_ref:.*?}',
903 controller='compare', action='compare',
897 controller='compare', action='compare',
904 conditions={'function': check_repo},
898 conditions={'function': check_repo},
905 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
899 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
906
900
907 rmap.connect('pullrequest_home',
901 rmap.connect('pullrequest_home',
908 '/{repo_name}/pull-request/new', controller='pullrequests',
902 '/{repo_name}/pull-request/new', controller='pullrequests',
909 action='index', conditions={'function': check_repo,
903 action='index', conditions={'function': check_repo,
910 'method': ['GET']},
904 'method': ['GET']},
911 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
905 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
912
906
913 rmap.connect('pullrequest',
907 rmap.connect('pullrequest',
914 '/{repo_name}/pull-request/new', controller='pullrequests',
908 '/{repo_name}/pull-request/new', controller='pullrequests',
915 action='create', conditions={'function': check_repo,
909 action='create', conditions={'function': check_repo,
916 'method': ['POST']},
910 'method': ['POST']},
917 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
911 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
918
912
919 rmap.connect('pullrequest_repo_refs',
913 rmap.connect('pullrequest_repo_refs',
920 '/{repo_name}/pull-request/refs/{target_repo_name:.*?[^/]}',
914 '/{repo_name}/pull-request/refs/{target_repo_name:.*?[^/]}',
921 controller='pullrequests',
915 controller='pullrequests',
922 action='get_repo_refs',
916 action='get_repo_refs',
923 conditions={'function': check_repo, 'method': ['GET']},
917 conditions={'function': check_repo, 'method': ['GET']},
924 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
918 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
925
919
926 rmap.connect('pullrequest_repo_destinations',
920 rmap.connect('pullrequest_repo_destinations',
927 '/{repo_name}/pull-request/repo-destinations',
921 '/{repo_name}/pull-request/repo-destinations',
928 controller='pullrequests',
922 controller='pullrequests',
929 action='get_repo_destinations',
923 action='get_repo_destinations',
930 conditions={'function': check_repo, 'method': ['GET']},
924 conditions={'function': check_repo, 'method': ['GET']},
931 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
925 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
932
926
933 rmap.connect('pullrequest_show',
927 rmap.connect('pullrequest_show',
934 '/{repo_name}/pull-request/{pull_request_id}',
928 '/{repo_name}/pull-request/{pull_request_id}',
935 controller='pullrequests',
929 controller='pullrequests',
936 action='show', conditions={'function': check_repo,
930 action='show', conditions={'function': check_repo,
937 'method': ['GET']},
931 'method': ['GET']},
938 requirements=URL_NAME_REQUIREMENTS)
932 requirements=URL_NAME_REQUIREMENTS)
939
933
940 rmap.connect('pullrequest_update',
934 rmap.connect('pullrequest_update',
941 '/{repo_name}/pull-request/{pull_request_id}',
935 '/{repo_name}/pull-request/{pull_request_id}',
942 controller='pullrequests',
936 controller='pullrequests',
943 action='update', conditions={'function': check_repo,
937 action='update', conditions={'function': check_repo,
944 'method': ['PUT']},
938 'method': ['PUT']},
945 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
939 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
946
940
947 rmap.connect('pullrequest_merge',
941 rmap.connect('pullrequest_merge',
948 '/{repo_name}/pull-request/{pull_request_id}',
942 '/{repo_name}/pull-request/{pull_request_id}',
949 controller='pullrequests',
943 controller='pullrequests',
950 action='merge', conditions={'function': check_repo,
944 action='merge', conditions={'function': check_repo,
951 'method': ['POST']},
945 'method': ['POST']},
952 requirements=URL_NAME_REQUIREMENTS)
946 requirements=URL_NAME_REQUIREMENTS)
953
947
954 rmap.connect('pullrequest_delete',
948 rmap.connect('pullrequest_delete',
955 '/{repo_name}/pull-request/{pull_request_id}',
949 '/{repo_name}/pull-request/{pull_request_id}',
956 controller='pullrequests',
950 controller='pullrequests',
957 action='delete', conditions={'function': check_repo,
951 action='delete', conditions={'function': check_repo,
958 'method': ['DELETE']},
952 'method': ['DELETE']},
959 requirements=URL_NAME_REQUIREMENTS)
953 requirements=URL_NAME_REQUIREMENTS)
960
954
961 rmap.connect('pullrequest_show_all',
955 rmap.connect('pullrequest_show_all',
962 '/{repo_name}/pull-request',
956 '/{repo_name}/pull-request',
963 controller='pullrequests',
957 controller='pullrequests',
964 action='show_all', conditions={'function': check_repo,
958 action='show_all', conditions={'function': check_repo,
965 'method': ['GET']},
959 'method': ['GET']},
966 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
960 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
967
961
968 rmap.connect('pullrequest_comment',
962 rmap.connect('pullrequest_comment',
969 '/{repo_name}/pull-request-comment/{pull_request_id}',
963 '/{repo_name}/pull-request-comment/{pull_request_id}',
970 controller='pullrequests',
964 controller='pullrequests',
971 action='comment', conditions={'function': check_repo,
965 action='comment', conditions={'function': check_repo,
972 'method': ['POST']},
966 'method': ['POST']},
973 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
967 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
974
968
975 rmap.connect('pullrequest_comment_delete',
969 rmap.connect('pullrequest_comment_delete',
976 '/{repo_name}/pull-request-comment/{comment_id}/delete',
970 '/{repo_name}/pull-request-comment/{comment_id}/delete',
977 controller='pullrequests', action='delete_comment',
971 controller='pullrequests', action='delete_comment',
978 conditions={'function': check_repo, 'method': ['DELETE']},
972 conditions={'function': check_repo, 'method': ['DELETE']},
979 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
973 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
980
974
981 rmap.connect('summary_home_explicit', '/{repo_name}/summary',
975 rmap.connect('summary_home_explicit', '/{repo_name}/summary',
982 controller='summary', conditions={'function': check_repo},
976 controller='summary', conditions={'function': check_repo},
983 requirements=URL_NAME_REQUIREMENTS)
977 requirements=URL_NAME_REQUIREMENTS)
984
978
985 rmap.connect('branches_home', '/{repo_name}/branches',
979 rmap.connect('branches_home', '/{repo_name}/branches',
986 controller='branches', conditions={'function': check_repo},
980 controller='branches', conditions={'function': check_repo},
987 requirements=URL_NAME_REQUIREMENTS)
981 requirements=URL_NAME_REQUIREMENTS)
988
982
989 rmap.connect('tags_home', '/{repo_name}/tags',
983 rmap.connect('tags_home', '/{repo_name}/tags',
990 controller='tags', conditions={'function': check_repo},
984 controller='tags', conditions={'function': check_repo},
991 requirements=URL_NAME_REQUIREMENTS)
985 requirements=URL_NAME_REQUIREMENTS)
992
986
993 rmap.connect('bookmarks_home', '/{repo_name}/bookmarks',
987 rmap.connect('bookmarks_home', '/{repo_name}/bookmarks',
994 controller='bookmarks', conditions={'function': check_repo},
988 controller='bookmarks', conditions={'function': check_repo},
995 requirements=URL_NAME_REQUIREMENTS)
989 requirements=URL_NAME_REQUIREMENTS)
996
990
997 rmap.connect('changelog_home', '/{repo_name}/changelog', jsroute=True,
991 rmap.connect('changelog_home', '/{repo_name}/changelog', jsroute=True,
998 controller='changelog', conditions={'function': check_repo},
992 controller='changelog', conditions={'function': check_repo},
999 requirements=URL_NAME_REQUIREMENTS)
993 requirements=URL_NAME_REQUIREMENTS)
1000
994
1001 rmap.connect('changelog_summary_home', '/{repo_name}/changelog_summary',
995 rmap.connect('changelog_summary_home', '/{repo_name}/changelog_summary',
1002 controller='changelog', action='changelog_summary',
996 controller='changelog', action='changelog_summary',
1003 conditions={'function': check_repo},
997 conditions={'function': check_repo},
1004 requirements=URL_NAME_REQUIREMENTS)
998 requirements=URL_NAME_REQUIREMENTS)
1005
999
1006 rmap.connect('changelog_file_home',
1000 rmap.connect('changelog_file_home',
1007 '/{repo_name}/changelog/{revision}/{f_path}',
1001 '/{repo_name}/changelog/{revision}/{f_path}',
1008 controller='changelog', f_path=None,
1002 controller='changelog', f_path=None,
1009 conditions={'function': check_repo},
1003 conditions={'function': check_repo},
1010 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1004 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1011
1005
1012 rmap.connect('changelog_details', '/{repo_name}/changelog_details/{cs}',
1006 rmap.connect('changelog_details', '/{repo_name}/changelog_details/{cs}',
1013 controller='changelog', action='changelog_details',
1007 controller='changelog', action='changelog_details',
1014 conditions={'function': check_repo},
1008 conditions={'function': check_repo},
1015 requirements=URL_NAME_REQUIREMENTS)
1009 requirements=URL_NAME_REQUIREMENTS)
1016
1010
1017 rmap.connect('files_home', '/{repo_name}/files/{revision}/{f_path}',
1011 rmap.connect('files_home', '/{repo_name}/files/{revision}/{f_path}',
1018 controller='files', revision='tip', f_path='',
1012 controller='files', revision='tip', f_path='',
1019 conditions={'function': check_repo},
1013 conditions={'function': check_repo},
1020 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1014 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1021
1015
1022 rmap.connect('files_home_simple_catchrev',
1016 rmap.connect('files_home_simple_catchrev',
1023 '/{repo_name}/files/{revision}',
1017 '/{repo_name}/files/{revision}',
1024 controller='files', revision='tip', f_path='',
1018 controller='files', revision='tip', f_path='',
1025 conditions={'function': check_repo},
1019 conditions={'function': check_repo},
1026 requirements=URL_NAME_REQUIREMENTS)
1020 requirements=URL_NAME_REQUIREMENTS)
1027
1021
1028 rmap.connect('files_home_simple_catchall',
1022 rmap.connect('files_home_simple_catchall',
1029 '/{repo_name}/files',
1023 '/{repo_name}/files',
1030 controller='files', revision='tip', f_path='',
1024 controller='files', revision='tip', f_path='',
1031 conditions={'function': check_repo},
1025 conditions={'function': check_repo},
1032 requirements=URL_NAME_REQUIREMENTS)
1026 requirements=URL_NAME_REQUIREMENTS)
1033
1027
1034 rmap.connect('files_history_home',
1028 rmap.connect('files_history_home',
1035 '/{repo_name}/history/{revision}/{f_path}',
1029 '/{repo_name}/history/{revision}/{f_path}',
1036 controller='files', action='history', revision='tip', f_path='',
1030 controller='files', action='history', revision='tip', f_path='',
1037 conditions={'function': check_repo},
1031 conditions={'function': check_repo},
1038 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1032 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1039
1033
1040 rmap.connect('files_authors_home',
1034 rmap.connect('files_authors_home',
1041 '/{repo_name}/authors/{revision}/{f_path}',
1035 '/{repo_name}/authors/{revision}/{f_path}',
1042 controller='files', action='authors', revision='tip', f_path='',
1036 controller='files', action='authors', revision='tip', f_path='',
1043 conditions={'function': check_repo},
1037 conditions={'function': check_repo},
1044 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1038 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1045
1039
1046 rmap.connect('files_diff_home', '/{repo_name}/diff/{f_path}',
1040 rmap.connect('files_diff_home', '/{repo_name}/diff/{f_path}',
1047 controller='files', action='diff', f_path='',
1041 controller='files', action='diff', f_path='',
1048 conditions={'function': check_repo},
1042 conditions={'function': check_repo},
1049 requirements=URL_NAME_REQUIREMENTS)
1043 requirements=URL_NAME_REQUIREMENTS)
1050
1044
1051 rmap.connect('files_diff_2way_home',
1045 rmap.connect('files_diff_2way_home',
1052 '/{repo_name}/diff-2way/{f_path}',
1046 '/{repo_name}/diff-2way/{f_path}',
1053 controller='files', action='diff_2way', f_path='',
1047 controller='files', action='diff_2way', f_path='',
1054 conditions={'function': check_repo},
1048 conditions={'function': check_repo},
1055 requirements=URL_NAME_REQUIREMENTS)
1049 requirements=URL_NAME_REQUIREMENTS)
1056
1050
1057 rmap.connect('files_rawfile_home',
1051 rmap.connect('files_rawfile_home',
1058 '/{repo_name}/rawfile/{revision}/{f_path}',
1052 '/{repo_name}/rawfile/{revision}/{f_path}',
1059 controller='files', action='rawfile', revision='tip',
1053 controller='files', action='rawfile', revision='tip',
1060 f_path='', conditions={'function': check_repo},
1054 f_path='', conditions={'function': check_repo},
1061 requirements=URL_NAME_REQUIREMENTS)
1055 requirements=URL_NAME_REQUIREMENTS)
1062
1056
1063 rmap.connect('files_raw_home',
1057 rmap.connect('files_raw_home',
1064 '/{repo_name}/raw/{revision}/{f_path}',
1058 '/{repo_name}/raw/{revision}/{f_path}',
1065 controller='files', action='raw', revision='tip', f_path='',
1059 controller='files', action='raw', revision='tip', f_path='',
1066 conditions={'function': check_repo},
1060 conditions={'function': check_repo},
1067 requirements=URL_NAME_REQUIREMENTS)
1061 requirements=URL_NAME_REQUIREMENTS)
1068
1062
1069 rmap.connect('files_render_home',
1063 rmap.connect('files_render_home',
1070 '/{repo_name}/render/{revision}/{f_path}',
1064 '/{repo_name}/render/{revision}/{f_path}',
1071 controller='files', action='index', revision='tip', f_path='',
1065 controller='files', action='index', revision='tip', f_path='',
1072 rendered=True, conditions={'function': check_repo},
1066 rendered=True, conditions={'function': check_repo},
1073 requirements=URL_NAME_REQUIREMENTS)
1067 requirements=URL_NAME_REQUIREMENTS)
1074
1068
1075 rmap.connect('files_annotate_home',
1069 rmap.connect('files_annotate_home',
1076 '/{repo_name}/annotate/{revision}/{f_path}',
1070 '/{repo_name}/annotate/{revision}/{f_path}',
1077 controller='files', action='index', revision='tip',
1071 controller='files', action='index', revision='tip',
1078 f_path='', annotate=True, conditions={'function': check_repo},
1072 f_path='', annotate=True, conditions={'function': check_repo},
1079 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1073 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1080
1074
1081 rmap.connect('files_edit',
1075 rmap.connect('files_edit',
1082 '/{repo_name}/edit/{revision}/{f_path}',
1076 '/{repo_name}/edit/{revision}/{f_path}',
1083 controller='files', action='edit', revision='tip',
1077 controller='files', action='edit', revision='tip',
1084 f_path='',
1078 f_path='',
1085 conditions={'function': check_repo, 'method': ['POST']},
1079 conditions={'function': check_repo, 'method': ['POST']},
1086 requirements=URL_NAME_REQUIREMENTS)
1080 requirements=URL_NAME_REQUIREMENTS)
1087
1081
1088 rmap.connect('files_edit_home',
1082 rmap.connect('files_edit_home',
1089 '/{repo_name}/edit/{revision}/{f_path}',
1083 '/{repo_name}/edit/{revision}/{f_path}',
1090 controller='files', action='edit_home', revision='tip',
1084 controller='files', action='edit_home', revision='tip',
1091 f_path='', conditions={'function': check_repo},
1085 f_path='', conditions={'function': check_repo},
1092 requirements=URL_NAME_REQUIREMENTS)
1086 requirements=URL_NAME_REQUIREMENTS)
1093
1087
1094 rmap.connect('files_add',
1088 rmap.connect('files_add',
1095 '/{repo_name}/add/{revision}/{f_path}',
1089 '/{repo_name}/add/{revision}/{f_path}',
1096 controller='files', action='add', revision='tip',
1090 controller='files', action='add', revision='tip',
1097 f_path='',
1091 f_path='',
1098 conditions={'function': check_repo, 'method': ['POST']},
1092 conditions={'function': check_repo, 'method': ['POST']},
1099 requirements=URL_NAME_REQUIREMENTS)
1093 requirements=URL_NAME_REQUIREMENTS)
1100
1094
1101 rmap.connect('files_add_home',
1095 rmap.connect('files_add_home',
1102 '/{repo_name}/add/{revision}/{f_path}',
1096 '/{repo_name}/add/{revision}/{f_path}',
1103 controller='files', action='add_home', revision='tip',
1097 controller='files', action='add_home', revision='tip',
1104 f_path='', conditions={'function': check_repo},
1098 f_path='', conditions={'function': check_repo},
1105 requirements=URL_NAME_REQUIREMENTS)
1099 requirements=URL_NAME_REQUIREMENTS)
1106
1100
1107 rmap.connect('files_delete',
1101 rmap.connect('files_delete',
1108 '/{repo_name}/delete/{revision}/{f_path}',
1102 '/{repo_name}/delete/{revision}/{f_path}',
1109 controller='files', action='delete', revision='tip',
1103 controller='files', action='delete', revision='tip',
1110 f_path='',
1104 f_path='',
1111 conditions={'function': check_repo, 'method': ['POST']},
1105 conditions={'function': check_repo, 'method': ['POST']},
1112 requirements=URL_NAME_REQUIREMENTS)
1106 requirements=URL_NAME_REQUIREMENTS)
1113
1107
1114 rmap.connect('files_delete_home',
1108 rmap.connect('files_delete_home',
1115 '/{repo_name}/delete/{revision}/{f_path}',
1109 '/{repo_name}/delete/{revision}/{f_path}',
1116 controller='files', action='delete_home', revision='tip',
1110 controller='files', action='delete_home', revision='tip',
1117 f_path='', conditions={'function': check_repo},
1111 f_path='', conditions={'function': check_repo},
1118 requirements=URL_NAME_REQUIREMENTS)
1112 requirements=URL_NAME_REQUIREMENTS)
1119
1113
1120 rmap.connect('files_archive_home', '/{repo_name}/archive/{fname}',
1114 rmap.connect('files_archive_home', '/{repo_name}/archive/{fname}',
1121 controller='files', action='archivefile',
1115 controller='files', action='archivefile',
1122 conditions={'function': check_repo},
1116 conditions={'function': check_repo},
1123 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1117 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1124
1118
1125 rmap.connect('files_nodelist_home',
1119 rmap.connect('files_nodelist_home',
1126 '/{repo_name}/nodelist/{revision}/{f_path}',
1120 '/{repo_name}/nodelist/{revision}/{f_path}',
1127 controller='files', action='nodelist',
1121 controller='files', action='nodelist',
1128 conditions={'function': check_repo},
1122 conditions={'function': check_repo},
1129 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1123 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1130
1124
1131 rmap.connect('files_nodetree_full',
1125 rmap.connect('files_nodetree_full',
1132 '/{repo_name}/nodetree_full/{commit_id}/{f_path}',
1126 '/{repo_name}/nodetree_full/{commit_id}/{f_path}',
1133 controller='files', action='nodetree_full',
1127 controller='files', action='nodetree_full',
1134 conditions={'function': check_repo},
1128 conditions={'function': check_repo},
1135 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1129 requirements=URL_NAME_REQUIREMENTS, jsroute=True)
1136
1130
1137 rmap.connect('repo_fork_create_home', '/{repo_name}/fork',
1131 rmap.connect('repo_fork_create_home', '/{repo_name}/fork',
1138 controller='forks', action='fork_create',
1132 controller='forks', action='fork_create',
1139 conditions={'function': check_repo, 'method': ['POST']},
1133 conditions={'function': check_repo, 'method': ['POST']},
1140 requirements=URL_NAME_REQUIREMENTS)
1134 requirements=URL_NAME_REQUIREMENTS)
1141
1135
1142 rmap.connect('repo_fork_home', '/{repo_name}/fork',
1136 rmap.connect('repo_fork_home', '/{repo_name}/fork',
1143 controller='forks', action='fork',
1137 controller='forks', action='fork',
1144 conditions={'function': check_repo},
1138 conditions={'function': check_repo},
1145 requirements=URL_NAME_REQUIREMENTS)
1139 requirements=URL_NAME_REQUIREMENTS)
1146
1140
1147 rmap.connect('repo_forks_home', '/{repo_name}/forks',
1141 rmap.connect('repo_forks_home', '/{repo_name}/forks',
1148 controller='forks', action='forks',
1142 controller='forks', action='forks',
1149 conditions={'function': check_repo},
1143 conditions={'function': check_repo},
1150 requirements=URL_NAME_REQUIREMENTS)
1144 requirements=URL_NAME_REQUIREMENTS)
1151
1145
1152 rmap.connect('repo_followers_home', '/{repo_name}/followers',
1146 rmap.connect('repo_followers_home', '/{repo_name}/followers',
1153 controller='followers', action='followers',
1147 controller='followers', action='followers',
1154 conditions={'function': check_repo},
1148 conditions={'function': check_repo},
1155 requirements=URL_NAME_REQUIREMENTS)
1149 requirements=URL_NAME_REQUIREMENTS)
1156
1150
1157 # must be here for proper group/repo catching pattern
1151 # must be here for proper group/repo catching pattern
1158 _connect_with_slash(
1152 _connect_with_slash(
1159 rmap, 'repo_group_home', '/{group_name}',
1153 rmap, 'repo_group_home', '/{group_name}',
1160 controller='home', action='index_repo_group',
1154 controller='home', action='index_repo_group',
1161 conditions={'function': check_group},
1155 conditions={'function': check_group},
1162 requirements=URL_NAME_REQUIREMENTS)
1156 requirements=URL_NAME_REQUIREMENTS)
1163
1157
1164 # catch all, at the end
1158 # catch all, at the end
1165 _connect_with_slash(
1159 _connect_with_slash(
1166 rmap, 'summary_home', '/{repo_name}', jsroute=True,
1160 rmap, 'summary_home', '/{repo_name}', jsroute=True,
1167 controller='summary', action='index',
1161 controller='summary', action='index',
1168 conditions={'function': check_repo},
1162 conditions={'function': check_repo},
1169 requirements=URL_NAME_REQUIREMENTS)
1163 requirements=URL_NAME_REQUIREMENTS)
1170
1164
1171 return rmap
1165 return rmap
1172
1166
1173
1167
1174 def _connect_with_slash(mapper, name, path, *args, **kwargs):
1168 def _connect_with_slash(mapper, name, path, *args, **kwargs):
1175 """
1169 """
1176 Connect a route with an optional trailing slash in `path`.
1170 Connect a route with an optional trailing slash in `path`.
1177 """
1171 """
1178 mapper.connect(name + '_slash', path + '/', *args, **kwargs)
1172 mapper.connect(name + '_slash', path + '/', *args, **kwargs)
1179 mapper.connect(name, path, *args, **kwargs)
1173 mapper.connect(name, path, *args, **kwargs)
@@ -1,890 +1,842 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 """
22 """
23 settings controller for rhodecode admin
23 settings controller for rhodecode admin
24 """
24 """
25
25
26 import collections
26 import collections
27 import logging
27 import logging
28 import urllib2
28 import urllib2
29
29
30 import datetime
30 import datetime
31 import formencode
31 import formencode
32 from formencode import htmlfill
32 from formencode import htmlfill
33 import packaging.version
33 import packaging.version
34 from pylons import request, tmpl_context as c, url, config
34 from pylons import request, tmpl_context as c, url, config
35 from pylons.controllers.util import redirect
35 from pylons.controllers.util import redirect
36 from pylons.i18n.translation import _, lazy_ugettext
36 from pylons.i18n.translation import _, lazy_ugettext
37 from pyramid.threadlocal import get_current_registry
37 from pyramid.threadlocal import get_current_registry
38 from webob.exc import HTTPBadRequest
38 from webob.exc import HTTPBadRequest
39
39
40 import rhodecode
40 import rhodecode
41 from rhodecode.admin.navigation import navigation_list
41 from rhodecode.admin.navigation import navigation_list
42 from rhodecode.lib import auth
42 from rhodecode.lib import auth
43 from rhodecode.lib import helpers as h
43 from rhodecode.lib import helpers as h
44 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
44 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
45 from rhodecode.lib.base import BaseController, render
45 from rhodecode.lib.base import BaseController, render
46 from rhodecode.lib.celerylib import tasks, run_task
46 from rhodecode.lib.celerylib import tasks, run_task
47 from rhodecode.lib.utils import repo2db_mapper
47 from rhodecode.lib.utils import repo2db_mapper
48 from rhodecode.lib.utils2 import (
48 from rhodecode.lib.utils2 import (
49 str2bool, safe_unicode, AttributeDict, safe_int)
49 str2bool, safe_unicode, AttributeDict, safe_int)
50 from rhodecode.lib.compat import OrderedDict
50 from rhodecode.lib.compat import OrderedDict
51 from rhodecode.lib.ext_json import json
51 from rhodecode.lib.ext_json import json
52 from rhodecode.lib.utils import jsonify
52 from rhodecode.lib.utils import jsonify
53 from rhodecode.lib import system_info
54 from rhodecode.lib import user_sessions
55
53
56 from rhodecode.model.db import RhodeCodeUi, Repository
54 from rhodecode.model.db import RhodeCodeUi, Repository
57 from rhodecode.model.forms import ApplicationSettingsForm, \
55 from rhodecode.model.forms import ApplicationSettingsForm, \
58 ApplicationUiSettingsForm, ApplicationVisualisationForm, \
56 ApplicationUiSettingsForm, ApplicationVisualisationForm, \
59 LabsSettingsForm, IssueTrackerPatternsForm
57 LabsSettingsForm, IssueTrackerPatternsForm
60 from rhodecode.model.repo_group import RepoGroupModel
58 from rhodecode.model.repo_group import RepoGroupModel
61
59
62 from rhodecode.model.scm import ScmModel
60 from rhodecode.model.scm import ScmModel
63 from rhodecode.model.notification import EmailNotificationModel
61 from rhodecode.model.notification import EmailNotificationModel
64 from rhodecode.model.meta import Session
62 from rhodecode.model.meta import Session
65 from rhodecode.model.settings import (
63 from rhodecode.model.settings import (
66 IssueTrackerSettingsModel, VcsSettingsModel, SettingNotFound,
64 IssueTrackerSettingsModel, VcsSettingsModel, SettingNotFound,
67 SettingsModel)
65 SettingsModel)
68
66
69 from rhodecode.model.supervisor import SupervisorModel, SUPERVISOR_MASTER
67 from rhodecode.model.supervisor import SupervisorModel, SUPERVISOR_MASTER
70 from rhodecode.svn_support.config_keys import generate_config
68 from rhodecode.svn_support.config_keys import generate_config
71
69
72
70
73 log = logging.getLogger(__name__)
71 log = logging.getLogger(__name__)
74
72
75
73
76 class SettingsController(BaseController):
74 class SettingsController(BaseController):
77 """REST Controller styled on the Atom Publishing Protocol"""
75 """REST Controller styled on the Atom Publishing Protocol"""
78 # To properly map this controller, ensure your config/routing.py
76 # To properly map this controller, ensure your config/routing.py
79 # file has a resource setup:
77 # file has a resource setup:
80 # map.resource('setting', 'settings', controller='admin/settings',
78 # map.resource('setting', 'settings', controller='admin/settings',
81 # path_prefix='/admin', name_prefix='admin_')
79 # path_prefix='/admin', name_prefix='admin_')
82
80
83 @LoginRequired()
81 @LoginRequired()
84 def __before__(self):
82 def __before__(self):
85 super(SettingsController, self).__before__()
83 super(SettingsController, self).__before__()
86 c.labs_active = str2bool(
84 c.labs_active = str2bool(
87 rhodecode.CONFIG.get('labs_settings_active', 'true'))
85 rhodecode.CONFIG.get('labs_settings_active', 'true'))
88 c.navlist = navigation_list(request)
86 c.navlist = navigation_list(request)
89
87
90 def _get_hg_ui_settings(self):
88 def _get_hg_ui_settings(self):
91 ret = RhodeCodeUi.query().all()
89 ret = RhodeCodeUi.query().all()
92
90
93 if not ret:
91 if not ret:
94 raise Exception('Could not get application ui settings !')
92 raise Exception('Could not get application ui settings !')
95 settings = {}
93 settings = {}
96 for each in ret:
94 for each in ret:
97 k = each.ui_key
95 k = each.ui_key
98 v = each.ui_value
96 v = each.ui_value
99 if k == '/':
97 if k == '/':
100 k = 'root_path'
98 k = 'root_path'
101
99
102 if k in ['push_ssl', 'publish']:
100 if k in ['push_ssl', 'publish']:
103 v = str2bool(v)
101 v = str2bool(v)
104
102
105 if k.find('.') != -1:
103 if k.find('.') != -1:
106 k = k.replace('.', '_')
104 k = k.replace('.', '_')
107
105
108 if each.ui_section in ['hooks', 'extensions']:
106 if each.ui_section in ['hooks', 'extensions']:
109 v = each.ui_active
107 v = each.ui_active
110
108
111 settings[each.ui_section + '_' + k] = v
109 settings[each.ui_section + '_' + k] = v
112 return settings
110 return settings
113
111
114 @HasPermissionAllDecorator('hg.admin')
112 @HasPermissionAllDecorator('hg.admin')
115 @auth.CSRFRequired()
113 @auth.CSRFRequired()
116 @jsonify
114 @jsonify
117 def delete_svn_pattern(self):
115 def delete_svn_pattern(self):
118 if not request.is_xhr:
116 if not request.is_xhr:
119 raise HTTPBadRequest()
117 raise HTTPBadRequest()
120
118
121 delete_pattern_id = request.POST.get('delete_svn_pattern')
119 delete_pattern_id = request.POST.get('delete_svn_pattern')
122 model = VcsSettingsModel()
120 model = VcsSettingsModel()
123 try:
121 try:
124 model.delete_global_svn_pattern(delete_pattern_id)
122 model.delete_global_svn_pattern(delete_pattern_id)
125 except SettingNotFound:
123 except SettingNotFound:
126 raise HTTPBadRequest()
124 raise HTTPBadRequest()
127
125
128 Session().commit()
126 Session().commit()
129 return True
127 return True
130
128
131 @HasPermissionAllDecorator('hg.admin')
129 @HasPermissionAllDecorator('hg.admin')
132 @auth.CSRFRequired()
130 @auth.CSRFRequired()
133 def settings_vcs_update(self):
131 def settings_vcs_update(self):
134 """POST /admin/settings: All items in the collection"""
132 """POST /admin/settings: All items in the collection"""
135 # url('admin_settings_vcs')
133 # url('admin_settings_vcs')
136 c.active = 'vcs'
134 c.active = 'vcs'
137
135
138 model = VcsSettingsModel()
136 model = VcsSettingsModel()
139 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
137 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
140 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
138 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
141
139
142 # TODO: Replace with request.registry after migrating to pyramid.
140 # TODO: Replace with request.registry after migrating to pyramid.
143 pyramid_settings = get_current_registry().settings
141 pyramid_settings = get_current_registry().settings
144 c.svn_proxy_generate_config = pyramid_settings[generate_config]
142 c.svn_proxy_generate_config = pyramid_settings[generate_config]
145
143
146 application_form = ApplicationUiSettingsForm()()
144 application_form = ApplicationUiSettingsForm()()
147
145
148 try:
146 try:
149 form_result = application_form.to_python(dict(request.POST))
147 form_result = application_form.to_python(dict(request.POST))
150 except formencode.Invalid as errors:
148 except formencode.Invalid as errors:
151 h.flash(
149 h.flash(
152 _("Some form inputs contain invalid data."),
150 _("Some form inputs contain invalid data."),
153 category='error')
151 category='error')
154 return htmlfill.render(
152 return htmlfill.render(
155 render('admin/settings/settings.mako'),
153 render('admin/settings/settings.mako'),
156 defaults=errors.value,
154 defaults=errors.value,
157 errors=errors.error_dict or {},
155 errors=errors.error_dict or {},
158 prefix_error=False,
156 prefix_error=False,
159 encoding="UTF-8",
157 encoding="UTF-8",
160 force_defaults=False
158 force_defaults=False
161 )
159 )
162
160
163 try:
161 try:
164 if c.visual.allow_repo_location_change:
162 if c.visual.allow_repo_location_change:
165 model.update_global_path_setting(
163 model.update_global_path_setting(
166 form_result['paths_root_path'])
164 form_result['paths_root_path'])
167
165
168 model.update_global_ssl_setting(form_result['web_push_ssl'])
166 model.update_global_ssl_setting(form_result['web_push_ssl'])
169 model.update_global_hook_settings(form_result)
167 model.update_global_hook_settings(form_result)
170
168
171 model.create_or_update_global_svn_settings(form_result)
169 model.create_or_update_global_svn_settings(form_result)
172 model.create_or_update_global_hg_settings(form_result)
170 model.create_or_update_global_hg_settings(form_result)
173 model.create_or_update_global_pr_settings(form_result)
171 model.create_or_update_global_pr_settings(form_result)
174 except Exception:
172 except Exception:
175 log.exception("Exception while updating settings")
173 log.exception("Exception while updating settings")
176 h.flash(_('Error occurred during updating '
174 h.flash(_('Error occurred during updating '
177 'application settings'), category='error')
175 'application settings'), category='error')
178 else:
176 else:
179 Session().commit()
177 Session().commit()
180 h.flash(_('Updated VCS settings'), category='success')
178 h.flash(_('Updated VCS settings'), category='success')
181 return redirect(url('admin_settings_vcs'))
179 return redirect(url('admin_settings_vcs'))
182
180
183 return htmlfill.render(
181 return htmlfill.render(
184 render('admin/settings/settings.mako'),
182 render('admin/settings/settings.mako'),
185 defaults=self._form_defaults(),
183 defaults=self._form_defaults(),
186 encoding="UTF-8",
184 encoding="UTF-8",
187 force_defaults=False)
185 force_defaults=False)
188
186
189 @HasPermissionAllDecorator('hg.admin')
187 @HasPermissionAllDecorator('hg.admin')
190 def settings_vcs(self):
188 def settings_vcs(self):
191 """GET /admin/settings: All items in the collection"""
189 """GET /admin/settings: All items in the collection"""
192 # url('admin_settings_vcs')
190 # url('admin_settings_vcs')
193 c.active = 'vcs'
191 c.active = 'vcs'
194 model = VcsSettingsModel()
192 model = VcsSettingsModel()
195 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
193 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
196 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
194 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
197
195
198 # TODO: Replace with request.registry after migrating to pyramid.
196 # TODO: Replace with request.registry after migrating to pyramid.
199 pyramid_settings = get_current_registry().settings
197 pyramid_settings = get_current_registry().settings
200 c.svn_proxy_generate_config = pyramid_settings[generate_config]
198 c.svn_proxy_generate_config = pyramid_settings[generate_config]
201
199
202 return htmlfill.render(
200 return htmlfill.render(
203 render('admin/settings/settings.mako'),
201 render('admin/settings/settings.mako'),
204 defaults=self._form_defaults(),
202 defaults=self._form_defaults(),
205 encoding="UTF-8",
203 encoding="UTF-8",
206 force_defaults=False)
204 force_defaults=False)
207
205
208 @HasPermissionAllDecorator('hg.admin')
206 @HasPermissionAllDecorator('hg.admin')
209 @auth.CSRFRequired()
207 @auth.CSRFRequired()
210 def settings_mapping_update(self):
208 def settings_mapping_update(self):
211 """POST /admin/settings/mapping: All items in the collection"""
209 """POST /admin/settings/mapping: All items in the collection"""
212 # url('admin_settings_mapping')
210 # url('admin_settings_mapping')
213 c.active = 'mapping'
211 c.active = 'mapping'
214 rm_obsolete = request.POST.get('destroy', False)
212 rm_obsolete = request.POST.get('destroy', False)
215 invalidate_cache = request.POST.get('invalidate', False)
213 invalidate_cache = request.POST.get('invalidate', False)
216 log.debug(
214 log.debug(
217 'rescanning repo location with destroy obsolete=%s', rm_obsolete)
215 'rescanning repo location with destroy obsolete=%s', rm_obsolete)
218
216
219 if invalidate_cache:
217 if invalidate_cache:
220 log.debug('invalidating all repositories cache')
218 log.debug('invalidating all repositories cache')
221 for repo in Repository.get_all():
219 for repo in Repository.get_all():
222 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
220 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
223
221
224 filesystem_repos = ScmModel().repo_scan()
222 filesystem_repos = ScmModel().repo_scan()
225 added, removed = repo2db_mapper(filesystem_repos, rm_obsolete)
223 added, removed = repo2db_mapper(filesystem_repos, rm_obsolete)
226 _repr = lambda l: ', '.join(map(safe_unicode, l)) or '-'
224 _repr = lambda l: ', '.join(map(safe_unicode, l)) or '-'
227 h.flash(_('Repositories successfully '
225 h.flash(_('Repositories successfully '
228 'rescanned added: %s ; removed: %s') %
226 'rescanned added: %s ; removed: %s') %
229 (_repr(added), _repr(removed)),
227 (_repr(added), _repr(removed)),
230 category='success')
228 category='success')
231 return redirect(url('admin_settings_mapping'))
229 return redirect(url('admin_settings_mapping'))
232
230
233 @HasPermissionAllDecorator('hg.admin')
231 @HasPermissionAllDecorator('hg.admin')
234 def settings_mapping(self):
232 def settings_mapping(self):
235 """GET /admin/settings/mapping: All items in the collection"""
233 """GET /admin/settings/mapping: All items in the collection"""
236 # url('admin_settings_mapping')
234 # url('admin_settings_mapping')
237 c.active = 'mapping'
235 c.active = 'mapping'
238
236
239 return htmlfill.render(
237 return htmlfill.render(
240 render('admin/settings/settings.mako'),
238 render('admin/settings/settings.mako'),
241 defaults=self._form_defaults(),
239 defaults=self._form_defaults(),
242 encoding="UTF-8",
240 encoding="UTF-8",
243 force_defaults=False)
241 force_defaults=False)
244
242
245 @HasPermissionAllDecorator('hg.admin')
243 @HasPermissionAllDecorator('hg.admin')
246 @auth.CSRFRequired()
244 @auth.CSRFRequired()
247 def settings_global_update(self):
245 def settings_global_update(self):
248 """POST /admin/settings/global: All items in the collection"""
246 """POST /admin/settings/global: All items in the collection"""
249 # url('admin_settings_global')
247 # url('admin_settings_global')
250 c.active = 'global'
248 c.active = 'global'
251 c.personal_repo_group_default_pattern = RepoGroupModel()\
249 c.personal_repo_group_default_pattern = RepoGroupModel()\
252 .get_personal_group_name_pattern()
250 .get_personal_group_name_pattern()
253 application_form = ApplicationSettingsForm()()
251 application_form = ApplicationSettingsForm()()
254 try:
252 try:
255 form_result = application_form.to_python(dict(request.POST))
253 form_result = application_form.to_python(dict(request.POST))
256 except formencode.Invalid as errors:
254 except formencode.Invalid as errors:
257 return htmlfill.render(
255 return htmlfill.render(
258 render('admin/settings/settings.mako'),
256 render('admin/settings/settings.mako'),
259 defaults=errors.value,
257 defaults=errors.value,
260 errors=errors.error_dict or {},
258 errors=errors.error_dict or {},
261 prefix_error=False,
259 prefix_error=False,
262 encoding="UTF-8",
260 encoding="UTF-8",
263 force_defaults=False)
261 force_defaults=False)
264
262
265 try:
263 try:
266 settings = [
264 settings = [
267 ('title', 'rhodecode_title', 'unicode'),
265 ('title', 'rhodecode_title', 'unicode'),
268 ('realm', 'rhodecode_realm', 'unicode'),
266 ('realm', 'rhodecode_realm', 'unicode'),
269 ('pre_code', 'rhodecode_pre_code', 'unicode'),
267 ('pre_code', 'rhodecode_pre_code', 'unicode'),
270 ('post_code', 'rhodecode_post_code', 'unicode'),
268 ('post_code', 'rhodecode_post_code', 'unicode'),
271 ('captcha_public_key', 'rhodecode_captcha_public_key', 'unicode'),
269 ('captcha_public_key', 'rhodecode_captcha_public_key', 'unicode'),
272 ('captcha_private_key', 'rhodecode_captcha_private_key', 'unicode'),
270 ('captcha_private_key', 'rhodecode_captcha_private_key', 'unicode'),
273 ('create_personal_repo_group', 'rhodecode_create_personal_repo_group', 'bool'),
271 ('create_personal_repo_group', 'rhodecode_create_personal_repo_group', 'bool'),
274 ('personal_repo_group_pattern', 'rhodecode_personal_repo_group_pattern', 'unicode'),
272 ('personal_repo_group_pattern', 'rhodecode_personal_repo_group_pattern', 'unicode'),
275 ]
273 ]
276 for setting, form_key, type_ in settings:
274 for setting, form_key, type_ in settings:
277 sett = SettingsModel().create_or_update_setting(
275 sett = SettingsModel().create_or_update_setting(
278 setting, form_result[form_key], type_)
276 setting, form_result[form_key], type_)
279 Session().add(sett)
277 Session().add(sett)
280
278
281 Session().commit()
279 Session().commit()
282 SettingsModel().invalidate_settings_cache()
280 SettingsModel().invalidate_settings_cache()
283 h.flash(_('Updated application settings'), category='success')
281 h.flash(_('Updated application settings'), category='success')
284 except Exception:
282 except Exception:
285 log.exception("Exception while updating application settings")
283 log.exception("Exception while updating application settings")
286 h.flash(
284 h.flash(
287 _('Error occurred during updating application settings'),
285 _('Error occurred during updating application settings'),
288 category='error')
286 category='error')
289
287
290 return redirect(url('admin_settings_global'))
288 return redirect(url('admin_settings_global'))
291
289
292 @HasPermissionAllDecorator('hg.admin')
290 @HasPermissionAllDecorator('hg.admin')
293 def settings_global(self):
291 def settings_global(self):
294 """GET /admin/settings/global: All items in the collection"""
292 """GET /admin/settings/global: All items in the collection"""
295 # url('admin_settings_global')
293 # url('admin_settings_global')
296 c.active = 'global'
294 c.active = 'global'
297 c.personal_repo_group_default_pattern = RepoGroupModel()\
295 c.personal_repo_group_default_pattern = RepoGroupModel()\
298 .get_personal_group_name_pattern()
296 .get_personal_group_name_pattern()
299
297
300 return htmlfill.render(
298 return htmlfill.render(
301 render('admin/settings/settings.mako'),
299 render('admin/settings/settings.mako'),
302 defaults=self._form_defaults(),
300 defaults=self._form_defaults(),
303 encoding="UTF-8",
301 encoding="UTF-8",
304 force_defaults=False)
302 force_defaults=False)
305
303
306 @HasPermissionAllDecorator('hg.admin')
304 @HasPermissionAllDecorator('hg.admin')
307 @auth.CSRFRequired()
305 @auth.CSRFRequired()
308 def settings_visual_update(self):
306 def settings_visual_update(self):
309 """POST /admin/settings/visual: All items in the collection"""
307 """POST /admin/settings/visual: All items in the collection"""
310 # url('admin_settings_visual')
308 # url('admin_settings_visual')
311 c.active = 'visual'
309 c.active = 'visual'
312 application_form = ApplicationVisualisationForm()()
310 application_form = ApplicationVisualisationForm()()
313 try:
311 try:
314 form_result = application_form.to_python(dict(request.POST))
312 form_result = application_form.to_python(dict(request.POST))
315 except formencode.Invalid as errors:
313 except formencode.Invalid as errors:
316 return htmlfill.render(
314 return htmlfill.render(
317 render('admin/settings/settings.mako'),
315 render('admin/settings/settings.mako'),
318 defaults=errors.value,
316 defaults=errors.value,
319 errors=errors.error_dict or {},
317 errors=errors.error_dict or {},
320 prefix_error=False,
318 prefix_error=False,
321 encoding="UTF-8",
319 encoding="UTF-8",
322 force_defaults=False
320 force_defaults=False
323 )
321 )
324
322
325 try:
323 try:
326 settings = [
324 settings = [
327 ('show_public_icon', 'rhodecode_show_public_icon', 'bool'),
325 ('show_public_icon', 'rhodecode_show_public_icon', 'bool'),
328 ('show_private_icon', 'rhodecode_show_private_icon', 'bool'),
326 ('show_private_icon', 'rhodecode_show_private_icon', 'bool'),
329 ('stylify_metatags', 'rhodecode_stylify_metatags', 'bool'),
327 ('stylify_metatags', 'rhodecode_stylify_metatags', 'bool'),
330 ('repository_fields', 'rhodecode_repository_fields', 'bool'),
328 ('repository_fields', 'rhodecode_repository_fields', 'bool'),
331 ('dashboard_items', 'rhodecode_dashboard_items', 'int'),
329 ('dashboard_items', 'rhodecode_dashboard_items', 'int'),
332 ('admin_grid_items', 'rhodecode_admin_grid_items', 'int'),
330 ('admin_grid_items', 'rhodecode_admin_grid_items', 'int'),
333 ('show_version', 'rhodecode_show_version', 'bool'),
331 ('show_version', 'rhodecode_show_version', 'bool'),
334 ('use_gravatar', 'rhodecode_use_gravatar', 'bool'),
332 ('use_gravatar', 'rhodecode_use_gravatar', 'bool'),
335 ('markup_renderer', 'rhodecode_markup_renderer', 'unicode'),
333 ('markup_renderer', 'rhodecode_markup_renderer', 'unicode'),
336 ('gravatar_url', 'rhodecode_gravatar_url', 'unicode'),
334 ('gravatar_url', 'rhodecode_gravatar_url', 'unicode'),
337 ('clone_uri_tmpl', 'rhodecode_clone_uri_tmpl', 'unicode'),
335 ('clone_uri_tmpl', 'rhodecode_clone_uri_tmpl', 'unicode'),
338 ('support_url', 'rhodecode_support_url', 'unicode'),
336 ('support_url', 'rhodecode_support_url', 'unicode'),
339 ('show_revision_number', 'rhodecode_show_revision_number', 'bool'),
337 ('show_revision_number', 'rhodecode_show_revision_number', 'bool'),
340 ('show_sha_length', 'rhodecode_show_sha_length', 'int'),
338 ('show_sha_length', 'rhodecode_show_sha_length', 'int'),
341 ]
339 ]
342 for setting, form_key, type_ in settings:
340 for setting, form_key, type_ in settings:
343 sett = SettingsModel().create_or_update_setting(
341 sett = SettingsModel().create_or_update_setting(
344 setting, form_result[form_key], type_)
342 setting, form_result[form_key], type_)
345 Session().add(sett)
343 Session().add(sett)
346
344
347 Session().commit()
345 Session().commit()
348 SettingsModel().invalidate_settings_cache()
346 SettingsModel().invalidate_settings_cache()
349 h.flash(_('Updated visualisation settings'), category='success')
347 h.flash(_('Updated visualisation settings'), category='success')
350 except Exception:
348 except Exception:
351 log.exception("Exception updating visualization settings")
349 log.exception("Exception updating visualization settings")
352 h.flash(_('Error occurred during updating '
350 h.flash(_('Error occurred during updating '
353 'visualisation settings'),
351 'visualisation settings'),
354 category='error')
352 category='error')
355
353
356 return redirect(url('admin_settings_visual'))
354 return redirect(url('admin_settings_visual'))
357
355
358 @HasPermissionAllDecorator('hg.admin')
356 @HasPermissionAllDecorator('hg.admin')
359 def settings_visual(self):
357 def settings_visual(self):
360 """GET /admin/settings/visual: All items in the collection"""
358 """GET /admin/settings/visual: All items in the collection"""
361 # url('admin_settings_visual')
359 # url('admin_settings_visual')
362 c.active = 'visual'
360 c.active = 'visual'
363
361
364 return htmlfill.render(
362 return htmlfill.render(
365 render('admin/settings/settings.mako'),
363 render('admin/settings/settings.mako'),
366 defaults=self._form_defaults(),
364 defaults=self._form_defaults(),
367 encoding="UTF-8",
365 encoding="UTF-8",
368 force_defaults=False)
366 force_defaults=False)
369
367
370 @HasPermissionAllDecorator('hg.admin')
368 @HasPermissionAllDecorator('hg.admin')
371 @auth.CSRFRequired()
369 @auth.CSRFRequired()
372 def settings_issuetracker_test(self):
370 def settings_issuetracker_test(self):
373 if request.is_xhr:
371 if request.is_xhr:
374 return h.urlify_commit_message(
372 return h.urlify_commit_message(
375 request.POST.get('test_text', ''),
373 request.POST.get('test_text', ''),
376 'repo_group/test_repo1')
374 'repo_group/test_repo1')
377 else:
375 else:
378 raise HTTPBadRequest()
376 raise HTTPBadRequest()
379
377
380 @HasPermissionAllDecorator('hg.admin')
378 @HasPermissionAllDecorator('hg.admin')
381 @auth.CSRFRequired()
379 @auth.CSRFRequired()
382 def settings_issuetracker_delete(self):
380 def settings_issuetracker_delete(self):
383 uid = request.POST.get('uid')
381 uid = request.POST.get('uid')
384 IssueTrackerSettingsModel().delete_entries(uid)
382 IssueTrackerSettingsModel().delete_entries(uid)
385 h.flash(_('Removed issue tracker entry'), category='success')
383 h.flash(_('Removed issue tracker entry'), category='success')
386 return redirect(url('admin_settings_issuetracker'))
384 return redirect(url('admin_settings_issuetracker'))
387
385
388 @HasPermissionAllDecorator('hg.admin')
386 @HasPermissionAllDecorator('hg.admin')
389 def settings_issuetracker(self):
387 def settings_issuetracker(self):
390 """GET /admin/settings/issue-tracker: All items in the collection"""
388 """GET /admin/settings/issue-tracker: All items in the collection"""
391 # url('admin_settings_issuetracker')
389 # url('admin_settings_issuetracker')
392 c.active = 'issuetracker'
390 c.active = 'issuetracker'
393 defaults = SettingsModel().get_all_settings()
391 defaults = SettingsModel().get_all_settings()
394
392
395 entry_key = 'rhodecode_issuetracker_pat_'
393 entry_key = 'rhodecode_issuetracker_pat_'
396
394
397 c.issuetracker_entries = {}
395 c.issuetracker_entries = {}
398 for k, v in defaults.items():
396 for k, v in defaults.items():
399 if k.startswith(entry_key):
397 if k.startswith(entry_key):
400 uid = k[len(entry_key):]
398 uid = k[len(entry_key):]
401 c.issuetracker_entries[uid] = None
399 c.issuetracker_entries[uid] = None
402
400
403 for uid in c.issuetracker_entries:
401 for uid in c.issuetracker_entries:
404 c.issuetracker_entries[uid] = AttributeDict({
402 c.issuetracker_entries[uid] = AttributeDict({
405 'pat': defaults.get('rhodecode_issuetracker_pat_' + uid),
403 'pat': defaults.get('rhodecode_issuetracker_pat_' + uid),
406 'url': defaults.get('rhodecode_issuetracker_url_' + uid),
404 'url': defaults.get('rhodecode_issuetracker_url_' + uid),
407 'pref': defaults.get('rhodecode_issuetracker_pref_' + uid),
405 'pref': defaults.get('rhodecode_issuetracker_pref_' + uid),
408 'desc': defaults.get('rhodecode_issuetracker_desc_' + uid),
406 'desc': defaults.get('rhodecode_issuetracker_desc_' + uid),
409 })
407 })
410
408
411 return render('admin/settings/settings.mako')
409 return render('admin/settings/settings.mako')
412
410
413 @HasPermissionAllDecorator('hg.admin')
411 @HasPermissionAllDecorator('hg.admin')
414 @auth.CSRFRequired()
412 @auth.CSRFRequired()
415 def settings_issuetracker_save(self):
413 def settings_issuetracker_save(self):
416 settings_model = IssueTrackerSettingsModel()
414 settings_model = IssueTrackerSettingsModel()
417
415
418 form = IssueTrackerPatternsForm()().to_python(request.POST)
416 form = IssueTrackerPatternsForm()().to_python(request.POST)
419 if form:
417 if form:
420 for uid in form.get('delete_patterns', []):
418 for uid in form.get('delete_patterns', []):
421 settings_model.delete_entries(uid)
419 settings_model.delete_entries(uid)
422
420
423 for pattern in form.get('patterns', []):
421 for pattern in form.get('patterns', []):
424 for setting, value, type_ in pattern:
422 for setting, value, type_ in pattern:
425 sett = settings_model.create_or_update_setting(
423 sett = settings_model.create_or_update_setting(
426 setting, value, type_)
424 setting, value, type_)
427 Session().add(sett)
425 Session().add(sett)
428
426
429 Session().commit()
427 Session().commit()
430
428
431 SettingsModel().invalidate_settings_cache()
429 SettingsModel().invalidate_settings_cache()
432 h.flash(_('Updated issue tracker entries'), category='success')
430 h.flash(_('Updated issue tracker entries'), category='success')
433 return redirect(url('admin_settings_issuetracker'))
431 return redirect(url('admin_settings_issuetracker'))
434
432
435 @HasPermissionAllDecorator('hg.admin')
433 @HasPermissionAllDecorator('hg.admin')
436 @auth.CSRFRequired()
434 @auth.CSRFRequired()
437 def settings_email_update(self):
435 def settings_email_update(self):
438 """POST /admin/settings/email: All items in the collection"""
436 """POST /admin/settings/email: All items in the collection"""
439 # url('admin_settings_email')
437 # url('admin_settings_email')
440 c.active = 'email'
438 c.active = 'email'
441
439
442 test_email = request.POST.get('test_email')
440 test_email = request.POST.get('test_email')
443
441
444 if not test_email:
442 if not test_email:
445 h.flash(_('Please enter email address'), category='error')
443 h.flash(_('Please enter email address'), category='error')
446 return redirect(url('admin_settings_email'))
444 return redirect(url('admin_settings_email'))
447
445
448 email_kwargs = {
446 email_kwargs = {
449 'date': datetime.datetime.now(),
447 'date': datetime.datetime.now(),
450 'user': c.rhodecode_user,
448 'user': c.rhodecode_user,
451 'rhodecode_version': c.rhodecode_version
449 'rhodecode_version': c.rhodecode_version
452 }
450 }
453
451
454 (subject, headers, email_body,
452 (subject, headers, email_body,
455 email_body_plaintext) = EmailNotificationModel().render_email(
453 email_body_plaintext) = EmailNotificationModel().render_email(
456 EmailNotificationModel.TYPE_EMAIL_TEST, **email_kwargs)
454 EmailNotificationModel.TYPE_EMAIL_TEST, **email_kwargs)
457
455
458 recipients = [test_email] if test_email else None
456 recipients = [test_email] if test_email else None
459
457
460 run_task(tasks.send_email, recipients, subject,
458 run_task(tasks.send_email, recipients, subject,
461 email_body_plaintext, email_body)
459 email_body_plaintext, email_body)
462
460
463 h.flash(_('Send email task created'), category='success')
461 h.flash(_('Send email task created'), category='success')
464 return redirect(url('admin_settings_email'))
462 return redirect(url('admin_settings_email'))
465
463
466 @HasPermissionAllDecorator('hg.admin')
464 @HasPermissionAllDecorator('hg.admin')
467 def settings_email(self):
465 def settings_email(self):
468 """GET /admin/settings/email: All items in the collection"""
466 """GET /admin/settings/email: All items in the collection"""
469 # url('admin_settings_email')
467 # url('admin_settings_email')
470 c.active = 'email'
468 c.active = 'email'
471 c.rhodecode_ini = rhodecode.CONFIG
469 c.rhodecode_ini = rhodecode.CONFIG
472
470
473 return htmlfill.render(
471 return htmlfill.render(
474 render('admin/settings/settings.mako'),
472 render('admin/settings/settings.mako'),
475 defaults=self._form_defaults(),
473 defaults=self._form_defaults(),
476 encoding="UTF-8",
474 encoding="UTF-8",
477 force_defaults=False)
475 force_defaults=False)
478
476
479 @HasPermissionAllDecorator('hg.admin')
477 @HasPermissionAllDecorator('hg.admin')
480 @auth.CSRFRequired()
478 @auth.CSRFRequired()
481 def settings_hooks_update(self):
479 def settings_hooks_update(self):
482 """POST or DELETE /admin/settings/hooks: All items in the collection"""
480 """POST or DELETE /admin/settings/hooks: All items in the collection"""
483 # url('admin_settings_hooks')
481 # url('admin_settings_hooks')
484 c.active = 'hooks'
482 c.active = 'hooks'
485 if c.visual.allow_custom_hooks_settings:
483 if c.visual.allow_custom_hooks_settings:
486 ui_key = request.POST.get('new_hook_ui_key')
484 ui_key = request.POST.get('new_hook_ui_key')
487 ui_value = request.POST.get('new_hook_ui_value')
485 ui_value = request.POST.get('new_hook_ui_value')
488
486
489 hook_id = request.POST.get('hook_id')
487 hook_id = request.POST.get('hook_id')
490 new_hook = False
488 new_hook = False
491
489
492 model = SettingsModel()
490 model = SettingsModel()
493 try:
491 try:
494 if ui_value and ui_key:
492 if ui_value and ui_key:
495 model.create_or_update_hook(ui_key, ui_value)
493 model.create_or_update_hook(ui_key, ui_value)
496 h.flash(_('Added new hook'), category='success')
494 h.flash(_('Added new hook'), category='success')
497 new_hook = True
495 new_hook = True
498 elif hook_id:
496 elif hook_id:
499 RhodeCodeUi.delete(hook_id)
497 RhodeCodeUi.delete(hook_id)
500 Session().commit()
498 Session().commit()
501
499
502 # check for edits
500 # check for edits
503 update = False
501 update = False
504 _d = request.POST.dict_of_lists()
502 _d = request.POST.dict_of_lists()
505 for k, v in zip(_d.get('hook_ui_key', []),
503 for k, v in zip(_d.get('hook_ui_key', []),
506 _d.get('hook_ui_value_new', [])):
504 _d.get('hook_ui_value_new', [])):
507 model.create_or_update_hook(k, v)
505 model.create_or_update_hook(k, v)
508 update = True
506 update = True
509
507
510 if update and not new_hook:
508 if update and not new_hook:
511 h.flash(_('Updated hooks'), category='success')
509 h.flash(_('Updated hooks'), category='success')
512 Session().commit()
510 Session().commit()
513 except Exception:
511 except Exception:
514 log.exception("Exception during hook creation")
512 log.exception("Exception during hook creation")
515 h.flash(_('Error occurred during hook creation'),
513 h.flash(_('Error occurred during hook creation'),
516 category='error')
514 category='error')
517
515
518 return redirect(url('admin_settings_hooks'))
516 return redirect(url('admin_settings_hooks'))
519
517
520 @HasPermissionAllDecorator('hg.admin')
518 @HasPermissionAllDecorator('hg.admin')
521 def settings_hooks(self):
519 def settings_hooks(self):
522 """GET /admin/settings/hooks: All items in the collection"""
520 """GET /admin/settings/hooks: All items in the collection"""
523 # url('admin_settings_hooks')
521 # url('admin_settings_hooks')
524 c.active = 'hooks'
522 c.active = 'hooks'
525
523
526 model = SettingsModel()
524 model = SettingsModel()
527 c.hooks = model.get_builtin_hooks()
525 c.hooks = model.get_builtin_hooks()
528 c.custom_hooks = model.get_custom_hooks()
526 c.custom_hooks = model.get_custom_hooks()
529
527
530 return htmlfill.render(
528 return htmlfill.render(
531 render('admin/settings/settings.mako'),
529 render('admin/settings/settings.mako'),
532 defaults=self._form_defaults(),
530 defaults=self._form_defaults(),
533 encoding="UTF-8",
531 encoding="UTF-8",
534 force_defaults=False)
532 force_defaults=False)
535
533
536 @HasPermissionAllDecorator('hg.admin')
534 @HasPermissionAllDecorator('hg.admin')
537 def settings_search(self):
535 def settings_search(self):
538 """GET /admin/settings/search: All items in the collection"""
536 """GET /admin/settings/search: All items in the collection"""
539 # url('admin_settings_search')
537 # url('admin_settings_search')
540 c.active = 'search'
538 c.active = 'search'
541
539
542 from rhodecode.lib.index import searcher_from_config
540 from rhodecode.lib.index import searcher_from_config
543 searcher = searcher_from_config(config)
541 searcher = searcher_from_config(config)
544 c.statistics = searcher.statistics()
542 c.statistics = searcher.statistics()
545
543
546 return render('admin/settings/settings.mako')
544 return render('admin/settings/settings.mako')
547
545
548 @HasPermissionAllDecorator('hg.admin')
546 @HasPermissionAllDecorator('hg.admin')
549 def settings_system(self):
547 def settings_system(self):
550 """GET /admin/settings/system: All items in the collection"""
548 """GET /admin/settings/system: All items in the collection"""
551 # url('admin_settings_system')
549 # url('admin_settings_system')
552 snapshot = str2bool(request.GET.get('snapshot'))
550 snapshot = str2bool(request.GET.get('snapshot'))
553 defaults = self._form_defaults()
551 defaults = self._form_defaults()
554
552
555 c.active = 'system'
553 c.active = 'system'
556 c.rhodecode_update_url = defaults.get('rhodecode_update_url')
554 c.rhodecode_update_url = defaults.get('rhodecode_update_url')
557 server_info = ScmModel().get_server_info(request.environ)
555 server_info = ScmModel().get_server_info(request.environ)
558
556
559 for key, val in server_info.iteritems():
557 for key, val in server_info.iteritems():
560 setattr(c, key, val)
558 setattr(c, key, val)
561
559
562 def val(name, subkey='human_value'):
560 def val(name, subkey='human_value'):
563 return server_info[name][subkey]
561 return server_info[name][subkey]
564
562
565 def state(name):
563 def state(name):
566 return server_info[name]['state']
564 return server_info[name]['state']
567
565
568 def val2(name):
566 def val2(name):
569 val = server_info[name]['human_value']
567 val = server_info[name]['human_value']
570 state = server_info[name]['state']
568 state = server_info[name]['state']
571 return val, state
569 return val, state
572
570
573 c.data_items = [
571 c.data_items = [
574 # update info
572 # update info
575 (_('Update info'), h.literal(
573 (_('Update info'), h.literal(
576 '<span class="link" id="check_for_update" >%s.</span>' % (
574 '<span class="link" id="check_for_update" >%s.</span>' % (
577 _('Check for updates')) +
575 _('Check for updates')) +
578 '<br/> <span >%s.</span>' % (_('Note: please make sure this server can access `%s` for the update link to work') % c.rhodecode_update_url)
576 '<br/> <span >%s.</span>' % (_('Note: please make sure this server can access `%s` for the update link to work') % c.rhodecode_update_url)
579 ), ''),
577 ), ''),
580
578
581 # RhodeCode specific
579 # RhodeCode specific
582 (_('RhodeCode Version'), val('rhodecode_app')['text'], state('rhodecode_app')),
580 (_('RhodeCode Version'), val('rhodecode_app')['text'], state('rhodecode_app')),
583 (_('RhodeCode Server IP'), val('server')['server_ip'], state('server')),
581 (_('RhodeCode Server IP'), val('server')['server_ip'], state('server')),
584 (_('RhodeCode Server ID'), val('server')['server_id'], state('server')),
582 (_('RhodeCode Server ID'), val('server')['server_id'], state('server')),
585 (_('RhodeCode Configuration'), val('rhodecode_config')['path'], state('rhodecode_config')),
583 (_('RhodeCode Configuration'), val('rhodecode_config')['path'], state('rhodecode_config')),
586 ('', '', ''), # spacer
584 ('', '', ''), # spacer
587
585
588 # Database
586 # Database
589 (_('Database'), val('database')['url'], state('database')),
587 (_('Database'), val('database')['url'], state('database')),
590 (_('Database version'), val('database')['version'], state('database')),
588 (_('Database version'), val('database')['version'], state('database')),
591 ('', '', ''), # spacer
589 ('', '', ''), # spacer
592
590
593 # Platform/Python
591 # Platform/Python
594 (_('Platform'), val('platform')['name'], state('platform')),
592 (_('Platform'), val('platform')['name'], state('platform')),
595 (_('Platform UUID'), val('platform')['uuid'], state('platform')),
593 (_('Platform UUID'), val('platform')['uuid'], state('platform')),
596 (_('Python version'), val('python')['version'], state('python')),
594 (_('Python version'), val('python')['version'], state('python')),
597 (_('Python path'), val('python')['executable'], state('python')),
595 (_('Python path'), val('python')['executable'], state('python')),
598 ('', '', ''), # spacer
596 ('', '', ''), # spacer
599
597
600 # Systems stats
598 # Systems stats
601 (_('CPU'), val('cpu'), state('cpu')),
599 (_('CPU'), val('cpu'), state('cpu')),
602 (_('Load'), val('load')['text'], state('load')),
600 (_('Load'), val('load')['text'], state('load')),
603 (_('Memory'), val('memory')['text'], state('memory')),
601 (_('Memory'), val('memory')['text'], state('memory')),
604 (_('Uptime'), val('uptime')['text'], state('uptime')),
602 (_('Uptime'), val('uptime')['text'], state('uptime')),
605 ('', '', ''), # spacer
603 ('', '', ''), # spacer
606
604
607 # Repo storage
605 # Repo storage
608 (_('Storage location'), val('storage')['path'], state('storage')),
606 (_('Storage location'), val('storage')['path'], state('storage')),
609 (_('Storage info'), val('storage')['text'], state('storage')),
607 (_('Storage info'), val('storage')['text'], state('storage')),
610 (_('Storage inodes'), val('storage_inodes')['text'], state('storage_inodes')),
608 (_('Storage inodes'), val('storage_inodes')['text'], state('storage_inodes')),
611
609
612 (_('Gist storage location'), val('storage_gist')['path'], state('storage_gist')),
610 (_('Gist storage location'), val('storage_gist')['path'], state('storage_gist')),
613 (_('Gist storage info'), val('storage_gist')['text'], state('storage_gist')),
611 (_('Gist storage info'), val('storage_gist')['text'], state('storage_gist')),
614
612
615 (_('Archive cache storage location'), val('storage_archive')['path'], state('storage_archive')),
613 (_('Archive cache storage location'), val('storage_archive')['path'], state('storage_archive')),
616 (_('Archive cache info'), val('storage_archive')['text'], state('storage_archive')),
614 (_('Archive cache info'), val('storage_archive')['text'], state('storage_archive')),
617
615
618 (_('Temp storage location'), val('storage_temp')['path'], state('storage_temp')),
616 (_('Temp storage location'), val('storage_temp')['path'], state('storage_temp')),
619 (_('Temp storage info'), val('storage_temp')['text'], state('storage_temp')),
617 (_('Temp storage info'), val('storage_temp')['text'], state('storage_temp')),
620
618
621 (_('Search info'), val('search')['text'], state('search')),
619 (_('Search info'), val('search')['text'], state('search')),
622 (_('Search location'), val('search')['location'], state('search')),
620 (_('Search location'), val('search')['location'], state('search')),
623 ('', '', ''), # spacer
621 ('', '', ''), # spacer
624
622
625 # VCS specific
623 # VCS specific
626 (_('VCS Backends'), val('vcs_backends'), state('vcs_backends')),
624 (_('VCS Backends'), val('vcs_backends'), state('vcs_backends')),
627 (_('VCS Server'), val('vcs_server')['text'], state('vcs_server')),
625 (_('VCS Server'), val('vcs_server')['text'], state('vcs_server')),
628 (_('GIT'), val('git'), state('git')),
626 (_('GIT'), val('git'), state('git')),
629 (_('HG'), val('hg'), state('hg')),
627 (_('HG'), val('hg'), state('hg')),
630 (_('SVN'), val('svn'), state('svn')),
628 (_('SVN'), val('svn'), state('svn')),
631
629
632 ]
630 ]
633
631
634 # TODO: marcink, figure out how to allow only selected users to do this
632 # TODO: marcink, figure out how to allow only selected users to do this
635 c.allowed_to_snapshot = c.rhodecode_user.admin
633 c.allowed_to_snapshot = c.rhodecode_user.admin
636
634
637 if snapshot:
635 if snapshot:
638 if c.allowed_to_snapshot:
636 if c.allowed_to_snapshot:
639 c.data_items.pop(0) # remove server info
637 c.data_items.pop(0) # remove server info
640 return render('admin/settings/settings_system_snapshot.mako')
638 return render('admin/settings/settings_system_snapshot.mako')
641 else:
639 else:
642 h.flash('You are not allowed to do this', category='warning')
640 h.flash('You are not allowed to do this', category='warning')
643
641
644 return htmlfill.render(
642 return htmlfill.render(
645 render('admin/settings/settings.mako'),
643 render('admin/settings/settings.mako'),
646 defaults=defaults,
644 defaults=defaults,
647 encoding="UTF-8",
645 encoding="UTF-8",
648 force_defaults=False)
646 force_defaults=False)
649
647
650 @staticmethod
648 @staticmethod
651 def get_update_data(update_url):
649 def get_update_data(update_url):
652 """Return the JSON update data."""
650 """Return the JSON update data."""
653 ver = rhodecode.__version__
651 ver = rhodecode.__version__
654 log.debug('Checking for upgrade on `%s` server', update_url)
652 log.debug('Checking for upgrade on `%s` server', update_url)
655 opener = urllib2.build_opener()
653 opener = urllib2.build_opener()
656 opener.addheaders = [('User-agent', 'RhodeCode-SCM/%s' % ver)]
654 opener.addheaders = [('User-agent', 'RhodeCode-SCM/%s' % ver)]
657 response = opener.open(update_url)
655 response = opener.open(update_url)
658 response_data = response.read()
656 response_data = response.read()
659 data = json.loads(response_data)
657 data = json.loads(response_data)
660
658
661 return data
659 return data
662
660
663 @HasPermissionAllDecorator('hg.admin')
661 @HasPermissionAllDecorator('hg.admin')
664 def settings_system_update(self):
662 def settings_system_update(self):
665 """GET /admin/settings/system/updates: All items in the collection"""
663 """GET /admin/settings/system/updates: All items in the collection"""
666 # url('admin_settings_system_update')
664 # url('admin_settings_system_update')
667 defaults = self._form_defaults()
665 defaults = self._form_defaults()
668 update_url = defaults.get('rhodecode_update_url', '')
666 update_url = defaults.get('rhodecode_update_url', '')
669
667
670 _err = lambda s: '<div style="color:#ff8888; padding:4px 0px">%s</div>' % (s)
668 _err = lambda s: '<div style="color:#ff8888; padding:4px 0px">%s</div>' % (s)
671 try:
669 try:
672 data = self.get_update_data(update_url)
670 data = self.get_update_data(update_url)
673 except urllib2.URLError as e:
671 except urllib2.URLError as e:
674 log.exception("Exception contacting upgrade server")
672 log.exception("Exception contacting upgrade server")
675 return _err('Failed to contact upgrade server: %r' % e)
673 return _err('Failed to contact upgrade server: %r' % e)
676 except ValueError as e:
674 except ValueError as e:
677 log.exception("Bad data sent from update server")
675 log.exception("Bad data sent from update server")
678 return _err('Bad data sent from update server')
676 return _err('Bad data sent from update server')
679
677
680 latest = data['versions'][0]
678 latest = data['versions'][0]
681
679
682 c.update_url = update_url
680 c.update_url = update_url
683 c.latest_data = latest
681 c.latest_data = latest
684 c.latest_ver = latest['version']
682 c.latest_ver = latest['version']
685 c.cur_ver = rhodecode.__version__
683 c.cur_ver = rhodecode.__version__
686 c.should_upgrade = False
684 c.should_upgrade = False
687
685
688 if (packaging.version.Version(c.latest_ver) >
686 if (packaging.version.Version(c.latest_ver) >
689 packaging.version.Version(c.cur_ver)):
687 packaging.version.Version(c.cur_ver)):
690 c.should_upgrade = True
688 c.should_upgrade = True
691 c.important_notices = latest['general']
689 c.important_notices = latest['general']
692
690
693 return render('admin/settings/settings_system_update.mako')
691 return render('admin/settings/settings_system_update.mako')
694
692
695 @HasPermissionAllDecorator('hg.admin')
693 @HasPermissionAllDecorator('hg.admin')
696 def settings_sessions(self):
697 # url('admin_settings_sessions')
698
699 c.active = 'sessions'
700 c.cleanup_older_days = 60
701 older_than_seconds = 24 * 60 * 60 * 24 * c.cleanup_older_days
702
703 config = system_info.rhodecode_config().get_value()['value']['config']
704 c.session_model = user_sessions.get_session_handler(
705 config.get('beaker.session.type', 'memory'))(config)
706
707 c.session_conf = c.session_model.config
708 c.session_count = c.session_model.get_count()
709 c.session_expired_count = c.session_model.get_expired_count(
710 older_than_seconds)
711
712 return render('admin/settings/settings.mako')
713
714 @HasPermissionAllDecorator('hg.admin')
715 def settings_sessions_cleanup(self):
716 # url('admin_settings_sessions_update')
717
718 expire_days = safe_int(request.POST.get('expire_days'))
719
720 if expire_days is None:
721 expire_days = 60
722
723 older_than_seconds = 24 * 60 * 60 * 24 * expire_days
724
725 config = system_info.rhodecode_config().get_value()['value']['config']
726 session_model = user_sessions.get_session_handler(
727 config.get('beaker.session.type', 'memory'))(config)
728
729 try:
730 session_model.clean_sessions(
731 older_than_seconds=older_than_seconds)
732 h.flash(_('Cleaned up old sessions'), category='success')
733 except user_sessions.CleanupCommand as msg:
734 h.flash(msg, category='warning')
735 except Exception as e:
736 log.exception('Failed session cleanup')
737 h.flash(_('Failed to cleanup up old sessions'), category='error')
738
739 return redirect(url('admin_settings_sessions'))
740
741 @HasPermissionAllDecorator('hg.admin')
742 def settings_supervisor(self):
694 def settings_supervisor(self):
743 c.rhodecode_ini = rhodecode.CONFIG
695 c.rhodecode_ini = rhodecode.CONFIG
744 c.active = 'supervisor'
696 c.active = 'supervisor'
745
697
746 c.supervisor_procs = OrderedDict([
698 c.supervisor_procs = OrderedDict([
747 (SUPERVISOR_MASTER, {}),
699 (SUPERVISOR_MASTER, {}),
748 ])
700 ])
749
701
750 c.log_size = 10240
702 c.log_size = 10240
751 supervisor = SupervisorModel()
703 supervisor = SupervisorModel()
752
704
753 _connection = supervisor.get_connection(
705 _connection = supervisor.get_connection(
754 c.rhodecode_ini.get('supervisor.uri'))
706 c.rhodecode_ini.get('supervisor.uri'))
755 c.connection_error = None
707 c.connection_error = None
756 try:
708 try:
757 _connection.supervisor.getAllProcessInfo()
709 _connection.supervisor.getAllProcessInfo()
758 except Exception as e:
710 except Exception as e:
759 c.connection_error = str(e)
711 c.connection_error = str(e)
760 log.exception("Exception reading supervisor data")
712 log.exception("Exception reading supervisor data")
761 return render('admin/settings/settings.mako')
713 return render('admin/settings/settings.mako')
762
714
763 groupid = c.rhodecode_ini.get('supervisor.group_id')
715 groupid = c.rhodecode_ini.get('supervisor.group_id')
764
716
765 # feed our group processes to the main
717 # feed our group processes to the main
766 for proc in supervisor.get_group_processes(_connection, groupid):
718 for proc in supervisor.get_group_processes(_connection, groupid):
767 c.supervisor_procs[proc['name']] = {}
719 c.supervisor_procs[proc['name']] = {}
768
720
769 for k in c.supervisor_procs.keys():
721 for k in c.supervisor_procs.keys():
770 try:
722 try:
771 # master process info
723 # master process info
772 if k == SUPERVISOR_MASTER:
724 if k == SUPERVISOR_MASTER:
773 _data = supervisor.get_master_state(_connection)
725 _data = supervisor.get_master_state(_connection)
774 _data['name'] = 'supervisor master'
726 _data['name'] = 'supervisor master'
775 _data['description'] = 'pid %s, id: %s, ver: %s' % (
727 _data['description'] = 'pid %s, id: %s, ver: %s' % (
776 _data['pid'], _data['id'], _data['ver'])
728 _data['pid'], _data['id'], _data['ver'])
777 c.supervisor_procs[k] = _data
729 c.supervisor_procs[k] = _data
778 else:
730 else:
779 procid = groupid + ":" + k
731 procid = groupid + ":" + k
780 c.supervisor_procs[k] = supervisor.get_process_info(_connection, procid)
732 c.supervisor_procs[k] = supervisor.get_process_info(_connection, procid)
781 except Exception as e:
733 except Exception as e:
782 log.exception("Exception reading supervisor data")
734 log.exception("Exception reading supervisor data")
783 c.supervisor_procs[k] = {'_rhodecode_error': str(e)}
735 c.supervisor_procs[k] = {'_rhodecode_error': str(e)}
784
736
785 return render('admin/settings/settings.mako')
737 return render('admin/settings/settings.mako')
786
738
787 @HasPermissionAllDecorator('hg.admin')
739 @HasPermissionAllDecorator('hg.admin')
788 def settings_supervisor_log(self, procid):
740 def settings_supervisor_log(self, procid):
789 import rhodecode
741 import rhodecode
790 c.rhodecode_ini = rhodecode.CONFIG
742 c.rhodecode_ini = rhodecode.CONFIG
791 c.active = 'supervisor_tail'
743 c.active = 'supervisor_tail'
792
744
793 supervisor = SupervisorModel()
745 supervisor = SupervisorModel()
794 _connection = supervisor.get_connection(c.rhodecode_ini.get('supervisor.uri'))
746 _connection = supervisor.get_connection(c.rhodecode_ini.get('supervisor.uri'))
795 groupid = c.rhodecode_ini.get('supervisor.group_id')
747 groupid = c.rhodecode_ini.get('supervisor.group_id')
796 procid = groupid + ":" + procid if procid != SUPERVISOR_MASTER else procid
748 procid = groupid + ":" + procid if procid != SUPERVISOR_MASTER else procid
797
749
798 c.log_size = 10240
750 c.log_size = 10240
799 offset = abs(safe_int(request.GET.get('offset', c.log_size))) * -1
751 offset = abs(safe_int(request.GET.get('offset', c.log_size))) * -1
800 c.log = supervisor.read_process_log(_connection, procid, offset, 0)
752 c.log = supervisor.read_process_log(_connection, procid, offset, 0)
801
753
802 return render('admin/settings/settings.mako')
754 return render('admin/settings/settings.mako')
803
755
804 @HasPermissionAllDecorator('hg.admin')
756 @HasPermissionAllDecorator('hg.admin')
805 @auth.CSRFRequired()
757 @auth.CSRFRequired()
806 def settings_labs_update(self):
758 def settings_labs_update(self):
807 """POST /admin/settings/labs: All items in the collection"""
759 """POST /admin/settings/labs: All items in the collection"""
808 # url('admin_settings/labs', method={'POST'})
760 # url('admin_settings/labs', method={'POST'})
809 c.active = 'labs'
761 c.active = 'labs'
810
762
811 application_form = LabsSettingsForm()()
763 application_form = LabsSettingsForm()()
812 try:
764 try:
813 form_result = application_form.to_python(dict(request.POST))
765 form_result = application_form.to_python(dict(request.POST))
814 except formencode.Invalid as errors:
766 except formencode.Invalid as errors:
815 h.flash(
767 h.flash(
816 _('Some form inputs contain invalid data.'),
768 _('Some form inputs contain invalid data.'),
817 category='error')
769 category='error')
818 return htmlfill.render(
770 return htmlfill.render(
819 render('admin/settings/settings.mako'),
771 render('admin/settings/settings.mako'),
820 defaults=errors.value,
772 defaults=errors.value,
821 errors=errors.error_dict or {},
773 errors=errors.error_dict or {},
822 prefix_error=False,
774 prefix_error=False,
823 encoding='UTF-8',
775 encoding='UTF-8',
824 force_defaults=False
776 force_defaults=False
825 )
777 )
826
778
827 try:
779 try:
828 session = Session()
780 session = Session()
829 for setting in _LAB_SETTINGS:
781 for setting in _LAB_SETTINGS:
830 setting_name = setting.key[len('rhodecode_'):]
782 setting_name = setting.key[len('rhodecode_'):]
831 sett = SettingsModel().create_or_update_setting(
783 sett = SettingsModel().create_or_update_setting(
832 setting_name, form_result[setting.key], setting.type)
784 setting_name, form_result[setting.key], setting.type)
833 session.add(sett)
785 session.add(sett)
834
786
835 except Exception:
787 except Exception:
836 log.exception('Exception while updating lab settings')
788 log.exception('Exception while updating lab settings')
837 h.flash(_('Error occurred during updating labs settings'),
789 h.flash(_('Error occurred during updating labs settings'),
838 category='error')
790 category='error')
839 else:
791 else:
840 Session().commit()
792 Session().commit()
841 SettingsModel().invalidate_settings_cache()
793 SettingsModel().invalidate_settings_cache()
842 h.flash(_('Updated Labs settings'), category='success')
794 h.flash(_('Updated Labs settings'), category='success')
843 return redirect(url('admin_settings_labs'))
795 return redirect(url('admin_settings_labs'))
844
796
845 return htmlfill.render(
797 return htmlfill.render(
846 render('admin/settings/settings.mako'),
798 render('admin/settings/settings.mako'),
847 defaults=self._form_defaults(),
799 defaults=self._form_defaults(),
848 encoding='UTF-8',
800 encoding='UTF-8',
849 force_defaults=False)
801 force_defaults=False)
850
802
851 @HasPermissionAllDecorator('hg.admin')
803 @HasPermissionAllDecorator('hg.admin')
852 def settings_labs(self):
804 def settings_labs(self):
853 """GET /admin/settings/labs: All items in the collection"""
805 """GET /admin/settings/labs: All items in the collection"""
854 # url('admin_settings_labs')
806 # url('admin_settings_labs')
855 if not c.labs_active:
807 if not c.labs_active:
856 redirect(url('admin_settings'))
808 redirect(url('admin_settings'))
857
809
858 c.active = 'labs'
810 c.active = 'labs'
859 c.lab_settings = _LAB_SETTINGS
811 c.lab_settings = _LAB_SETTINGS
860
812
861 return htmlfill.render(
813 return htmlfill.render(
862 render('admin/settings/settings.mako'),
814 render('admin/settings/settings.mako'),
863 defaults=self._form_defaults(),
815 defaults=self._form_defaults(),
864 encoding='UTF-8',
816 encoding='UTF-8',
865 force_defaults=False)
817 force_defaults=False)
866
818
867 def _form_defaults(self):
819 def _form_defaults(self):
868 defaults = SettingsModel().get_all_settings()
820 defaults = SettingsModel().get_all_settings()
869 defaults.update(self._get_hg_ui_settings())
821 defaults.update(self._get_hg_ui_settings())
870 defaults.update({
822 defaults.update({
871 'new_svn_branch': '',
823 'new_svn_branch': '',
872 'new_svn_tag': '',
824 'new_svn_tag': '',
873 })
825 })
874 return defaults
826 return defaults
875
827
876
828
877 # :param key: name of the setting including the 'rhodecode_' prefix
829 # :param key: name of the setting including the 'rhodecode_' prefix
878 # :param type: the RhodeCodeSetting type to use.
830 # :param type: the RhodeCodeSetting type to use.
879 # :param group: the i18ned group in which we should dispaly this setting
831 # :param group: the i18ned group in which we should dispaly this setting
880 # :param label: the i18ned label we should display for this setting
832 # :param label: the i18ned label we should display for this setting
881 # :param help: the i18ned help we should dispaly for this setting
833 # :param help: the i18ned help we should dispaly for this setting
882 LabSetting = collections.namedtuple(
834 LabSetting = collections.namedtuple(
883 'LabSetting', ('key', 'type', 'group', 'label', 'help'))
835 'LabSetting', ('key', 'type', 'group', 'label', 'help'))
884
836
885
837
886 # This list has to be kept in sync with the form
838 # This list has to be kept in sync with the form
887 # rhodecode.model.forms.LabsSettingsForm.
839 # rhodecode.model.forms.LabsSettingsForm.
888 _LAB_SETTINGS = [
840 _LAB_SETTINGS = [
889
841
890 ]
842 ]
@@ -1,62 +1,62 b''
1 <div class="panel panel-default">
1 <div class="panel panel-default">
2 <div class="panel-heading">
2 <div class="panel-heading">
3 <h3 class="panel-title">${_('User Sessions Configuration')}</h3>
3 <h3 class="panel-title">${_('User Sessions Configuration')}</h3>
4 </div>
4 </div>
5 <div class="panel-body">
5 <div class="panel-body">
6 <%
6 <%
7 elems = [
7 elems = [
8 (_('Session type'), c.session_model.SESSION_TYPE, ''),
8 (_('Session type'), c.session_model.SESSION_TYPE, ''),
9 (_('Session expiration period'), '{} seconds'.format(c.session_conf.get('beaker.session.timeout', 0)), ''),
9 (_('Session expiration period'), '{} seconds'.format(c.session_conf.get('beaker.session.timeout', 0)), ''),
10
10
11 (_('Total sessions'), c.session_count, ''),
11 (_('Total sessions'), c.session_count, ''),
12 (_('Expired sessions ({} days)').format(c.cleanup_older_days ), c.session_expired_count, ''),
12 (_('Expired sessions ({} days)').format(c.cleanup_older_days ), c.session_expired_count, ''),
13
13
14 ]
14 ]
15 %>
15 %>
16 <dl class="dl-horizontal settings">
16 <dl class="dl-horizontal settings">
17 %for dt, dd, tt in elems:
17 %for dt, dd, tt in elems:
18 <dt>${dt}:</dt>
18 <dt>${dt}:</dt>
19 <dd title="${tt}">${dd}</dd>
19 <dd title="${tt}">${dd}</dd>
20 %endfor
20 %endfor
21 </dl>
21 </dl>
22 </div>
22 </div>
23 </div>
23 </div>
24
24
25
25
26 <div class="panel panel-warning">
26 <div class="panel panel-warning">
27 <div class="panel-heading">
27 <div class="panel-heading">
28 <h3 class="panel-title">${_('Cleanup Old Sessions')}</h3>
28 <h3 class="panel-title">${_('Cleanup Old Sessions')}</h3>
29 </div>
29 </div>
30 <div class="panel-body">
30 <div class="panel-body">
31 ${h.secure_form(h.url('admin_settings_sessions_cleanup'), method='post')}
31 ${h.secure_form(h.route_path('admin_settings_sessions_cleanup'), method='post')}
32
32
33 <p>
33 <p>
34 ${_('Cleanup user sessions that were not active during chosen time frame.')} <br/>
34 ${_('Cleanup user sessions that were not active during chosen time frame.')} <br/>
35 ${_('After performing this action users whose session will be removed will be required to log in again.')} <br/>
35 ${_('After performing this action users whose session will be removed will be required to log in again.')} <br/>
36 <strong>${_('Picking `All` will log-out you, and all users in the system.')}</strong>
36 <strong>${_('Picking `All` will log-out you, and all users in the system.')}</strong>
37 </p>
37 </p>
38
38
39 <script type="text/javascript">
39 <script type="text/javascript">
40 $(document).ready(function() {
40 $(document).ready(function() {
41 $('#expire_days').select2({
41 $('#expire_days').select2({
42 containerCssClass: 'drop-menu',
42 containerCssClass: 'drop-menu',
43 dropdownCssClass: 'drop-menu-dropdown',
43 dropdownCssClass: 'drop-menu-dropdown',
44 dropdownAutoWidth: true,
44 dropdownAutoWidth: true,
45 minimumResultsForSearch: -1
45 minimumResultsForSearch: -1
46 });
46 });
47 });
47 });
48 </script>
48 </script>
49 <select id="expire_days" name="expire_days">
49 <select id="expire_days" name="expire_days">
50 % for n in [60, 90, 30, 7, 0]:
50 % for n in [60, 90, 30, 7, 0]:
51 <option value="${n}">${'{} days'.format(n) if n != 0 else 'All'}</option>
51 <option value="${n}">${'{} days'.format(n) if n != 0 else 'All'}</option>
52 % endfor
52 % endfor
53 </select>
53 </select>
54 <button class="btn btn-small" type="submit"
54 <button class="btn btn-small" type="submit"
55 onclick="return confirm('${_('Confirm to cleanup user sessions')}');">
55 onclick="return confirm('${_('Confirm to cleanup user sessions')}');">
56 ${_('Cleanup sessions')}
56 ${_('Cleanup sessions')}
57 </button>
57 </button>
58 ${h.end_form()}
58 ${h.end_form()}
59 </div>
59 </div>
60 </div>
60 </div>
61
61
62
62
General Comments 0
You need to be logged in to leave comments. Login now