##// END OF EJS Templates
settings: removed not used supervisor views/models....
marcink -
r2325:f18d95e5 default
parent child Browse files
Show More
@@ -1,142 +1,139 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2017 RhodeCode GmbH
3 # Copyright (C) 2016-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 import logging
22 import logging
23 import collections
23 import collections
24
24
25 from zope.interface import implementer
25 from zope.interface import implementer
26
26
27 from rhodecode.apps.admin.interfaces import IAdminNavigationRegistry
27 from rhodecode.apps.admin.interfaces import IAdminNavigationRegistry
28 from rhodecode.lib.utils import get_registry
28 from rhodecode.lib.utils import get_registry
29 from rhodecode.translation import _
29 from rhodecode.translation import _
30
30
31
31
32 log = logging.getLogger(__name__)
32 log = logging.getLogger(__name__)
33
33
34 NavListEntry = collections.namedtuple('NavListEntry', ['key', 'name', 'url'])
34 NavListEntry = collections.namedtuple('NavListEntry', ['key', 'name', 'url'])
35
35
36
36
37 class NavEntry(object):
37 class NavEntry(object):
38 """
38 """
39 Represents an entry in the admin navigation.
39 Represents an entry in the admin navigation.
40
40
41 :param key: Unique identifier used to store reference in an OrderedDict.
41 :param key: Unique identifier used to store reference in an OrderedDict.
42 :param name: Display name, usually a translation string.
42 :param name: Display name, usually a translation string.
43 :param view_name: Name of the view, used generate the URL.
43 :param view_name: Name of the view, used generate the URL.
44 :param pyramid: Indicator to use pyramid for URL generation. This should
44 :param pyramid: Indicator to use pyramid for URL generation. This should
45 be removed as soon as we are fully migrated to pyramid.
45 be removed as soon as we are fully migrated to pyramid.
46 """
46 """
47
47
48 def __init__(self, key, name, view_name, pyramid=False):
48 def __init__(self, key, name, view_name, pyramid=False):
49 self.key = key
49 self.key = key
50 self.name = name
50 self.name = name
51 self.view_name = view_name
51 self.view_name = view_name
52 self.pyramid = pyramid
52 self.pyramid = pyramid
53
53
54 def generate_url(self, request):
54 def generate_url(self, request):
55 if self.pyramid:
55 if self.pyramid:
56 if hasattr(request, 'route_path'):
56 if hasattr(request, 'route_path'):
57 return request.route_path(self.view_name)
57 return request.route_path(self.view_name)
58 else:
58 else:
59 # TODO: johbo: Remove this after migrating to pyramid.
59 # TODO: johbo: Remove this after migrating to pyramid.
60 # We need the pyramid request here to generate URLs to pyramid
60 # We need the pyramid request here to generate URLs to pyramid
61 # views from within pylons views.
61 # views from within pylons views.
62 from pyramid.threadlocal import get_current_request
62 from pyramid.threadlocal import get_current_request
63 pyramid_request = get_current_request()
63 pyramid_request = get_current_request()
64 return pyramid_request.route_path(self.view_name)
64 return pyramid_request.route_path(self.view_name)
65 else:
65 else:
66 from pylons import url
66 from pylons import url
67 return url(self.view_name)
67 return url(self.view_name)
68
68
69 def get_localized_name(self, request):
69 def get_localized_name(self, request):
70 if hasattr(request, 'translate'):
70 if hasattr(request, 'translate'):
71 return request.translate(self.name)
71 return request.translate(self.name)
72 else:
72 else:
73 # TODO(marcink): Remove this after migrating to pyramid
73 # TODO(marcink): Remove this after migrating to pyramid
74 from pyramid.threadlocal import get_current_request
74 from pyramid.threadlocal import get_current_request
75 pyramid_request = get_current_request()
75 pyramid_request = get_current_request()
76 return pyramid_request.translate(self.name)
76 return pyramid_request.translate(self.name)
77
77
78
78
79 @implementer(IAdminNavigationRegistry)
79 @implementer(IAdminNavigationRegistry)
80 class NavigationRegistry(object):
80 class NavigationRegistry(object):
81
81
82 _base_entries = [
82 _base_entries = [
83 NavEntry('global', _('Global'), 'admin_settings_global'),
83 NavEntry('global', _('Global'), 'admin_settings_global'),
84 NavEntry('vcs', _('VCS'), 'admin_settings_vcs'),
84 NavEntry('vcs', _('VCS'), 'admin_settings_vcs'),
85 NavEntry('visual', _('Visual'), 'admin_settings_visual'),
85 NavEntry('visual', _('Visual'), 'admin_settings_visual'),
86 NavEntry('mapping', _('Remap and Rescan'), 'admin_settings_mapping'),
86 NavEntry('mapping', _('Remap and Rescan'), 'admin_settings_mapping'),
87 NavEntry('issuetracker', _('Issue Tracker'),
87 NavEntry('issuetracker', _('Issue Tracker'),
88 'admin_settings_issuetracker'),
88 'admin_settings_issuetracker'),
89 NavEntry('email', _('Email'), 'admin_settings_email'),
89 NavEntry('email', _('Email'), 'admin_settings_email'),
90 NavEntry('hooks', _('Hooks'), 'admin_settings_hooks'),
90 NavEntry('hooks', _('Hooks'), 'admin_settings_hooks'),
91 NavEntry('search', _('Full Text Search'), 'admin_settings_search'),
91 NavEntry('search', _('Full Text Search'), 'admin_settings_search'),
92
92
93 NavEntry('integrations', _('Integrations'),
93 NavEntry('integrations', _('Integrations'),
94 'global_integrations_home', pyramid=True),
94 'global_integrations_home', pyramid=True),
95 NavEntry('system', _('System Info'),
95 NavEntry('system', _('System Info'),
96 'admin_settings_system', pyramid=True),
96 'admin_settings_system', pyramid=True),
97 NavEntry('process_management', _('Processes'),
97 NavEntry('process_management', _('Processes'),
98 'admin_settings_process_management', pyramid=True),
98 'admin_settings_process_management', pyramid=True),
99 NavEntry('sessions', _('User Sessions'),
99 NavEntry('sessions', _('User Sessions'),
100 'admin_settings_sessions', pyramid=True),
100 'admin_settings_sessions', pyramid=True),
101 NavEntry('open_source', _('Open Source Licenses'),
101 NavEntry('open_source', _('Open Source Licenses'),
102 'admin_settings_open_source', pyramid=True),
102 'admin_settings_open_source', pyramid=True),
103
103
104 # TODO: marcink: we disable supervisor now until the supervisor stats
105 # page is fixed in the nix configuration
106 # NavEntry('supervisor', _('Supervisor'), 'admin_settings_supervisor'),
107 ]
104 ]
108
105
109 _labs_entry = NavEntry('labs', _('Labs'), 'admin_settings_labs')
106 _labs_entry = NavEntry('labs', _('Labs'), 'admin_settings_labs')
110
107
111 def __init__(self, labs_active=False):
108 def __init__(self, labs_active=False):
112 self._registered_entries = collections.OrderedDict()
109 self._registered_entries = collections.OrderedDict()
113 for item in self.__class__._base_entries:
110 for item in self.__class__._base_entries:
114 self._registered_entries[item.key] = item
111 self._registered_entries[item.key] = item
115
112
116 if labs_active:
113 if labs_active:
117 self.add_entry(self._labs_entry)
114 self.add_entry(self._labs_entry)
118
115
119 def add_entry(self, entry):
116 def add_entry(self, entry):
120 self._registered_entries[entry.key] = entry
117 self._registered_entries[entry.key] = entry
121
118
122 def get_navlist(self, request):
119 def get_navlist(self, request):
123 navlist = [NavListEntry(i.key, i.get_localized_name(request),
120 navlist = [NavListEntry(i.key, i.get_localized_name(request),
124 i.generate_url(request))
121 i.generate_url(request))
125 for i in self._registered_entries.values()]
122 for i in self._registered_entries.values()]
126 return navlist
123 return navlist
127
124
128
125
129 def navigation_registry(request, registry=None):
126 def navigation_registry(request, registry=None):
130 """
127 """
131 Helper that returns the admin navigation registry.
128 Helper that returns the admin navigation registry.
132 """
129 """
133 pyramid_registry = registry or get_registry(request)
130 pyramid_registry = registry or get_registry(request)
134 nav_registry = pyramid_registry.queryUtility(IAdminNavigationRegistry)
131 nav_registry = pyramid_registry.queryUtility(IAdminNavigationRegistry)
135 return nav_registry
132 return nav_registry
136
133
137
134
138 def navigation_list(request):
135 def navigation_list(request):
139 """
136 """
140 Helper that returns the admin navigation as list of NavListEntry objects.
137 Helper that returns the admin navigation as list of NavListEntry objects.
141 """
138 """
142 return navigation_registry(request).get_navlist(request)
139 return navigation_registry(request).get_navlist(request)
@@ -1,543 +1,545 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 Pylons middleware initialization
22 Pylons middleware initialization
23 """
23 """
24 import logging
24 import logging
25 import traceback
25 import traceback
26 import collections
26 import collections
27
27
28 from paste.registry import RegistryManager
28 from paste.registry import RegistryManager
29 from paste.gzipper import make_gzip_middleware
29 from paste.gzipper import make_gzip_middleware
30 from pyramid.authorization import ACLAuthorizationPolicy
30 from pyramid.authorization import ACLAuthorizationPolicy
31 from pyramid.config import Configurator
31 from pyramid.config import Configurator
32 from pyramid.settings import asbool, aslist
32 from pyramid.settings import asbool, aslist
33 from pyramid.wsgi import wsgiapp
33 from pyramid.wsgi import wsgiapp
34 from pyramid.httpexceptions import (
34 from pyramid.httpexceptions import (
35 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound)
35 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound)
36 from pyramid.events import ApplicationCreated
36 from pyramid.events import ApplicationCreated
37 from pyramid.renderers import render_to_response
37 from pyramid.renderers import render_to_response
38 from routes.middleware import RoutesMiddleware
38 from routes.middleware import RoutesMiddleware
39 import rhodecode
39 import rhodecode
40
40
41 from rhodecode.model import meta
41 from rhodecode.model import meta
42 from rhodecode.config import patches
42 from rhodecode.config import patches
43 from rhodecode.config import utils as config_utils
43 from rhodecode.config import utils as config_utils
44 from rhodecode.config.routing import STATIC_FILE_PREFIX
44 from rhodecode.config.routing import STATIC_FILE_PREFIX
45 from rhodecode.config.environment import (
45 from rhodecode.config.environment import (
46 load_environment, load_pyramid_environment)
46 load_environment, load_pyramid_environment)
47
47
48 from rhodecode.lib.vcs import VCSCommunicationError
48 from rhodecode.lib.vcs import VCSCommunicationError
49 from rhodecode.lib.exceptions import VCSServerUnavailable
49 from rhodecode.lib.exceptions import VCSServerUnavailable
50 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
50 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
51 from rhodecode.lib.middleware.error_handling import (
51 from rhodecode.lib.middleware.error_handling import (
52 PylonsErrorHandlingMiddleware)
52 PylonsErrorHandlingMiddleware)
53 from rhodecode.lib.middleware.https_fixup import HttpsFixup
53 from rhodecode.lib.middleware.https_fixup import HttpsFixup
54 from rhodecode.lib.middleware.vcs import VCSMiddleware
54 from rhodecode.lib.middleware.vcs import VCSMiddleware
55 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
55 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
56 from rhodecode.lib.utils2 import aslist as rhodecode_aslist, AttributeDict
56 from rhodecode.lib.utils2 import aslist as rhodecode_aslist, AttributeDict
57 from rhodecode.subscribers import (
57 from rhodecode.subscribers import (
58 scan_repositories_if_enabled, write_js_routes_if_enabled,
58 scan_repositories_if_enabled, write_js_routes_if_enabled,
59 write_metadata_if_needed, inject_app_settings)
59 write_metadata_if_needed, inject_app_settings)
60
60
61
61
62 log = logging.getLogger(__name__)
62 log = logging.getLogger(__name__)
63
63
64
64
65 # this is used to avoid avoid the route lookup overhead in routesmiddleware
65 # this is used to avoid avoid the route lookup overhead in routesmiddleware
66 # for certain routes which won't go to pylons to - eg. static files, debugger
66 # for certain routes which won't go to pylons to - eg. static files, debugger
67 # it is only needed for the pylons migration and can be removed once complete
67 # it is only needed for the pylons migration and can be removed once complete
68 class SkippableRoutesMiddleware(RoutesMiddleware):
68 class SkippableRoutesMiddleware(RoutesMiddleware):
69 """ Routes middleware that allows you to skip prefixes """
69 """ Routes middleware that allows you to skip prefixes """
70
70
71 def __init__(self, *args, **kw):
71 def __init__(self, *args, **kw):
72 self.skip_prefixes = kw.pop('skip_prefixes', [])
72 self.skip_prefixes = kw.pop('skip_prefixes', [])
73 super(SkippableRoutesMiddleware, self).__init__(*args, **kw)
73 super(SkippableRoutesMiddleware, self).__init__(*args, **kw)
74
74
75 def __call__(self, environ, start_response):
75 def __call__(self, environ, start_response):
76 for prefix in self.skip_prefixes:
76 for prefix in self.skip_prefixes:
77 if environ['PATH_INFO'].startswith(prefix):
77 if environ['PATH_INFO'].startswith(prefix):
78 # added to avoid the case when a missing /_static route falls
78 # added to avoid the case when a missing /_static route falls
79 # through to pylons and causes an exception as pylons is
79 # through to pylons and causes an exception as pylons is
80 # expecting wsgiorg.routingargs to be set in the environ
80 # expecting wsgiorg.routingargs to be set in the environ
81 # by RoutesMiddleware.
81 # by RoutesMiddleware.
82 if 'wsgiorg.routing_args' not in environ:
82 if 'wsgiorg.routing_args' not in environ:
83 environ['wsgiorg.routing_args'] = (None, {})
83 environ['wsgiorg.routing_args'] = (None, {})
84 return self.app(environ, start_response)
84 return self.app(environ, start_response)
85
85
86 return super(SkippableRoutesMiddleware, self).__call__(
86 return super(SkippableRoutesMiddleware, self).__call__(
87 environ, start_response)
87 environ, start_response)
88
88
89
89
90 def make_app(global_conf, static_files=True, **app_conf):
90 def make_app(global_conf, static_files=True, **app_conf):
91 """Create a Pylons WSGI application and return it
91 """Create a Pylons WSGI application and return it
92
92
93 ``global_conf``
93 ``global_conf``
94 The inherited configuration for this application. Normally from
94 The inherited configuration for this application. Normally from
95 the [DEFAULT] section of the Paste ini file.
95 the [DEFAULT] section of the Paste ini file.
96
96
97 ``app_conf``
97 ``app_conf``
98 The application's local configuration. Normally specified in
98 The application's local configuration. Normally specified in
99 the [app:<name>] section of the Paste ini file (where <name>
99 the [app:<name>] section of the Paste ini file (where <name>
100 defaults to main).
100 defaults to main).
101
101
102 """
102 """
103 from pylons.wsgiapp import PylonsApp
103 from pylons.wsgiapp import PylonsApp
104
104
105 # Apply compatibility patches
105 # Apply compatibility patches
106 patches.kombu_1_5_1_python_2_7_11()
106 patches.kombu_1_5_1_python_2_7_11()
107 patches.inspect_getargspec()
107 patches.inspect_getargspec()
108
108
109 # Configure the Pylons environment
109 # Configure the Pylons environment
110 config = load_environment(global_conf, app_conf)
110 config = load_environment(global_conf, app_conf)
111
111
112 # The Pylons WSGI app
112 # The Pylons WSGI app
113 app = PylonsApp(config=config)
113 app = PylonsApp(config=config)
114
114
115 # Establish the Registry for this application
115 # Establish the Registry for this application
116 app = RegistryManager(app)
116 app = RegistryManager(app)
117
117
118 app.config = config
118 app.config = config
119
119
120 return app
120 return app
121
121
122
122
123 def make_pyramid_app(global_config, **settings):
123 def make_pyramid_app(global_config, **settings):
124 """
124 """
125 Constructs the WSGI application based on Pyramid and wraps the Pylons based
125 Constructs the WSGI application based on Pyramid and wraps the Pylons based
126 application.
126 application.
127
127
128 Specials:
128 Specials:
129
129
130 * We migrate from Pylons to Pyramid. While doing this, we keep both
130 * We migrate from Pylons to Pyramid. While doing this, we keep both
131 frameworks functional. This involves moving some WSGI middlewares around
131 frameworks functional. This involves moving some WSGI middlewares around
132 and providing access to some data internals, so that the old code is
132 and providing access to some data internals, so that the old code is
133 still functional.
133 still functional.
134
134
135 * The application can also be integrated like a plugin via the call to
135 * The application can also be integrated like a plugin via the call to
136 `includeme`. This is accompanied with the other utility functions which
136 `includeme`. This is accompanied with the other utility functions which
137 are called. Changing this should be done with great care to not break
137 are called. Changing this should be done with great care to not break
138 cases when these fragments are assembled from another place.
138 cases when these fragments are assembled from another place.
139
139
140 """
140 """
141 sanitize_settings_and_apply_defaults(settings)
141 sanitize_settings_and_apply_defaults(settings)
142
142
143 config = Configurator(settings=settings)
143 config = Configurator(settings=settings)
144 load_pyramid_environment(global_config, settings)
144 load_pyramid_environment(global_config, settings)
145
145
146 add_pylons_compat_data(config.registry, global_config, settings.copy())
146 add_pylons_compat_data(config.registry, global_config, settings.copy())
147
147
148 # Static file view comes first
148 includeme_first(config)
149 includeme_first(config)
150
149 includeme(config)
151 includeme(config)
150
152
151 pyramid_app = config.make_wsgi_app()
153 pyramid_app = config.make_wsgi_app()
152 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
154 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
153 pyramid_app.config = config
155 pyramid_app.config = config
154
156
155 # creating the app uses a connection - return it after we are done
157 # creating the app uses a connection - return it after we are done
156 meta.Session.remove()
158 meta.Session.remove()
157
159
158 return pyramid_app
160 return pyramid_app
159
161
160
162
161 def make_not_found_view(config):
163 def make_not_found_view(config):
162 """
164 """
163 This creates the view which should be registered as not-found-view to
165 This creates the view which should be registered as not-found-view to
164 pyramid. Basically it contains of the old pylons app, converted to a view.
166 pyramid. Basically it contains of the old pylons app, converted to a view.
165 Additionally it is wrapped by some other middlewares.
167 Additionally it is wrapped by some other middlewares.
166 """
168 """
167 settings = config.registry.settings
169 settings = config.registry.settings
168 vcs_server_enabled = settings['vcs.server.enable']
170 vcs_server_enabled = settings['vcs.server.enable']
169
171
170 # Make pylons app from unprepared settings.
172 # Make pylons app from unprepared settings.
171 pylons_app = make_app(
173 pylons_app = make_app(
172 config.registry._pylons_compat_global_config,
174 config.registry._pylons_compat_global_config,
173 **config.registry._pylons_compat_settings)
175 **config.registry._pylons_compat_settings)
174 config.registry._pylons_compat_config = pylons_app.config
176 config.registry._pylons_compat_config = pylons_app.config
175
177
176 # Appenlight monitoring.
178 # Appenlight monitoring.
177 pylons_app, appenlight_client = wrap_in_appenlight_if_enabled(
179 pylons_app, appenlight_client = wrap_in_appenlight_if_enabled(
178 pylons_app, settings)
180 pylons_app, settings)
179
181
180 # The pylons app is executed inside of the pyramid 404 exception handler.
182 # The pylons app is executed inside of the pyramid 404 exception handler.
181 # Exceptions which are raised inside of it are not handled by pyramid
183 # Exceptions which are raised inside of it are not handled by pyramid
182 # again. Therefore we add a middleware that invokes the error handler in
184 # again. Therefore we add a middleware that invokes the error handler in
183 # case of an exception or error response. This way we return proper error
185 # case of an exception or error response. This way we return proper error
184 # HTML pages in case of an error.
186 # HTML pages in case of an error.
185 reraise = (settings.get('debugtoolbar.enabled', False) or
187 reraise = (settings.get('debugtoolbar.enabled', False) or
186 rhodecode.disable_error_handler)
188 rhodecode.disable_error_handler)
187 pylons_app = PylonsErrorHandlingMiddleware(
189 pylons_app = PylonsErrorHandlingMiddleware(
188 pylons_app, error_handler, reraise)
190 pylons_app, error_handler, reraise)
189
191
190 # The VCSMiddleware shall operate like a fallback if pyramid doesn't find a
192 # The VCSMiddleware shall operate like a fallback if pyramid doesn't find a
191 # view to handle the request. Therefore it is wrapped around the pylons
193 # view to handle the request. Therefore it is wrapped around the pylons
192 # app. It has to be outside of the error handling otherwise error responses
194 # app. It has to be outside of the error handling otherwise error responses
193 # from the vcsserver are converted to HTML error pages. This confuses the
195 # from the vcsserver are converted to HTML error pages. This confuses the
194 # command line tools and the user won't get a meaningful error message.
196 # command line tools and the user won't get a meaningful error message.
195 if vcs_server_enabled:
197 if vcs_server_enabled:
196 pylons_app = VCSMiddleware(
198 pylons_app = VCSMiddleware(
197 pylons_app, settings, appenlight_client, registry=config.registry)
199 pylons_app, settings, appenlight_client, registry=config.registry)
198
200
199 # Convert WSGI app to pyramid view and return it.
201 # Convert WSGI app to pyramid view and return it.
200 return wsgiapp(pylons_app)
202 return wsgiapp(pylons_app)
201
203
202
204
203 def add_pylons_compat_data(registry, global_config, settings):
205 def add_pylons_compat_data(registry, global_config, settings):
204 """
206 """
205 Attach data to the registry to support the Pylons integration.
207 Attach data to the registry to support the Pylons integration.
206 """
208 """
207 registry._pylons_compat_global_config = global_config
209 registry._pylons_compat_global_config = global_config
208 registry._pylons_compat_settings = settings
210 registry._pylons_compat_settings = settings
209
211
210
212
211 def error_handler(exception, request):
213 def error_handler(exception, request):
212 import rhodecode
214 import rhodecode
213 from rhodecode.lib import helpers
215 from rhodecode.lib import helpers
214
216
215 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
217 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
216
218
217 base_response = HTTPInternalServerError()
219 base_response = HTTPInternalServerError()
218 # prefer original exception for the response since it may have headers set
220 # prefer original exception for the response since it may have headers set
219 if isinstance(exception, HTTPException):
221 if isinstance(exception, HTTPException):
220 base_response = exception
222 base_response = exception
221 elif isinstance(exception, VCSCommunicationError):
223 elif isinstance(exception, VCSCommunicationError):
222 base_response = VCSServerUnavailable()
224 base_response = VCSServerUnavailable()
223
225
224 def is_http_error(response):
226 def is_http_error(response):
225 # error which should have traceback
227 # error which should have traceback
226 return response.status_code > 499
228 return response.status_code > 499
227
229
228 if is_http_error(base_response):
230 if is_http_error(base_response):
229 log.exception(
231 log.exception(
230 'error occurred handling this request for path: %s', request.path)
232 'error occurred handling this request for path: %s', request.path)
231
233
232 error_explanation = base_response.explanation or str(base_response)
234 error_explanation = base_response.explanation or str(base_response)
233 if base_response.status_code == 404:
235 if base_response.status_code == 404:
234 error_explanation += " Or you don't have permission to access it."
236 error_explanation += " Or you don't have permission to access it."
235 c = AttributeDict()
237 c = AttributeDict()
236 c.error_message = base_response.status
238 c.error_message = base_response.status
237 c.error_explanation = error_explanation
239 c.error_explanation = error_explanation
238 c.visual = AttributeDict()
240 c.visual = AttributeDict()
239
241
240 c.visual.rhodecode_support_url = (
242 c.visual.rhodecode_support_url = (
241 request.registry.settings.get('rhodecode_support_url') or
243 request.registry.settings.get('rhodecode_support_url') or
242 request.route_url('rhodecode_support')
244 request.route_url('rhodecode_support')
243 )
245 )
244 c.redirect_time = 0
246 c.redirect_time = 0
245 c.rhodecode_name = rhodecode_title
247 c.rhodecode_name = rhodecode_title
246 if not c.rhodecode_name:
248 if not c.rhodecode_name:
247 c.rhodecode_name = 'Rhodecode'
249 c.rhodecode_name = 'Rhodecode'
248
250
249 c.causes = []
251 c.causes = []
250 if is_http_error(base_response):
252 if is_http_error(base_response):
251 c.causes.append('Server is overloaded.')
253 c.causes.append('Server is overloaded.')
252 c.causes.append('Server database connection is lost.')
254 c.causes.append('Server database connection is lost.')
253 c.causes.append('Server expected unhandled error.')
255 c.causes.append('Server expected unhandled error.')
254
256
255 if hasattr(base_response, 'causes'):
257 if hasattr(base_response, 'causes'):
256 c.causes = base_response.causes
258 c.causes = base_response.causes
257
259
258 c.messages = helpers.flash.pop_messages(request=request)
260 c.messages = helpers.flash.pop_messages(request=request)
259 c.traceback = traceback.format_exc()
261 c.traceback = traceback.format_exc()
260 response = render_to_response(
262 response = render_to_response(
261 '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request,
263 '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request,
262 response=base_response)
264 response=base_response)
263
265
264 return response
266 return response
265
267
266
268
267 def includeme(config):
269 def includeme(config):
268 settings = config.registry.settings
270 settings = config.registry.settings
269
271
270 # plugin information
272 # plugin information
271 config.registry.rhodecode_plugins = collections.OrderedDict()
273 config.registry.rhodecode_plugins = collections.OrderedDict()
272
274
273 config.add_directive(
275 config.add_directive(
274 'register_rhodecode_plugin', register_rhodecode_plugin)
276 'register_rhodecode_plugin', register_rhodecode_plugin)
275
277
276 if asbool(settings.get('appenlight', 'false')):
278 if asbool(settings.get('appenlight', 'false')):
277 config.include('appenlight_client.ext.pyramid_tween')
279 config.include('appenlight_client.ext.pyramid_tween')
278
280
279 # Includes which are required. The application would fail without them.
281 # Includes which are required. The application would fail without them.
280 config.include('pyramid_mako')
282 config.include('pyramid_mako')
281 config.include('pyramid_beaker')
283 config.include('pyramid_beaker')
282
284
283 config.include('rhodecode.authentication')
285 config.include('rhodecode.authentication')
284 config.include('rhodecode.integrations')
286 config.include('rhodecode.integrations')
285
287
286 # apps
288 # apps
287 config.include('rhodecode.apps._base')
289 config.include('rhodecode.apps._base')
288 config.include('rhodecode.apps.ops')
290 config.include('rhodecode.apps.ops')
289
291
290 config.include('rhodecode.apps.admin')
292 config.include('rhodecode.apps.admin')
291 config.include('rhodecode.apps.channelstream')
293 config.include('rhodecode.apps.channelstream')
292 config.include('rhodecode.apps.login')
294 config.include('rhodecode.apps.login')
293 config.include('rhodecode.apps.home')
295 config.include('rhodecode.apps.home')
294 config.include('rhodecode.apps.journal')
296 config.include('rhodecode.apps.journal')
295 config.include('rhodecode.apps.repository')
297 config.include('rhodecode.apps.repository')
296 config.include('rhodecode.apps.repo_group')
298 config.include('rhodecode.apps.repo_group')
297 config.include('rhodecode.apps.user_group')
299 config.include('rhodecode.apps.user_group')
298 config.include('rhodecode.apps.search')
300 config.include('rhodecode.apps.search')
299 config.include('rhodecode.apps.user_profile')
301 config.include('rhodecode.apps.user_profile')
300 config.include('rhodecode.apps.my_account')
302 config.include('rhodecode.apps.my_account')
301 config.include('rhodecode.apps.svn_support')
303 config.include('rhodecode.apps.svn_support')
302 config.include('rhodecode.apps.ssh_support')
304 config.include('rhodecode.apps.ssh_support')
303 config.include('rhodecode.apps.gist')
305 config.include('rhodecode.apps.gist')
304
306
305 config.include('rhodecode.apps.debug_style')
307 config.include('rhodecode.apps.debug_style')
306 config.include('rhodecode.tweens')
308 config.include('rhodecode.tweens')
307 config.include('rhodecode.api')
309 config.include('rhodecode.api')
308
310
309 config.add_route(
311 config.add_route(
310 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
312 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
311
313
312 config.add_translation_dirs('rhodecode:i18n/')
314 config.add_translation_dirs('rhodecode:i18n/')
313 settings['default_locale_name'] = settings.get('lang', 'en')
315 settings['default_locale_name'] = settings.get('lang', 'en')
314
316
315 # Add subscribers.
317 # Add subscribers.
316 config.add_subscriber(inject_app_settings, ApplicationCreated)
318 config.add_subscriber(inject_app_settings, ApplicationCreated)
317 config.add_subscriber(scan_repositories_if_enabled, ApplicationCreated)
319 config.add_subscriber(scan_repositories_if_enabled, ApplicationCreated)
318 config.add_subscriber(write_metadata_if_needed, ApplicationCreated)
320 config.add_subscriber(write_metadata_if_needed, ApplicationCreated)
319 config.add_subscriber(write_js_routes_if_enabled, ApplicationCreated)
321 config.add_subscriber(write_js_routes_if_enabled, ApplicationCreated)
320
322
321 config.add_request_method(
323 config.add_request_method(
322 'rhodecode.lib.partial_renderer.get_partial_renderer',
324 'rhodecode.lib.partial_renderer.get_partial_renderer',
323 'get_partial_renderer')
325 'get_partial_renderer')
324
326
325 # events
327 # events
326 # TODO(marcink): this should be done when pyramid migration is finished
328 # TODO(marcink): this should be done when pyramid migration is finished
327 # config.add_subscriber(
329 # config.add_subscriber(
328 # 'rhodecode.integrations.integrations_event_handler',
330 # 'rhodecode.integrations.integrations_event_handler',
329 # 'rhodecode.events.RhodecodeEvent')
331 # 'rhodecode.events.RhodecodeEvent')
330
332
331 # Set the authorization policy.
333 # Set the authorization policy.
332 authz_policy = ACLAuthorizationPolicy()
334 authz_policy = ACLAuthorizationPolicy()
333 config.set_authorization_policy(authz_policy)
335 config.set_authorization_policy(authz_policy)
334
336
335 # Set the default renderer for HTML templates to mako.
337 # Set the default renderer for HTML templates to mako.
336 config.add_mako_renderer('.html')
338 config.add_mako_renderer('.html')
337
339
338 config.add_renderer(
340 config.add_renderer(
339 name='json_ext',
341 name='json_ext',
340 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
342 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
341
343
342 # include RhodeCode plugins
344 # include RhodeCode plugins
343 includes = aslist(settings.get('rhodecode.includes', []))
345 includes = aslist(settings.get('rhodecode.includes', []))
344 for inc in includes:
346 for inc in includes:
345 config.include(inc)
347 config.include(inc)
346
348
347 # This is the glue which allows us to migrate in chunks. By registering the
349 # This is the glue which allows us to migrate in chunks. By registering the
348 # pylons based application as the "Not Found" view in Pyramid, we will
350 # pylons based application as the "Not Found" view in Pyramid, we will
349 # fallback to the old application each time the new one does not yet know
351 # fallback to the old application each time the new one does not yet know
350 # how to handle a request.
352 # how to handle a request.
351 config.add_notfound_view(make_not_found_view(config))
353 config.add_notfound_view(make_not_found_view(config))
352
354
353 if not settings.get('debugtoolbar.enabled', False):
355 if not settings.get('debugtoolbar.enabled', False):
354 # disabled debugtoolbar handle all exceptions via the error_handlers
356 # disabled debugtoolbar handle all exceptions via the error_handlers
355 config.add_view(error_handler, context=Exception)
357 config.add_view(error_handler, context=Exception)
356
358
357 config.add_view(error_handler, context=HTTPError)
359 config.add_view(error_handler, context=HTTPError)
358
360
359
361
360 def includeme_first(config):
362 def includeme_first(config):
361 # redirect automatic browser favicon.ico requests to correct place
363 # redirect automatic browser favicon.ico requests to correct place
362 def favicon_redirect(context, request):
364 def favicon_redirect(context, request):
363 return HTTPFound(
365 return HTTPFound(
364 request.static_path('rhodecode:public/images/favicon.ico'))
366 request.static_path('rhodecode:public/images/favicon.ico'))
365
367
366 config.add_view(favicon_redirect, route_name='favicon')
368 config.add_view(favicon_redirect, route_name='favicon')
367 config.add_route('favicon', '/favicon.ico')
369 config.add_route('favicon', '/favicon.ico')
368
370
369 def robots_redirect(context, request):
371 def robots_redirect(context, request):
370 return HTTPFound(
372 return HTTPFound(
371 request.static_path('rhodecode:public/robots.txt'))
373 request.static_path('rhodecode:public/robots.txt'))
372
374
373 config.add_view(robots_redirect, route_name='robots')
375 config.add_view(robots_redirect, route_name='robots')
374 config.add_route('robots', '/robots.txt')
376 config.add_route('robots', '/robots.txt')
375
377
376 config.add_static_view(
378 config.add_static_view(
377 '_static/deform', 'deform:static')
379 '_static/deform', 'deform:static')
378 config.add_static_view(
380 config.add_static_view(
379 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
381 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
380
382
381
383
382 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
384 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
383 """
385 """
384 Apply outer WSGI middlewares around the application.
386 Apply outer WSGI middlewares around the application.
385
387
386 Part of this has been moved up from the Pylons layer, so that the
388 Part of this has been moved up from the Pylons layer, so that the
387 data is also available if old Pylons code is hit through an already ported
389 data is also available if old Pylons code is hit through an already ported
388 view.
390 view.
389 """
391 """
390 settings = config.registry.settings
392 settings = config.registry.settings
391
393
392 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
394 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
393 pyramid_app = HttpsFixup(pyramid_app, settings)
395 pyramid_app = HttpsFixup(pyramid_app, settings)
394
396
395 # Add RoutesMiddleware to support the pylons compatibility tween during
397 # Add RoutesMiddleware to support the pylons compatibility tween during
396 # migration to pyramid.
398 # migration to pyramid.
397
399
398 # TODO(marcink): remove after migration to pyramid
400 # TODO(marcink): remove after migration to pyramid
399 if hasattr(config.registry, '_pylons_compat_config'):
401 if hasattr(config.registry, '_pylons_compat_config'):
400 routes_map = config.registry._pylons_compat_config['routes.map']
402 routes_map = config.registry._pylons_compat_config['routes.map']
401 pyramid_app = SkippableRoutesMiddleware(
403 pyramid_app = SkippableRoutesMiddleware(
402 pyramid_app, routes_map,
404 pyramid_app, routes_map,
403 skip_prefixes=(STATIC_FILE_PREFIX, '/_debug_toolbar'))
405 skip_prefixes=(STATIC_FILE_PREFIX, '/_debug_toolbar'))
404
406
405 pyramid_app, _ = wrap_in_appenlight_if_enabled(pyramid_app, settings)
407 pyramid_app, _ = wrap_in_appenlight_if_enabled(pyramid_app, settings)
406
408
407 if settings['gzip_responses']:
409 if settings['gzip_responses']:
408 pyramid_app = make_gzip_middleware(
410 pyramid_app = make_gzip_middleware(
409 pyramid_app, settings, compress_level=1)
411 pyramid_app, settings, compress_level=1)
410
412
411 # this should be the outer most middleware in the wsgi stack since
413 # this should be the outer most middleware in the wsgi stack since
412 # middleware like Routes make database calls
414 # middleware like Routes make database calls
413 def pyramid_app_with_cleanup(environ, start_response):
415 def pyramid_app_with_cleanup(environ, start_response):
414 try:
416 try:
415 return pyramid_app(environ, start_response)
417 return pyramid_app(environ, start_response)
416 finally:
418 finally:
417 # Dispose current database session and rollback uncommitted
419 # Dispose current database session and rollback uncommitted
418 # transactions.
420 # transactions.
419 meta.Session.remove()
421 meta.Session.remove()
420
422
421 # In a single threaded mode server, on non sqlite db we should have
423 # In a single threaded mode server, on non sqlite db we should have
422 # '0 Current Checked out connections' at the end of a request,
424 # '0 Current Checked out connections' at the end of a request,
423 # if not, then something, somewhere is leaving a connection open
425 # if not, then something, somewhere is leaving a connection open
424 pool = meta.Base.metadata.bind.engine.pool
426 pool = meta.Base.metadata.bind.engine.pool
425 log.debug('sa pool status: %s', pool.status())
427 log.debug('sa pool status: %s', pool.status())
426
428
427 return pyramid_app_with_cleanup
429 return pyramid_app_with_cleanup
428
430
429
431
430 def sanitize_settings_and_apply_defaults(settings):
432 def sanitize_settings_and_apply_defaults(settings):
431 """
433 """
432 Applies settings defaults and does all type conversion.
434 Applies settings defaults and does all type conversion.
433
435
434 We would move all settings parsing and preparation into this place, so that
436 We would move all settings parsing and preparation into this place, so that
435 we have only one place left which deals with this part. The remaining parts
437 we have only one place left which deals with this part. The remaining parts
436 of the application would start to rely fully on well prepared settings.
438 of the application would start to rely fully on well prepared settings.
437
439
438 This piece would later be split up per topic to avoid a big fat monster
440 This piece would later be split up per topic to avoid a big fat monster
439 function.
441 function.
440 """
442 """
441
443
442 settings.setdefault('rhodecode.edition', 'Community Edition')
444 settings.setdefault('rhodecode.edition', 'Community Edition')
443
445
444 if 'mako.default_filters' not in settings:
446 if 'mako.default_filters' not in settings:
445 # set custom default filters if we don't have it defined
447 # set custom default filters if we don't have it defined
446 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
448 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
447 settings['mako.default_filters'] = 'h_filter'
449 settings['mako.default_filters'] = 'h_filter'
448
450
449 if 'mako.directories' not in settings:
451 if 'mako.directories' not in settings:
450 mako_directories = settings.setdefault('mako.directories', [
452 mako_directories = settings.setdefault('mako.directories', [
451 # Base templates of the original application
453 # Base templates of the original application
452 'rhodecode:templates',
454 'rhodecode:templates',
453 ])
455 ])
454 log.debug(
456 log.debug(
455 "Using the following Mako template directories: %s",
457 "Using the following Mako template directories: %s",
456 mako_directories)
458 mako_directories)
457
459
458 # Default includes, possible to change as a user
460 # Default includes, possible to change as a user
459 pyramid_includes = settings.setdefault('pyramid.includes', [
461 pyramid_includes = settings.setdefault('pyramid.includes', [
460 'rhodecode.lib.middleware.request_wrapper',
462 'rhodecode.lib.middleware.request_wrapper',
461 ])
463 ])
462 log.debug(
464 log.debug(
463 "Using the following pyramid.includes: %s",
465 "Using the following pyramid.includes: %s",
464 pyramid_includes)
466 pyramid_includes)
465
467
466 # TODO: johbo: Re-think this, usually the call to config.include
468 # TODO: johbo: Re-think this, usually the call to config.include
467 # should allow to pass in a prefix.
469 # should allow to pass in a prefix.
468 settings.setdefault('rhodecode.api.url', '/_admin/api')
470 settings.setdefault('rhodecode.api.url', '/_admin/api')
469
471
470 # Sanitize generic settings.
472 # Sanitize generic settings.
471 _list_setting(settings, 'default_encoding', 'UTF-8')
473 _list_setting(settings, 'default_encoding', 'UTF-8')
472 _bool_setting(settings, 'is_test', 'false')
474 _bool_setting(settings, 'is_test', 'false')
473 _bool_setting(settings, 'gzip_responses', 'false')
475 _bool_setting(settings, 'gzip_responses', 'false')
474
476
475 # Call split out functions that sanitize settings for each topic.
477 # Call split out functions that sanitize settings for each topic.
476 _sanitize_appenlight_settings(settings)
478 _sanitize_appenlight_settings(settings)
477 _sanitize_vcs_settings(settings)
479 _sanitize_vcs_settings(settings)
478
480
479 # configure instance id
481 # configure instance id
480 config_utils.set_instance_id(settings)
482 config_utils.set_instance_id(settings)
481
483
482 return settings
484 return settings
483
485
484
486
485 def _sanitize_appenlight_settings(settings):
487 def _sanitize_appenlight_settings(settings):
486 _bool_setting(settings, 'appenlight', 'false')
488 _bool_setting(settings, 'appenlight', 'false')
487
489
488
490
489 def _sanitize_vcs_settings(settings):
491 def _sanitize_vcs_settings(settings):
490 """
492 """
491 Applies settings defaults and does type conversion for all VCS related
493 Applies settings defaults and does type conversion for all VCS related
492 settings.
494 settings.
493 """
495 """
494 _string_setting(settings, 'vcs.svn.compatible_version', '')
496 _string_setting(settings, 'vcs.svn.compatible_version', '')
495 _string_setting(settings, 'git_rev_filter', '--all')
497 _string_setting(settings, 'git_rev_filter', '--all')
496 _string_setting(settings, 'vcs.hooks.protocol', 'http')
498 _string_setting(settings, 'vcs.hooks.protocol', 'http')
497 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
499 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
498 _string_setting(settings, 'vcs.server', '')
500 _string_setting(settings, 'vcs.server', '')
499 _string_setting(settings, 'vcs.server.log_level', 'debug')
501 _string_setting(settings, 'vcs.server.log_level', 'debug')
500 _string_setting(settings, 'vcs.server.protocol', 'http')
502 _string_setting(settings, 'vcs.server.protocol', 'http')
501 _bool_setting(settings, 'startup.import_repos', 'false')
503 _bool_setting(settings, 'startup.import_repos', 'false')
502 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
504 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
503 _bool_setting(settings, 'vcs.server.enable', 'true')
505 _bool_setting(settings, 'vcs.server.enable', 'true')
504 _bool_setting(settings, 'vcs.start_server', 'false')
506 _bool_setting(settings, 'vcs.start_server', 'false')
505 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
507 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
506 _int_setting(settings, 'vcs.connection_timeout', 3600)
508 _int_setting(settings, 'vcs.connection_timeout', 3600)
507
509
508 # Support legacy values of vcs.scm_app_implementation. Legacy
510 # Support legacy values of vcs.scm_app_implementation. Legacy
509 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http'
511 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http'
510 # which is now mapped to 'http'.
512 # which is now mapped to 'http'.
511 scm_app_impl = settings['vcs.scm_app_implementation']
513 scm_app_impl = settings['vcs.scm_app_implementation']
512 if scm_app_impl == 'rhodecode.lib.middleware.utils.scm_app_http':
514 if scm_app_impl == 'rhodecode.lib.middleware.utils.scm_app_http':
513 settings['vcs.scm_app_implementation'] = 'http'
515 settings['vcs.scm_app_implementation'] = 'http'
514
516
515
517
516 def _int_setting(settings, name, default):
518 def _int_setting(settings, name, default):
517 settings[name] = int(settings.get(name, default))
519 settings[name] = int(settings.get(name, default))
518
520
519
521
520 def _bool_setting(settings, name, default):
522 def _bool_setting(settings, name, default):
521 input = settings.get(name, default)
523 input = settings.get(name, default)
522 if isinstance(input, unicode):
524 if isinstance(input, unicode):
523 input = input.encode('utf8')
525 input = input.encode('utf8')
524 settings[name] = asbool(input)
526 settings[name] = asbool(input)
525
527
526
528
527 def _list_setting(settings, name, default):
529 def _list_setting(settings, name, default):
528 raw_value = settings.get(name, default)
530 raw_value = settings.get(name, default)
529
531
530 old_separator = ','
532 old_separator = ','
531 if old_separator in raw_value:
533 if old_separator in raw_value:
532 # If we get a comma separated list, pass it to our own function.
534 # If we get a comma separated list, pass it to our own function.
533 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
535 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
534 else:
536 else:
535 # Otherwise we assume it uses pyramids space/newline separation.
537 # Otherwise we assume it uses pyramids space/newline separation.
536 settings[name] = aslist(raw_value)
538 settings[name] = aslist(raw_value)
537
539
538
540
539 def _string_setting(settings, name, default, lower=True):
541 def _string_setting(settings, name, default, lower=True):
540 value = settings.get(name, default)
542 value = settings.get(name, default)
541 if lower:
543 if lower:
542 value = value.lower()
544 value = value.lower()
543 settings[name] = value
545 settings[name] = value
@@ -1,264 +1,259 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 Routes configuration
22 Routes configuration
23
23
24 The more specific and detailed routes should be defined first so they
24 The more specific and detailed routes should be defined first so they
25 may take precedent over the more generic routes. For more information
25 may take precedent over the more generic routes. For more information
26 refer to the routes manual at http://routes.groovie.org/docs/
26 refer to the routes manual at http://routes.groovie.org/docs/
27
27
28 IMPORTANT: if you change any routing here, make sure to take a look at lib/base.py
28 IMPORTANT: if you change any routing here, make sure to take a look at lib/base.py
29 and _route_name variable which uses some of stored naming here to do redirects.
29 and _route_name variable which uses some of stored naming here to do redirects.
30 """
30 """
31 import os
31 import os
32 import re
32 import re
33 from routes import Mapper
33 from routes import Mapper
34
34
35 # prefix for non repository related links needs to be prefixed with `/`
35 # prefix for non repository related links needs to be prefixed with `/`
36 ADMIN_PREFIX = '/_admin'
36 ADMIN_PREFIX = '/_admin'
37 STATIC_FILE_PREFIX = '/_static'
37 STATIC_FILE_PREFIX = '/_static'
38
38
39 # Default requirements for URL parts
39 # Default requirements for URL parts
40 URL_NAME_REQUIREMENTS = {
40 URL_NAME_REQUIREMENTS = {
41 # group name can have a slash in them, but they must not end with a slash
41 # group name can have a slash in them, but they must not end with a slash
42 'group_name': r'.*?[^/]',
42 'group_name': r'.*?[^/]',
43 'repo_group_name': r'.*?[^/]',
43 'repo_group_name': r'.*?[^/]',
44 # repo names can have a slash in them, but they must not end with a slash
44 # repo names can have a slash in them, but they must not end with a slash
45 'repo_name': r'.*?[^/]',
45 'repo_name': r'.*?[^/]',
46 # file path eats up everything at the end
46 # file path eats up everything at the end
47 'f_path': r'.*',
47 'f_path': r'.*',
48 # reference types
48 # reference types
49 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
49 'source_ref_type': '(branch|book|tag|rev|\%\(source_ref_type\)s)',
50 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
50 'target_ref_type': '(branch|book|tag|rev|\%\(target_ref_type\)s)',
51 }
51 }
52
52
53
53
54 class JSRoutesMapper(Mapper):
54 class JSRoutesMapper(Mapper):
55 """
55 """
56 Wrapper for routes.Mapper to make pyroutes compatible url definitions
56 Wrapper for routes.Mapper to make pyroutes compatible url definitions
57 """
57 """
58 _named_route_regex = re.compile(r'^[a-z-_0-9A-Z]+$')
58 _named_route_regex = re.compile(r'^[a-z-_0-9A-Z]+$')
59 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
59 _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)')
60 def __init__(self, *args, **kw):
60 def __init__(self, *args, **kw):
61 super(JSRoutesMapper, self).__init__(*args, **kw)
61 super(JSRoutesMapper, self).__init__(*args, **kw)
62 self._jsroutes = []
62 self._jsroutes = []
63
63
64 def connect(self, *args, **kw):
64 def connect(self, *args, **kw):
65 """
65 """
66 Wrapper for connect to take an extra argument jsroute=True
66 Wrapper for connect to take an extra argument jsroute=True
67
67
68 :param jsroute: boolean, if True will add the route to the pyroutes list
68 :param jsroute: boolean, if True will add the route to the pyroutes list
69 """
69 """
70 if kw.pop('jsroute', False):
70 if kw.pop('jsroute', False):
71 if not self._named_route_regex.match(args[0]):
71 if not self._named_route_regex.match(args[0]):
72 raise Exception('only named routes can be added to pyroutes')
72 raise Exception('only named routes can be added to pyroutes')
73 self._jsroutes.append(args[0])
73 self._jsroutes.append(args[0])
74
74
75 super(JSRoutesMapper, self).connect(*args, **kw)
75 super(JSRoutesMapper, self).connect(*args, **kw)
76
76
77 def _extract_route_information(self, route):
77 def _extract_route_information(self, route):
78 """
78 """
79 Convert a route into tuple(name, path, args), eg:
79 Convert a route into tuple(name, path, args), eg:
80 ('show_user', '/profile/%(username)s', ['username'])
80 ('show_user', '/profile/%(username)s', ['username'])
81 """
81 """
82 routepath = route.routepath
82 routepath = route.routepath
83 def replace(matchobj):
83 def replace(matchobj):
84 if matchobj.group(1):
84 if matchobj.group(1):
85 return "%%(%s)s" % matchobj.group(1).split(':')[0]
85 return "%%(%s)s" % matchobj.group(1).split(':')[0]
86 else:
86 else:
87 return "%%(%s)s" % matchobj.group(2)
87 return "%%(%s)s" % matchobj.group(2)
88
88
89 routepath = self._argument_prog.sub(replace, routepath)
89 routepath = self._argument_prog.sub(replace, routepath)
90 return (
90 return (
91 route.name,
91 route.name,
92 routepath,
92 routepath,
93 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
93 [(arg[0].split(':')[0] if arg[0] != '' else arg[1])
94 for arg in self._argument_prog.findall(route.routepath)]
94 for arg in self._argument_prog.findall(route.routepath)]
95 )
95 )
96
96
97 def jsroutes(self):
97 def jsroutes(self):
98 """
98 """
99 Return a list of pyroutes.js compatible routes
99 Return a list of pyroutes.js compatible routes
100 """
100 """
101 for route_name in self._jsroutes:
101 for route_name in self._jsroutes:
102 yield self._extract_route_information(self._routenames[route_name])
102 yield self._extract_route_information(self._routenames[route_name])
103
103
104
104
105 def make_map(config):
105 def make_map(config):
106 """Create, configure and return the routes Mapper"""
106 """Create, configure and return the routes Mapper"""
107 rmap = JSRoutesMapper(
107 rmap = JSRoutesMapper(
108 directory=config['pylons.paths']['controllers'],
108 directory=config['pylons.paths']['controllers'],
109 always_scan=config['debug'])
109 always_scan=config['debug'])
110 rmap.minimization = False
110 rmap.minimization = False
111 rmap.explicit = False
111 rmap.explicit = False
112
112
113 from rhodecode.lib.utils2 import str2bool
113 from rhodecode.lib.utils2 import str2bool
114 from rhodecode.model import repo, repo_group
114 from rhodecode.model import repo, repo_group
115
115
116 def check_repo(environ, match_dict):
116 def check_repo(environ, match_dict):
117 """
117 """
118 check for valid repository for proper 404 handling
118 check for valid repository for proper 404 handling
119
119
120 :param environ:
120 :param environ:
121 :param match_dict:
121 :param match_dict:
122 """
122 """
123 repo_name = match_dict.get('repo_name')
123 repo_name = match_dict.get('repo_name')
124
124
125 if match_dict.get('f_path'):
125 if match_dict.get('f_path'):
126 # fix for multiple initial slashes that causes errors
126 # fix for multiple initial slashes that causes errors
127 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
127 match_dict['f_path'] = match_dict['f_path'].lstrip('/')
128 repo_model = repo.RepoModel()
128 repo_model = repo.RepoModel()
129 by_name_match = repo_model.get_by_repo_name(repo_name)
129 by_name_match = repo_model.get_by_repo_name(repo_name)
130 # if we match quickly from database, short circuit the operation,
130 # if we match quickly from database, short circuit the operation,
131 # and validate repo based on the type.
131 # and validate repo based on the type.
132 if by_name_match:
132 if by_name_match:
133 return True
133 return True
134
134
135 by_id_match = repo_model.get_repo_by_id(repo_name)
135 by_id_match = repo_model.get_repo_by_id(repo_name)
136 if by_id_match:
136 if by_id_match:
137 repo_name = by_id_match.repo_name
137 repo_name = by_id_match.repo_name
138 match_dict['repo_name'] = repo_name
138 match_dict['repo_name'] = repo_name
139 return True
139 return True
140
140
141 return False
141 return False
142
142
143 def check_group(environ, match_dict):
143 def check_group(environ, match_dict):
144 """
144 """
145 check for valid repository group path for proper 404 handling
145 check for valid repository group path for proper 404 handling
146
146
147 :param environ:
147 :param environ:
148 :param match_dict:
148 :param match_dict:
149 """
149 """
150 repo_group_name = match_dict.get('group_name')
150 repo_group_name = match_dict.get('group_name')
151 repo_group_model = repo_group.RepoGroupModel()
151 repo_group_model = repo_group.RepoGroupModel()
152 by_name_match = repo_group_model.get_by_group_name(repo_group_name)
152 by_name_match = repo_group_model.get_by_group_name(repo_group_name)
153 if by_name_match:
153 if by_name_match:
154 return True
154 return True
155
155
156 return False
156 return False
157
157
158 def check_user_group(environ, match_dict):
158 def check_user_group(environ, match_dict):
159 """
159 """
160 check for valid user group for proper 404 handling
160 check for valid user group for proper 404 handling
161
161
162 :param environ:
162 :param environ:
163 :param match_dict:
163 :param match_dict:
164 """
164 """
165 return True
165 return True
166
166
167 def check_int(environ, match_dict):
167 def check_int(environ, match_dict):
168 return match_dict.get('id').isdigit()
168 return match_dict.get('id').isdigit()
169
169
170
170
171 #==========================================================================
171 #==========================================================================
172 # CUSTOM ROUTES HERE
172 # CUSTOM ROUTES HERE
173 #==========================================================================
173 #==========================================================================
174
174
175 # ADMIN SETTINGS ROUTES
175 # ADMIN SETTINGS ROUTES
176 with rmap.submapper(path_prefix=ADMIN_PREFIX,
176 with rmap.submapper(path_prefix=ADMIN_PREFIX,
177 controller='admin/settings') as m:
177 controller='admin/settings') as m:
178
178
179 # default
179 # default
180 m.connect('admin_settings', '/settings',
180 m.connect('admin_settings', '/settings',
181 action='settings_global_update',
181 action='settings_global_update',
182 conditions={'method': ['POST']})
182 conditions={'method': ['POST']})
183 m.connect('admin_settings', '/settings',
183 m.connect('admin_settings', '/settings',
184 action='settings_global', conditions={'method': ['GET']})
184 action='settings_global', conditions={'method': ['GET']})
185
185
186 m.connect('admin_settings_vcs', '/settings/vcs',
186 m.connect('admin_settings_vcs', '/settings/vcs',
187 action='settings_vcs_update',
187 action='settings_vcs_update',
188 conditions={'method': ['POST']})
188 conditions={'method': ['POST']})
189 m.connect('admin_settings_vcs', '/settings/vcs',
189 m.connect('admin_settings_vcs', '/settings/vcs',
190 action='settings_vcs',
190 action='settings_vcs',
191 conditions={'method': ['GET']})
191 conditions={'method': ['GET']})
192 m.connect('admin_settings_vcs', '/settings/vcs',
192 m.connect('admin_settings_vcs', '/settings/vcs',
193 action='delete_svn_pattern',
193 action='delete_svn_pattern',
194 conditions={'method': ['DELETE']})
194 conditions={'method': ['DELETE']})
195
195
196 m.connect('admin_settings_mapping', '/settings/mapping',
196 m.connect('admin_settings_mapping', '/settings/mapping',
197 action='settings_mapping_update',
197 action='settings_mapping_update',
198 conditions={'method': ['POST']})
198 conditions={'method': ['POST']})
199 m.connect('admin_settings_mapping', '/settings/mapping',
199 m.connect('admin_settings_mapping', '/settings/mapping',
200 action='settings_mapping', conditions={'method': ['GET']})
200 action='settings_mapping', conditions={'method': ['GET']})
201
201
202 m.connect('admin_settings_global', '/settings/global',
202 m.connect('admin_settings_global', '/settings/global',
203 action='settings_global_update',
203 action='settings_global_update',
204 conditions={'method': ['POST']})
204 conditions={'method': ['POST']})
205 m.connect('admin_settings_global', '/settings/global',
205 m.connect('admin_settings_global', '/settings/global',
206 action='settings_global', conditions={'method': ['GET']})
206 action='settings_global', conditions={'method': ['GET']})
207
207
208 m.connect('admin_settings_visual', '/settings/visual',
208 m.connect('admin_settings_visual', '/settings/visual',
209 action='settings_visual_update',
209 action='settings_visual_update',
210 conditions={'method': ['POST']})
210 conditions={'method': ['POST']})
211 m.connect('admin_settings_visual', '/settings/visual',
211 m.connect('admin_settings_visual', '/settings/visual',
212 action='settings_visual', conditions={'method': ['GET']})
212 action='settings_visual', conditions={'method': ['GET']})
213
213
214 m.connect('admin_settings_issuetracker',
214 m.connect('admin_settings_issuetracker',
215 '/settings/issue-tracker', action='settings_issuetracker',
215 '/settings/issue-tracker', action='settings_issuetracker',
216 conditions={'method': ['GET']})
216 conditions={'method': ['GET']})
217 m.connect('admin_settings_issuetracker_save',
217 m.connect('admin_settings_issuetracker_save',
218 '/settings/issue-tracker/save',
218 '/settings/issue-tracker/save',
219 action='settings_issuetracker_save',
219 action='settings_issuetracker_save',
220 conditions={'method': ['POST']})
220 conditions={'method': ['POST']})
221 m.connect('admin_issuetracker_test', '/settings/issue-tracker/test',
221 m.connect('admin_issuetracker_test', '/settings/issue-tracker/test',
222 action='settings_issuetracker_test',
222 action='settings_issuetracker_test',
223 conditions={'method': ['POST']})
223 conditions={'method': ['POST']})
224 m.connect('admin_issuetracker_delete',
224 m.connect('admin_issuetracker_delete',
225 '/settings/issue-tracker/delete',
225 '/settings/issue-tracker/delete',
226 action='settings_issuetracker_delete',
226 action='settings_issuetracker_delete',
227 conditions={'method': ['DELETE']})
227 conditions={'method': ['DELETE']})
228
228
229 m.connect('admin_settings_email', '/settings/email',
229 m.connect('admin_settings_email', '/settings/email',
230 action='settings_email_update',
230 action='settings_email_update',
231 conditions={'method': ['POST']})
231 conditions={'method': ['POST']})
232 m.connect('admin_settings_email', '/settings/email',
232 m.connect('admin_settings_email', '/settings/email',
233 action='settings_email', conditions={'method': ['GET']})
233 action='settings_email', conditions={'method': ['GET']})
234
234
235 m.connect('admin_settings_hooks', '/settings/hooks',
235 m.connect('admin_settings_hooks', '/settings/hooks',
236 action='settings_hooks_update',
236 action='settings_hooks_update',
237 conditions={'method': ['POST', 'DELETE']})
237 conditions={'method': ['POST', 'DELETE']})
238 m.connect('admin_settings_hooks', '/settings/hooks',
238 m.connect('admin_settings_hooks', '/settings/hooks',
239 action='settings_hooks', conditions={'method': ['GET']})
239 action='settings_hooks', conditions={'method': ['GET']})
240
240
241 m.connect('admin_settings_search', '/settings/search',
241 m.connect('admin_settings_search', '/settings/search',
242 action='settings_search', conditions={'method': ['GET']})
242 action='settings_search', conditions={'method': ['GET']})
243
243
244 m.connect('admin_settings_supervisor', '/settings/supervisor',
245 action='settings_supervisor', conditions={'method': ['GET']})
246 m.connect('admin_settings_supervisor_log', '/settings/supervisor/{procid}/log',
247 action='settings_supervisor_log', conditions={'method': ['GET']})
248
249 m.connect('admin_settings_labs', '/settings/labs',
244 m.connect('admin_settings_labs', '/settings/labs',
250 action='settings_labs_update',
245 action='settings_labs_update',
251 conditions={'method': ['POST']})
246 conditions={'method': ['POST']})
252 m.connect('admin_settings_labs', '/settings/labs',
247 m.connect('admin_settings_labs', '/settings/labs',
253 action='settings_labs', conditions={'method': ['GET']})
248 action='settings_labs', conditions={'method': ['GET']})
254
249
255 # ADMIN MY ACCOUNT
250 # ADMIN MY ACCOUNT
256 with rmap.submapper(path_prefix=ADMIN_PREFIX,
251 with rmap.submapper(path_prefix=ADMIN_PREFIX,
257 controller='admin/my_account') as m:
252 controller='admin/my_account') as m:
258
253
259 # NOTE(marcink): this needs to be kept for password force flag to be
254 # NOTE(marcink): this needs to be kept for password force flag to be
260 # handled in pylons controllers, remove after full migration to pyramid
255 # handled in pylons controllers, remove after full migration to pyramid
261 m.connect('my_account_password', '/my_account/password',
256 m.connect('my_account_password', '/my_account/password',
262 action='my_account_password', conditions={'method': ['GET']})
257 action='my_account_password', conditions={'method': ['GET']})
263
258
264 return rmap
259 return rmap
@@ -1,697 +1,631 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
28
29 import datetime
29 import datetime
30 import formencode
30 import formencode
31 from formencode import htmlfill
31 from formencode import htmlfill
32 from pylons import request, tmpl_context as c, url, config
32 from pylons import request, tmpl_context as c, url, config
33 from pylons.controllers.util import redirect
33 from pylons.controllers.util import redirect
34 from pylons.i18n.translation import _
34 from pylons.i18n.translation import _
35 from pylons.decorators import jsonify
35 from pylons.decorators import jsonify
36 from pyramid.threadlocal import get_current_registry
36 from pyramid.threadlocal import get_current_registry
37 from webob.exc import HTTPBadRequest
37 from webob.exc import HTTPBadRequest
38
38
39 import rhodecode
39 import rhodecode
40 from rhodecode.apps.admin.navigation import navigation_list
40 from rhodecode.apps.admin.navigation import navigation_list
41 from rhodecode.apps.svn_support.config_keys import generate_config
41 from rhodecode.apps.svn_support.config_keys import generate_config
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
51
50
52 from rhodecode.model.db import RhodeCodeUi, Repository
51 from rhodecode.model.db import RhodeCodeUi, Repository
53 from rhodecode.model.forms import ApplicationSettingsForm, \
52 from rhodecode.model.forms import ApplicationSettingsForm, \
54 ApplicationUiSettingsForm, ApplicationVisualisationForm, \
53 ApplicationUiSettingsForm, ApplicationVisualisationForm, \
55 LabsSettingsForm, IssueTrackerPatternsForm
54 LabsSettingsForm, IssueTrackerPatternsForm
56 from rhodecode.model.repo_group import RepoGroupModel
55 from rhodecode.model.repo_group import RepoGroupModel
57
56
58 from rhodecode.model.scm import ScmModel
57 from rhodecode.model.scm import ScmModel
59 from rhodecode.model.notification import EmailNotificationModel
58 from rhodecode.model.notification import EmailNotificationModel
60 from rhodecode.model.meta import Session
59 from rhodecode.model.meta import Session
61 from rhodecode.model.settings import (
60 from rhodecode.model.settings import (
62 IssueTrackerSettingsModel, VcsSettingsModel, SettingNotFound,
61 IssueTrackerSettingsModel, VcsSettingsModel, SettingNotFound,
63 SettingsModel)
62 SettingsModel)
64
63
65 from rhodecode.model.supervisor import SupervisorModel, SUPERVISOR_MASTER
66
67
64
68 log = logging.getLogger(__name__)
65 log = logging.getLogger(__name__)
69
66
70
67
71 class SettingsController(BaseController):
68 class SettingsController(BaseController):
72 """REST Controller styled on the Atom Publishing Protocol"""
69 """REST Controller styled on the Atom Publishing Protocol"""
73 # To properly map this controller, ensure your config/routing.py
70 # To properly map this controller, ensure your config/routing.py
74 # file has a resource setup:
71 # file has a resource setup:
75 # map.resource('setting', 'settings', controller='admin/settings',
72 # map.resource('setting', 'settings', controller='admin/settings',
76 # path_prefix='/admin', name_prefix='admin_')
73 # path_prefix='/admin', name_prefix='admin_')
77
74
78 @LoginRequired()
75 @LoginRequired()
79 def __before__(self):
76 def __before__(self):
80 super(SettingsController, self).__before__()
77 super(SettingsController, self).__before__()
81 c.labs_active = str2bool(
78 c.labs_active = str2bool(
82 rhodecode.CONFIG.get('labs_settings_active', 'true'))
79 rhodecode.CONFIG.get('labs_settings_active', 'true'))
83 c.navlist = navigation_list(request)
80 c.navlist = navigation_list(request)
84
81
85 def _get_ui_settings(self):
82 def _get_ui_settings(self):
86 ret = RhodeCodeUi.query().all()
83 ret = RhodeCodeUi.query().all()
87
84
88 if not ret:
85 if not ret:
89 raise Exception('Could not get application ui settings !')
86 raise Exception('Could not get application ui settings !')
90 settings = {}
87 settings = {}
91 for each in ret:
88 for each in ret:
92 k = each.ui_key
89 k = each.ui_key
93 v = each.ui_value
90 v = each.ui_value
94 if k == '/':
91 if k == '/':
95 k = 'root_path'
92 k = 'root_path'
96
93
97 if k in ['push_ssl', 'publish', 'enabled']:
94 if k in ['push_ssl', 'publish', 'enabled']:
98 v = str2bool(v)
95 v = str2bool(v)
99
96
100 if k.find('.') != -1:
97 if k.find('.') != -1:
101 k = k.replace('.', '_')
98 k = k.replace('.', '_')
102
99
103 if each.ui_section in ['hooks', 'extensions']:
100 if each.ui_section in ['hooks', 'extensions']:
104 v = each.ui_active
101 v = each.ui_active
105
102
106 settings[each.ui_section + '_' + k] = v
103 settings[each.ui_section + '_' + k] = v
107 return settings
104 return settings
108
105
109 @HasPermissionAllDecorator('hg.admin')
106 @HasPermissionAllDecorator('hg.admin')
110 @auth.CSRFRequired()
107 @auth.CSRFRequired()
111 @jsonify
108 @jsonify
112 def delete_svn_pattern(self):
109 def delete_svn_pattern(self):
113 if not request.is_xhr:
110 if not request.is_xhr:
114 raise HTTPBadRequest()
111 raise HTTPBadRequest()
115
112
116 delete_pattern_id = request.POST.get('delete_svn_pattern')
113 delete_pattern_id = request.POST.get('delete_svn_pattern')
117 model = VcsSettingsModel()
114 model = VcsSettingsModel()
118 try:
115 try:
119 model.delete_global_svn_pattern(delete_pattern_id)
116 model.delete_global_svn_pattern(delete_pattern_id)
120 except SettingNotFound:
117 except SettingNotFound:
121 raise HTTPBadRequest()
118 raise HTTPBadRequest()
122
119
123 Session().commit()
120 Session().commit()
124 return True
121 return True
125
122
126 @HasPermissionAllDecorator('hg.admin')
123 @HasPermissionAllDecorator('hg.admin')
127 @auth.CSRFRequired()
124 @auth.CSRFRequired()
128 def settings_vcs_update(self):
125 def settings_vcs_update(self):
129 """POST /admin/settings: All items in the collection"""
126 """POST /admin/settings: All items in the collection"""
130 # url('admin_settings_vcs')
127 # url('admin_settings_vcs')
131 c.active = 'vcs'
128 c.active = 'vcs'
132
129
133 model = VcsSettingsModel()
130 model = VcsSettingsModel()
134 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
131 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
135 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
132 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
136
133
137 # TODO: Replace with request.registry after migrating to pyramid.
134 # TODO: Replace with request.registry after migrating to pyramid.
138 pyramid_settings = get_current_registry().settings
135 pyramid_settings = get_current_registry().settings
139 c.svn_proxy_generate_config = pyramid_settings[generate_config]
136 c.svn_proxy_generate_config = pyramid_settings[generate_config]
140
137
141 application_form = ApplicationUiSettingsForm()()
138 application_form = ApplicationUiSettingsForm()()
142
139
143 try:
140 try:
144 form_result = application_form.to_python(dict(request.POST))
141 form_result = application_form.to_python(dict(request.POST))
145 except formencode.Invalid as errors:
142 except formencode.Invalid as errors:
146 h.flash(
143 h.flash(
147 _("Some form inputs contain invalid data."),
144 _("Some form inputs contain invalid data."),
148 category='error')
145 category='error')
149 return htmlfill.render(
146 return htmlfill.render(
150 render('admin/settings/settings.mako'),
147 render('admin/settings/settings.mako'),
151 defaults=errors.value,
148 defaults=errors.value,
152 errors=errors.error_dict or {},
149 errors=errors.error_dict or {},
153 prefix_error=False,
150 prefix_error=False,
154 encoding="UTF-8",
151 encoding="UTF-8",
155 force_defaults=False
152 force_defaults=False
156 )
153 )
157
154
158 try:
155 try:
159 if c.visual.allow_repo_location_change:
156 if c.visual.allow_repo_location_change:
160 model.update_global_path_setting(
157 model.update_global_path_setting(
161 form_result['paths_root_path'])
158 form_result['paths_root_path'])
162
159
163 model.update_global_ssl_setting(form_result['web_push_ssl'])
160 model.update_global_ssl_setting(form_result['web_push_ssl'])
164 model.update_global_hook_settings(form_result)
161 model.update_global_hook_settings(form_result)
165
162
166 model.create_or_update_global_svn_settings(form_result)
163 model.create_or_update_global_svn_settings(form_result)
167 model.create_or_update_global_hg_settings(form_result)
164 model.create_or_update_global_hg_settings(form_result)
168 model.create_or_update_global_git_settings(form_result)
165 model.create_or_update_global_git_settings(form_result)
169 model.create_or_update_global_pr_settings(form_result)
166 model.create_or_update_global_pr_settings(form_result)
170 except Exception:
167 except Exception:
171 log.exception("Exception while updating settings")
168 log.exception("Exception while updating settings")
172 h.flash(_('Error occurred during updating '
169 h.flash(_('Error occurred during updating '
173 'application settings'), category='error')
170 'application settings'), category='error')
174 else:
171 else:
175 Session().commit()
172 Session().commit()
176 h.flash(_('Updated VCS settings'), category='success')
173 h.flash(_('Updated VCS settings'), category='success')
177 return redirect(url('admin_settings_vcs'))
174 return redirect(url('admin_settings_vcs'))
178
175
179 return htmlfill.render(
176 return htmlfill.render(
180 render('admin/settings/settings.mako'),
177 render('admin/settings/settings.mako'),
181 defaults=self._form_defaults(),
178 defaults=self._form_defaults(),
182 encoding="UTF-8",
179 encoding="UTF-8",
183 force_defaults=False)
180 force_defaults=False)
184
181
185 @HasPermissionAllDecorator('hg.admin')
182 @HasPermissionAllDecorator('hg.admin')
186 def settings_vcs(self):
183 def settings_vcs(self):
187 """GET /admin/settings: All items in the collection"""
184 """GET /admin/settings: All items in the collection"""
188 # url('admin_settings_vcs')
185 # url('admin_settings_vcs')
189 c.active = 'vcs'
186 c.active = 'vcs'
190 model = VcsSettingsModel()
187 model = VcsSettingsModel()
191 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
188 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
192 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
189 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
193
190
194 # TODO: Replace with request.registry after migrating to pyramid.
191 # TODO: Replace with request.registry after migrating to pyramid.
195 pyramid_settings = get_current_registry().settings
192 pyramid_settings = get_current_registry().settings
196 c.svn_proxy_generate_config = pyramid_settings[generate_config]
193 c.svn_proxy_generate_config = pyramid_settings[generate_config]
197
194
198 defaults = self._form_defaults()
195 defaults = self._form_defaults()
199
196
200 model.create_largeobjects_dirs_if_needed(defaults['paths_root_path'])
197 model.create_largeobjects_dirs_if_needed(defaults['paths_root_path'])
201 return htmlfill.render(
198 return htmlfill.render(
202 render('admin/settings/settings.mako'),
199 render('admin/settings/settings.mako'),
203 defaults=defaults,
200 defaults=defaults,
204 encoding="UTF-8",
201 encoding="UTF-8",
205 force_defaults=False)
202 force_defaults=False)
206
203
207 @HasPermissionAllDecorator('hg.admin')
204 @HasPermissionAllDecorator('hg.admin')
208 @auth.CSRFRequired()
205 @auth.CSRFRequired()
209 def settings_mapping_update(self):
206 def settings_mapping_update(self):
210 """POST /admin/settings/mapping: All items in the collection"""
207 """POST /admin/settings/mapping: All items in the collection"""
211 # url('admin_settings_mapping')
208 # url('admin_settings_mapping')
212 c.active = 'mapping'
209 c.active = 'mapping'
213 rm_obsolete = request.POST.get('destroy', False)
210 rm_obsolete = request.POST.get('destroy', False)
214 invalidate_cache = request.POST.get('invalidate', False)
211 invalidate_cache = request.POST.get('invalidate', False)
215 log.debug(
212 log.debug(
216 'rescanning repo location with destroy obsolete=%s', rm_obsolete)
213 'rescanning repo location with destroy obsolete=%s', rm_obsolete)
217
214
218 if invalidate_cache:
215 if invalidate_cache:
219 log.debug('invalidating all repositories cache')
216 log.debug('invalidating all repositories cache')
220 for repo in Repository.get_all():
217 for repo in Repository.get_all():
221 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
218 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
222
219
223 filesystem_repos = ScmModel().repo_scan()
220 filesystem_repos = ScmModel().repo_scan()
224 added, removed = repo2db_mapper(filesystem_repos, rm_obsolete)
221 added, removed = repo2db_mapper(filesystem_repos, rm_obsolete)
225 _repr = lambda l: ', '.join(map(safe_unicode, l)) or '-'
222 _repr = lambda l: ', '.join(map(safe_unicode, l)) or '-'
226 h.flash(_('Repositories successfully '
223 h.flash(_('Repositories successfully '
227 'rescanned added: %s ; removed: %s') %
224 'rescanned added: %s ; removed: %s') %
228 (_repr(added), _repr(removed)),
225 (_repr(added), _repr(removed)),
229 category='success')
226 category='success')
230 return redirect(url('admin_settings_mapping'))
227 return redirect(url('admin_settings_mapping'))
231
228
232 @HasPermissionAllDecorator('hg.admin')
229 @HasPermissionAllDecorator('hg.admin')
233 def settings_mapping(self):
230 def settings_mapping(self):
234 """GET /admin/settings/mapping: All items in the collection"""
231 """GET /admin/settings/mapping: All items in the collection"""
235 # url('admin_settings_mapping')
232 # url('admin_settings_mapping')
236 c.active = 'mapping'
233 c.active = 'mapping'
237
234
238 return htmlfill.render(
235 return htmlfill.render(
239 render('admin/settings/settings.mako'),
236 render('admin/settings/settings.mako'),
240 defaults=self._form_defaults(),
237 defaults=self._form_defaults(),
241 encoding="UTF-8",
238 encoding="UTF-8",
242 force_defaults=False)
239 force_defaults=False)
243
240
244 @HasPermissionAllDecorator('hg.admin')
241 @HasPermissionAllDecorator('hg.admin')
245 @auth.CSRFRequired()
242 @auth.CSRFRequired()
246 def settings_global_update(self):
243 def settings_global_update(self):
247 """POST /admin/settings/global: All items in the collection"""
244 """POST /admin/settings/global: All items in the collection"""
248 # url('admin_settings_global')
245 # url('admin_settings_global')
249 c.active = 'global'
246 c.active = 'global'
250 c.personal_repo_group_default_pattern = RepoGroupModel()\
247 c.personal_repo_group_default_pattern = RepoGroupModel()\
251 .get_personal_group_name_pattern()
248 .get_personal_group_name_pattern()
252 application_form = ApplicationSettingsForm()()
249 application_form = ApplicationSettingsForm()()
253 try:
250 try:
254 form_result = application_form.to_python(dict(request.POST))
251 form_result = application_form.to_python(dict(request.POST))
255 except formencode.Invalid as errors:
252 except formencode.Invalid as errors:
256 return htmlfill.render(
253 return htmlfill.render(
257 render('admin/settings/settings.mako'),
254 render('admin/settings/settings.mako'),
258 defaults=errors.value,
255 defaults=errors.value,
259 errors=errors.error_dict or {},
256 errors=errors.error_dict or {},
260 prefix_error=False,
257 prefix_error=False,
261 encoding="UTF-8",
258 encoding="UTF-8",
262 force_defaults=False)
259 force_defaults=False)
263
260
264 try:
261 try:
265 settings = [
262 settings = [
266 ('title', 'rhodecode_title', 'unicode'),
263 ('title', 'rhodecode_title', 'unicode'),
267 ('realm', 'rhodecode_realm', 'unicode'),
264 ('realm', 'rhodecode_realm', 'unicode'),
268 ('pre_code', 'rhodecode_pre_code', 'unicode'),
265 ('pre_code', 'rhodecode_pre_code', 'unicode'),
269 ('post_code', 'rhodecode_post_code', 'unicode'),
266 ('post_code', 'rhodecode_post_code', 'unicode'),
270 ('captcha_public_key', 'rhodecode_captcha_public_key', 'unicode'),
267 ('captcha_public_key', 'rhodecode_captcha_public_key', 'unicode'),
271 ('captcha_private_key', 'rhodecode_captcha_private_key', 'unicode'),
268 ('captcha_private_key', 'rhodecode_captcha_private_key', 'unicode'),
272 ('create_personal_repo_group', 'rhodecode_create_personal_repo_group', 'bool'),
269 ('create_personal_repo_group', 'rhodecode_create_personal_repo_group', 'bool'),
273 ('personal_repo_group_pattern', 'rhodecode_personal_repo_group_pattern', 'unicode'),
270 ('personal_repo_group_pattern', 'rhodecode_personal_repo_group_pattern', 'unicode'),
274 ]
271 ]
275 for setting, form_key, type_ in settings:
272 for setting, form_key, type_ in settings:
276 sett = SettingsModel().create_or_update_setting(
273 sett = SettingsModel().create_or_update_setting(
277 setting, form_result[form_key], type_)
274 setting, form_result[form_key], type_)
278 Session().add(sett)
275 Session().add(sett)
279
276
280 Session().commit()
277 Session().commit()
281 SettingsModel().invalidate_settings_cache()
278 SettingsModel().invalidate_settings_cache()
282 h.flash(_('Updated application settings'), category='success')
279 h.flash(_('Updated application settings'), category='success')
283 except Exception:
280 except Exception:
284 log.exception("Exception while updating application settings")
281 log.exception("Exception while updating application settings")
285 h.flash(
282 h.flash(
286 _('Error occurred during updating application settings'),
283 _('Error occurred during updating application settings'),
287 category='error')
284 category='error')
288
285
289 return redirect(url('admin_settings_global'))
286 return redirect(url('admin_settings_global'))
290
287
291 @HasPermissionAllDecorator('hg.admin')
288 @HasPermissionAllDecorator('hg.admin')
292 def settings_global(self):
289 def settings_global(self):
293 """GET /admin/settings/global: All items in the collection"""
290 """GET /admin/settings/global: All items in the collection"""
294 # url('admin_settings_global')
291 # url('admin_settings_global')
295 c.active = 'global'
292 c.active = 'global'
296 c.personal_repo_group_default_pattern = RepoGroupModel()\
293 c.personal_repo_group_default_pattern = RepoGroupModel()\
297 .get_personal_group_name_pattern()
294 .get_personal_group_name_pattern()
298
295
299 return htmlfill.render(
296 return htmlfill.render(
300 render('admin/settings/settings.mako'),
297 render('admin/settings/settings.mako'),
301 defaults=self._form_defaults(),
298 defaults=self._form_defaults(),
302 encoding="UTF-8",
299 encoding="UTF-8",
303 force_defaults=False)
300 force_defaults=False)
304
301
305 @HasPermissionAllDecorator('hg.admin')
302 @HasPermissionAllDecorator('hg.admin')
306 @auth.CSRFRequired()
303 @auth.CSRFRequired()
307 def settings_visual_update(self):
304 def settings_visual_update(self):
308 """POST /admin/settings/visual: All items in the collection"""
305 """POST /admin/settings/visual: All items in the collection"""
309 # url('admin_settings_visual')
306 # url('admin_settings_visual')
310 c.active = 'visual'
307 c.active = 'visual'
311 application_form = ApplicationVisualisationForm()()
308 application_form = ApplicationVisualisationForm()()
312 try:
309 try:
313 form_result = application_form.to_python(dict(request.POST))
310 form_result = application_form.to_python(dict(request.POST))
314 except formencode.Invalid as errors:
311 except formencode.Invalid as errors:
315 return htmlfill.render(
312 return htmlfill.render(
316 render('admin/settings/settings.mako'),
313 render('admin/settings/settings.mako'),
317 defaults=errors.value,
314 defaults=errors.value,
318 errors=errors.error_dict or {},
315 errors=errors.error_dict or {},
319 prefix_error=False,
316 prefix_error=False,
320 encoding="UTF-8",
317 encoding="UTF-8",
321 force_defaults=False
318 force_defaults=False
322 )
319 )
323
320
324 try:
321 try:
325 settings = [
322 settings = [
326 ('show_public_icon', 'rhodecode_show_public_icon', 'bool'),
323 ('show_public_icon', 'rhodecode_show_public_icon', 'bool'),
327 ('show_private_icon', 'rhodecode_show_private_icon', 'bool'),
324 ('show_private_icon', 'rhodecode_show_private_icon', 'bool'),
328 ('stylify_metatags', 'rhodecode_stylify_metatags', 'bool'),
325 ('stylify_metatags', 'rhodecode_stylify_metatags', 'bool'),
329 ('repository_fields', 'rhodecode_repository_fields', 'bool'),
326 ('repository_fields', 'rhodecode_repository_fields', 'bool'),
330 ('dashboard_items', 'rhodecode_dashboard_items', 'int'),
327 ('dashboard_items', 'rhodecode_dashboard_items', 'int'),
331 ('admin_grid_items', 'rhodecode_admin_grid_items', 'int'),
328 ('admin_grid_items', 'rhodecode_admin_grid_items', 'int'),
332 ('show_version', 'rhodecode_show_version', 'bool'),
329 ('show_version', 'rhodecode_show_version', 'bool'),
333 ('use_gravatar', 'rhodecode_use_gravatar', 'bool'),
330 ('use_gravatar', 'rhodecode_use_gravatar', 'bool'),
334 ('markup_renderer', 'rhodecode_markup_renderer', 'unicode'),
331 ('markup_renderer', 'rhodecode_markup_renderer', 'unicode'),
335 ('gravatar_url', 'rhodecode_gravatar_url', 'unicode'),
332 ('gravatar_url', 'rhodecode_gravatar_url', 'unicode'),
336 ('clone_uri_tmpl', 'rhodecode_clone_uri_tmpl', 'unicode'),
333 ('clone_uri_tmpl', 'rhodecode_clone_uri_tmpl', 'unicode'),
337 ('support_url', 'rhodecode_support_url', 'unicode'),
334 ('support_url', 'rhodecode_support_url', 'unicode'),
338 ('show_revision_number', 'rhodecode_show_revision_number', 'bool'),
335 ('show_revision_number', 'rhodecode_show_revision_number', 'bool'),
339 ('show_sha_length', 'rhodecode_show_sha_length', 'int'),
336 ('show_sha_length', 'rhodecode_show_sha_length', 'int'),
340 ]
337 ]
341 for setting, form_key, type_ in settings:
338 for setting, form_key, type_ in settings:
342 sett = SettingsModel().create_or_update_setting(
339 sett = SettingsModel().create_or_update_setting(
343 setting, form_result[form_key], type_)
340 setting, form_result[form_key], type_)
344 Session().add(sett)
341 Session().add(sett)
345
342
346 Session().commit()
343 Session().commit()
347 SettingsModel().invalidate_settings_cache()
344 SettingsModel().invalidate_settings_cache()
348 h.flash(_('Updated visualisation settings'), category='success')
345 h.flash(_('Updated visualisation settings'), category='success')
349 except Exception:
346 except Exception:
350 log.exception("Exception updating visualization settings")
347 log.exception("Exception updating visualization settings")
351 h.flash(_('Error occurred during updating '
348 h.flash(_('Error occurred during updating '
352 'visualisation settings'),
349 'visualisation settings'),
353 category='error')
350 category='error')
354
351
355 return redirect(url('admin_settings_visual'))
352 return redirect(url('admin_settings_visual'))
356
353
357 @HasPermissionAllDecorator('hg.admin')
354 @HasPermissionAllDecorator('hg.admin')
358 def settings_visual(self):
355 def settings_visual(self):
359 """GET /admin/settings/visual: All items in the collection"""
356 """GET /admin/settings/visual: All items in the collection"""
360 # url('admin_settings_visual')
357 # url('admin_settings_visual')
361 c.active = 'visual'
358 c.active = 'visual'
362
359
363 return htmlfill.render(
360 return htmlfill.render(
364 render('admin/settings/settings.mako'),
361 render('admin/settings/settings.mako'),
365 defaults=self._form_defaults(),
362 defaults=self._form_defaults(),
366 encoding="UTF-8",
363 encoding="UTF-8",
367 force_defaults=False)
364 force_defaults=False)
368
365
369 @HasPermissionAllDecorator('hg.admin')
366 @HasPermissionAllDecorator('hg.admin')
370 @auth.CSRFRequired()
367 @auth.CSRFRequired()
371 def settings_issuetracker_test(self):
368 def settings_issuetracker_test(self):
372 if request.is_xhr:
369 if request.is_xhr:
373 return h.urlify_commit_message(
370 return h.urlify_commit_message(
374 request.POST.get('test_text', ''),
371 request.POST.get('test_text', ''),
375 'repo_group/test_repo1')
372 'repo_group/test_repo1')
376 else:
373 else:
377 raise HTTPBadRequest()
374 raise HTTPBadRequest()
378
375
379 @HasPermissionAllDecorator('hg.admin')
376 @HasPermissionAllDecorator('hg.admin')
380 @auth.CSRFRequired()
377 @auth.CSRFRequired()
381 def settings_issuetracker_delete(self):
378 def settings_issuetracker_delete(self):
382 uid = request.POST.get('uid')
379 uid = request.POST.get('uid')
383 IssueTrackerSettingsModel().delete_entries(uid)
380 IssueTrackerSettingsModel().delete_entries(uid)
384 h.flash(_('Removed issue tracker entry'), category='success')
381 h.flash(_('Removed issue tracker entry'), category='success')
385 return redirect(url('admin_settings_issuetracker'))
382 return redirect(url('admin_settings_issuetracker'))
386
383
387 @HasPermissionAllDecorator('hg.admin')
384 @HasPermissionAllDecorator('hg.admin')
388 def settings_issuetracker(self):
385 def settings_issuetracker(self):
389 """GET /admin/settings/issue-tracker: All items in the collection"""
386 """GET /admin/settings/issue-tracker: All items in the collection"""
390 # url('admin_settings_issuetracker')
387 # url('admin_settings_issuetracker')
391 c.active = 'issuetracker'
388 c.active = 'issuetracker'
392 defaults = SettingsModel().get_all_settings()
389 defaults = SettingsModel().get_all_settings()
393
390
394 entry_key = 'rhodecode_issuetracker_pat_'
391 entry_key = 'rhodecode_issuetracker_pat_'
395
392
396 c.issuetracker_entries = {}
393 c.issuetracker_entries = {}
397 for k, v in defaults.items():
394 for k, v in defaults.items():
398 if k.startswith(entry_key):
395 if k.startswith(entry_key):
399 uid = k[len(entry_key):]
396 uid = k[len(entry_key):]
400 c.issuetracker_entries[uid] = None
397 c.issuetracker_entries[uid] = None
401
398
402 for uid in c.issuetracker_entries:
399 for uid in c.issuetracker_entries:
403 c.issuetracker_entries[uid] = AttributeDict({
400 c.issuetracker_entries[uid] = AttributeDict({
404 'pat': defaults.get('rhodecode_issuetracker_pat_' + uid),
401 'pat': defaults.get('rhodecode_issuetracker_pat_' + uid),
405 'url': defaults.get('rhodecode_issuetracker_url_' + uid),
402 'url': defaults.get('rhodecode_issuetracker_url_' + uid),
406 'pref': defaults.get('rhodecode_issuetracker_pref_' + uid),
403 'pref': defaults.get('rhodecode_issuetracker_pref_' + uid),
407 'desc': defaults.get('rhodecode_issuetracker_desc_' + uid),
404 'desc': defaults.get('rhodecode_issuetracker_desc_' + uid),
408 })
405 })
409
406
410 return render('admin/settings/settings.mako')
407 return render('admin/settings/settings.mako')
411
408
412 @HasPermissionAllDecorator('hg.admin')
409 @HasPermissionAllDecorator('hg.admin')
413 @auth.CSRFRequired()
410 @auth.CSRFRequired()
414 def settings_issuetracker_save(self):
411 def settings_issuetracker_save(self):
415 settings_model = IssueTrackerSettingsModel()
412 settings_model = IssueTrackerSettingsModel()
416
413
417 form = IssueTrackerPatternsForm()().to_python(request.POST)
414 form = IssueTrackerPatternsForm()().to_python(request.POST)
418 if form:
415 if form:
419 for uid in form.get('delete_patterns', []):
416 for uid in form.get('delete_patterns', []):
420 settings_model.delete_entries(uid)
417 settings_model.delete_entries(uid)
421
418
422 for pattern in form.get('patterns', []):
419 for pattern in form.get('patterns', []):
423 for setting, value, type_ in pattern:
420 for setting, value, type_ in pattern:
424 sett = settings_model.create_or_update_setting(
421 sett = settings_model.create_or_update_setting(
425 setting, value, type_)
422 setting, value, type_)
426 Session().add(sett)
423 Session().add(sett)
427
424
428 Session().commit()
425 Session().commit()
429
426
430 SettingsModel().invalidate_settings_cache()
427 SettingsModel().invalidate_settings_cache()
431 h.flash(_('Updated issue tracker entries'), category='success')
428 h.flash(_('Updated issue tracker entries'), category='success')
432 return redirect(url('admin_settings_issuetracker'))
429 return redirect(url('admin_settings_issuetracker'))
433
430
434 @HasPermissionAllDecorator('hg.admin')
431 @HasPermissionAllDecorator('hg.admin')
435 @auth.CSRFRequired()
432 @auth.CSRFRequired()
436 def settings_email_update(self):
433 def settings_email_update(self):
437 """POST /admin/settings/email: All items in the collection"""
434 """POST /admin/settings/email: All items in the collection"""
438 # url('admin_settings_email')
435 # url('admin_settings_email')
439 c.active = 'email'
436 c.active = 'email'
440
437
441 test_email = request.POST.get('test_email')
438 test_email = request.POST.get('test_email')
442
439
443 if not test_email:
440 if not test_email:
444 h.flash(_('Please enter email address'), category='error')
441 h.flash(_('Please enter email address'), category='error')
445 return redirect(url('admin_settings_email'))
442 return redirect(url('admin_settings_email'))
446
443
447 email_kwargs = {
444 email_kwargs = {
448 'date': datetime.datetime.now(),
445 'date': datetime.datetime.now(),
449 'user': c.rhodecode_user,
446 'user': c.rhodecode_user,
450 'rhodecode_version': c.rhodecode_version
447 'rhodecode_version': c.rhodecode_version
451 }
448 }
452
449
453 (subject, headers, email_body,
450 (subject, headers, email_body,
454 email_body_plaintext) = EmailNotificationModel().render_email(
451 email_body_plaintext) = EmailNotificationModel().render_email(
455 EmailNotificationModel.TYPE_EMAIL_TEST, **email_kwargs)
452 EmailNotificationModel.TYPE_EMAIL_TEST, **email_kwargs)
456
453
457 recipients = [test_email] if test_email else None
454 recipients = [test_email] if test_email else None
458
455
459 run_task(tasks.send_email, recipients, subject,
456 run_task(tasks.send_email, recipients, subject,
460 email_body_plaintext, email_body)
457 email_body_plaintext, email_body)
461
458
462 h.flash(_('Send email task created'), category='success')
459 h.flash(_('Send email task created'), category='success')
463 return redirect(url('admin_settings_email'))
460 return redirect(url('admin_settings_email'))
464
461
465 @HasPermissionAllDecorator('hg.admin')
462 @HasPermissionAllDecorator('hg.admin')
466 def settings_email(self):
463 def settings_email(self):
467 """GET /admin/settings/email: All items in the collection"""
464 """GET /admin/settings/email: All items in the collection"""
468 # url('admin_settings_email')
465 # url('admin_settings_email')
469 c.active = 'email'
466 c.active = 'email'
470 c.rhodecode_ini = rhodecode.CONFIG
467 c.rhodecode_ini = rhodecode.CONFIG
471
468
472 return htmlfill.render(
469 return htmlfill.render(
473 render('admin/settings/settings.mako'),
470 render('admin/settings/settings.mako'),
474 defaults=self._form_defaults(),
471 defaults=self._form_defaults(),
475 encoding="UTF-8",
472 encoding="UTF-8",
476 force_defaults=False)
473 force_defaults=False)
477
474
478 @HasPermissionAllDecorator('hg.admin')
475 @HasPermissionAllDecorator('hg.admin')
479 @auth.CSRFRequired()
476 @auth.CSRFRequired()
480 def settings_hooks_update(self):
477 def settings_hooks_update(self):
481 """POST or DELETE /admin/settings/hooks: All items in the collection"""
478 """POST or DELETE /admin/settings/hooks: All items in the collection"""
482 # url('admin_settings_hooks')
479 # url('admin_settings_hooks')
483 c.active = 'hooks'
480 c.active = 'hooks'
484 if c.visual.allow_custom_hooks_settings:
481 if c.visual.allow_custom_hooks_settings:
485 ui_key = request.POST.get('new_hook_ui_key')
482 ui_key = request.POST.get('new_hook_ui_key')
486 ui_value = request.POST.get('new_hook_ui_value')
483 ui_value = request.POST.get('new_hook_ui_value')
487
484
488 hook_id = request.POST.get('hook_id')
485 hook_id = request.POST.get('hook_id')
489 new_hook = False
486 new_hook = False
490
487
491 model = SettingsModel()
488 model = SettingsModel()
492 try:
489 try:
493 if ui_value and ui_key:
490 if ui_value and ui_key:
494 model.create_or_update_hook(ui_key, ui_value)
491 model.create_or_update_hook(ui_key, ui_value)
495 h.flash(_('Added new hook'), category='success')
492 h.flash(_('Added new hook'), category='success')
496 new_hook = True
493 new_hook = True
497 elif hook_id:
494 elif hook_id:
498 RhodeCodeUi.delete(hook_id)
495 RhodeCodeUi.delete(hook_id)
499 Session().commit()
496 Session().commit()
500
497
501 # check for edits
498 # check for edits
502 update = False
499 update = False
503 _d = request.POST.dict_of_lists()
500 _d = request.POST.dict_of_lists()
504 for k, v in zip(_d.get('hook_ui_key', []),
501 for k, v in zip(_d.get('hook_ui_key', []),
505 _d.get('hook_ui_value_new', [])):
502 _d.get('hook_ui_value_new', [])):
506 model.create_or_update_hook(k, v)
503 model.create_or_update_hook(k, v)
507 update = True
504 update = True
508
505
509 if update and not new_hook:
506 if update and not new_hook:
510 h.flash(_('Updated hooks'), category='success')
507 h.flash(_('Updated hooks'), category='success')
511 Session().commit()
508 Session().commit()
512 except Exception:
509 except Exception:
513 log.exception("Exception during hook creation")
510 log.exception("Exception during hook creation")
514 h.flash(_('Error occurred during hook creation'),
511 h.flash(_('Error occurred during hook creation'),
515 category='error')
512 category='error')
516
513
517 return redirect(url('admin_settings_hooks'))
514 return redirect(url('admin_settings_hooks'))
518
515
519 @HasPermissionAllDecorator('hg.admin')
516 @HasPermissionAllDecorator('hg.admin')
520 def settings_hooks(self):
517 def settings_hooks(self):
521 """GET /admin/settings/hooks: All items in the collection"""
518 """GET /admin/settings/hooks: All items in the collection"""
522 # url('admin_settings_hooks')
519 # url('admin_settings_hooks')
523 c.active = 'hooks'
520 c.active = 'hooks'
524
521
525 model = SettingsModel()
522 model = SettingsModel()
526 c.hooks = model.get_builtin_hooks()
523 c.hooks = model.get_builtin_hooks()
527 c.custom_hooks = model.get_custom_hooks()
524 c.custom_hooks = model.get_custom_hooks()
528
525
529 return htmlfill.render(
526 return htmlfill.render(
530 render('admin/settings/settings.mako'),
527 render('admin/settings/settings.mako'),
531 defaults=self._form_defaults(),
528 defaults=self._form_defaults(),
532 encoding="UTF-8",
529 encoding="UTF-8",
533 force_defaults=False)
530 force_defaults=False)
534
531
535 @HasPermissionAllDecorator('hg.admin')
532 @HasPermissionAllDecorator('hg.admin')
536 def settings_search(self):
533 def settings_search(self):
537 """GET /admin/settings/search: All items in the collection"""
534 """GET /admin/settings/search: All items in the collection"""
538 # url('admin_settings_search')
535 # url('admin_settings_search')
539 c.active = 'search'
536 c.active = 'search'
540
537
541 from rhodecode.lib.index import searcher_from_config
538 from rhodecode.lib.index import searcher_from_config
542 searcher = searcher_from_config(config)
539 searcher = searcher_from_config(config)
543 c.statistics = searcher.statistics()
540 c.statistics = searcher.statistics()
544
541
545 return render('admin/settings/settings.mako')
542 return render('admin/settings/settings.mako')
546
543
547 @HasPermissionAllDecorator('hg.admin')
544 @HasPermissionAllDecorator('hg.admin')
548 def settings_supervisor(self):
549 c.rhodecode_ini = rhodecode.CONFIG
550 c.active = 'supervisor'
551
552 c.supervisor_procs = OrderedDict([
553 (SUPERVISOR_MASTER, {}),
554 ])
555
556 c.log_size = 10240
557 supervisor = SupervisorModel()
558
559 _connection = supervisor.get_connection(
560 c.rhodecode_ini.get('supervisor.uri'))
561 c.connection_error = None
562 try:
563 _connection.supervisor.getAllProcessInfo()
564 except Exception as e:
565 c.connection_error = str(e)
566 log.exception("Exception reading supervisor data")
567 return render('admin/settings/settings.mako')
568
569 groupid = c.rhodecode_ini.get('supervisor.group_id')
570
571 # feed our group processes to the main
572 for proc in supervisor.get_group_processes(_connection, groupid):
573 c.supervisor_procs[proc['name']] = {}
574
575 for k in c.supervisor_procs.keys():
576 try:
577 # master process info
578 if k == SUPERVISOR_MASTER:
579 _data = supervisor.get_master_state(_connection)
580 _data['name'] = 'supervisor master'
581 _data['description'] = 'pid %s, id: %s, ver: %s' % (
582 _data['pid'], _data['id'], _data['ver'])
583 c.supervisor_procs[k] = _data
584 else:
585 procid = groupid + ":" + k
586 c.supervisor_procs[k] = supervisor.get_process_info(_connection, procid)
587 except Exception as e:
588 log.exception("Exception reading supervisor data")
589 c.supervisor_procs[k] = {'_rhodecode_error': str(e)}
590
591 return render('admin/settings/settings.mako')
592
593 @HasPermissionAllDecorator('hg.admin')
594 def settings_supervisor_log(self, procid):
595 import rhodecode
596 c.rhodecode_ini = rhodecode.CONFIG
597 c.active = 'supervisor_tail'
598
599 supervisor = SupervisorModel()
600 _connection = supervisor.get_connection(c.rhodecode_ini.get('supervisor.uri'))
601 groupid = c.rhodecode_ini.get('supervisor.group_id')
602 procid = groupid + ":" + procid if procid != SUPERVISOR_MASTER else procid
603
604 c.log_size = 10240
605 offset = abs(safe_int(request.GET.get('offset', c.log_size))) * -1
606 c.log = supervisor.read_process_log(_connection, procid, offset, 0)
607
608 return render('admin/settings/settings.mako')
609
610 @HasPermissionAllDecorator('hg.admin')
611 @auth.CSRFRequired()
545 @auth.CSRFRequired()
612 def settings_labs_update(self):
546 def settings_labs_update(self):
613 """POST /admin/settings/labs: All items in the collection"""
547 """POST /admin/settings/labs: All items in the collection"""
614 # url('admin_settings/labs', method={'POST'})
548 # url('admin_settings/labs', method={'POST'})
615 c.active = 'labs'
549 c.active = 'labs'
616
550
617 application_form = LabsSettingsForm()()
551 application_form = LabsSettingsForm()()
618 try:
552 try:
619 form_result = application_form.to_python(dict(request.POST))
553 form_result = application_form.to_python(dict(request.POST))
620 except formencode.Invalid as errors:
554 except formencode.Invalid as errors:
621 h.flash(
555 h.flash(
622 _('Some form inputs contain invalid data.'),
556 _('Some form inputs contain invalid data.'),
623 category='error')
557 category='error')
624 return htmlfill.render(
558 return htmlfill.render(
625 render('admin/settings/settings.mako'),
559 render('admin/settings/settings.mako'),
626 defaults=errors.value,
560 defaults=errors.value,
627 errors=errors.error_dict or {},
561 errors=errors.error_dict or {},
628 prefix_error=False,
562 prefix_error=False,
629 encoding='UTF-8',
563 encoding='UTF-8',
630 force_defaults=False
564 force_defaults=False
631 )
565 )
632
566
633 try:
567 try:
634 session = Session()
568 session = Session()
635 for setting in _LAB_SETTINGS:
569 for setting in _LAB_SETTINGS:
636 setting_name = setting.key[len('rhodecode_'):]
570 setting_name = setting.key[len('rhodecode_'):]
637 sett = SettingsModel().create_or_update_setting(
571 sett = SettingsModel().create_or_update_setting(
638 setting_name, form_result[setting.key], setting.type)
572 setting_name, form_result[setting.key], setting.type)
639 session.add(sett)
573 session.add(sett)
640
574
641 except Exception:
575 except Exception:
642 log.exception('Exception while updating lab settings')
576 log.exception('Exception while updating lab settings')
643 h.flash(_('Error occurred during updating labs settings'),
577 h.flash(_('Error occurred during updating labs settings'),
644 category='error')
578 category='error')
645 else:
579 else:
646 Session().commit()
580 Session().commit()
647 SettingsModel().invalidate_settings_cache()
581 SettingsModel().invalidate_settings_cache()
648 h.flash(_('Updated Labs settings'), category='success')
582 h.flash(_('Updated Labs settings'), category='success')
649 return redirect(url('admin_settings_labs'))
583 return redirect(url('admin_settings_labs'))
650
584
651 return htmlfill.render(
585 return htmlfill.render(
652 render('admin/settings/settings.mako'),
586 render('admin/settings/settings.mako'),
653 defaults=self._form_defaults(),
587 defaults=self._form_defaults(),
654 encoding='UTF-8',
588 encoding='UTF-8',
655 force_defaults=False)
589 force_defaults=False)
656
590
657 @HasPermissionAllDecorator('hg.admin')
591 @HasPermissionAllDecorator('hg.admin')
658 def settings_labs(self):
592 def settings_labs(self):
659 """GET /admin/settings/labs: All items in the collection"""
593 """GET /admin/settings/labs: All items in the collection"""
660 # url('admin_settings_labs')
594 # url('admin_settings_labs')
661 if not c.labs_active:
595 if not c.labs_active:
662 redirect(url('admin_settings'))
596 redirect(url('admin_settings'))
663
597
664 c.active = 'labs'
598 c.active = 'labs'
665 c.lab_settings = _LAB_SETTINGS
599 c.lab_settings = _LAB_SETTINGS
666
600
667 return htmlfill.render(
601 return htmlfill.render(
668 render('admin/settings/settings.mako'),
602 render('admin/settings/settings.mako'),
669 defaults=self._form_defaults(),
603 defaults=self._form_defaults(),
670 encoding='UTF-8',
604 encoding='UTF-8',
671 force_defaults=False)
605 force_defaults=False)
672
606
673 def _form_defaults(self):
607 def _form_defaults(self):
674 defaults = SettingsModel().get_all_settings()
608 defaults = SettingsModel().get_all_settings()
675 defaults.update(self._get_ui_settings())
609 defaults.update(self._get_ui_settings())
676
610
677 defaults.update({
611 defaults.update({
678 'new_svn_branch': '',
612 'new_svn_branch': '',
679 'new_svn_tag': '',
613 'new_svn_tag': '',
680 })
614 })
681 return defaults
615 return defaults
682
616
683
617
684 # :param key: name of the setting including the 'rhodecode_' prefix
618 # :param key: name of the setting including the 'rhodecode_' prefix
685 # :param type: the RhodeCodeSetting type to use.
619 # :param type: the RhodeCodeSetting type to use.
686 # :param group: the i18ned group in which we should dispaly this setting
620 # :param group: the i18ned group in which we should dispaly this setting
687 # :param label: the i18ned label we should display for this setting
621 # :param label: the i18ned label we should display for this setting
688 # :param help: the i18ned help we should dispaly for this setting
622 # :param help: the i18ned help we should dispaly for this setting
689 LabSetting = collections.namedtuple(
623 LabSetting = collections.namedtuple(
690 'LabSetting', ('key', 'type', 'group', 'label', 'help'))
624 'LabSetting', ('key', 'type', 'group', 'label', 'help'))
691
625
692
626
693 # This list has to be kept in sync with the form
627 # This list has to be kept in sync with the form
694 # rhodecode.model.forms.LabsSettingsForm.
628 # rhodecode.model.forms.LabsSettingsForm.
695 _LAB_SETTINGS = [
629 _LAB_SETTINGS = [
696
630
697 ]
631 ]
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now