##// END OF EJS Templates
oss-licenses: Migrate view to pyramid.
Martin Bornhold -
r204:9d966d61 default
parent child Browse files
Show More
@@ -0,0 +1,32 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-2016 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21
22 from rhodecode.config.routing import ADMIN_PREFIX
23
24
25 def includeme(config):
26
27 config.add_route(
28 name='admin_settings_open_source',
29 pattern=ADMIN_PREFIX + '/settings/open_source')
30
31 # Scan module for configuration decorators.
32 config.scan()
@@ -0,0 +1,54 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-2016 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import collections
22 import logging
23
24 from pylons import tmpl_context as c
25 from pyramid.view import view_config
26
27 from rhodecode.controllers.admin.settings import navigation
28 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
29 from rhodecode.lib.utils import read_opensource_licenses
30
31
32 log = logging.getLogger(__name__)
33
34
35 class AdminSettingsView(object):
36
37 def __init__(self, context, request):
38 self.request = request
39 self.context = context
40 self.session = request.session
41 self._rhodecode_user = request.user
42
43 @LoginRequired()
44 @HasPermissionAllDecorator('hg.admin')
45 @view_config(
46 route_name='admin_settings_open_source', request_method='GET',
47 renderer='rhodecode:templates/admin/settings/settings.html')
48 def open_source_licenses(self):
49 c.active = 'open_source'
50 c.navlist = navigation.get_navlist(self.request)
51 c.opensource_licenses = collections.OrderedDict(
52 sorted(read_opensource_licenses().items(), key=lambda t: t[0]))
53
54 return {}
@@ -1,385 +1,386 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 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
25
26 from paste.registry import RegistryManager
26 from paste.registry import RegistryManager
27 from paste.gzipper import make_gzip_middleware
27 from paste.gzipper import make_gzip_middleware
28 from pylons.wsgiapp import PylonsApp
28 from pylons.wsgiapp import PylonsApp
29 from pyramid.authorization import ACLAuthorizationPolicy
29 from pyramid.authorization import ACLAuthorizationPolicy
30 from pyramid.config import Configurator
30 from pyramid.config import Configurator
31 from pyramid.static import static_view
31 from pyramid.static import static_view
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 HTTPError, HTTPInternalServerError
34 from pyramid.httpexceptions import HTTPError, HTTPInternalServerError
35 import pyramid.httpexceptions as httpexceptions
35 import pyramid.httpexceptions as httpexceptions
36 from pyramid.renderers import render_to_response, render
36 from pyramid.renderers import render_to_response, render
37 from routes.middleware import RoutesMiddleware
37 from routes.middleware import RoutesMiddleware
38 import routes.util
38 import routes.util
39
39
40 import rhodecode
40 import rhodecode
41 from rhodecode.config import patches
41 from rhodecode.config import patches
42 from rhodecode.config.environment import (
42 from rhodecode.config.environment import (
43 load_environment, load_pyramid_environment)
43 load_environment, load_pyramid_environment)
44 from rhodecode.lib.middleware import csrf
44 from rhodecode.lib.middleware import csrf
45 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
45 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
46 from rhodecode.lib.middleware.disable_vcs import DisableVCSPagesWrapper
46 from rhodecode.lib.middleware.disable_vcs import DisableVCSPagesWrapper
47 from rhodecode.lib.middleware.https_fixup import HttpsFixup
47 from rhodecode.lib.middleware.https_fixup import HttpsFixup
48 from rhodecode.lib.middleware.vcs import VCSMiddleware
48 from rhodecode.lib.middleware.vcs import VCSMiddleware
49 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
49 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
50
50
51
51
52 log = logging.getLogger(__name__)
52 log = logging.getLogger(__name__)
53
53
54
54
55 def make_app(global_conf, full_stack=True, static_files=True, **app_conf):
55 def make_app(global_conf, full_stack=True, static_files=True, **app_conf):
56 """Create a Pylons WSGI application and return it
56 """Create a Pylons WSGI application and return it
57
57
58 ``global_conf``
58 ``global_conf``
59 The inherited configuration for this application. Normally from
59 The inherited configuration for this application. Normally from
60 the [DEFAULT] section of the Paste ini file.
60 the [DEFAULT] section of the Paste ini file.
61
61
62 ``full_stack``
62 ``full_stack``
63 Whether or not this application provides a full WSGI stack (by
63 Whether or not this application provides a full WSGI stack (by
64 default, meaning it handles its own exceptions and errors).
64 default, meaning it handles its own exceptions and errors).
65 Disable full_stack when this application is "managed" by
65 Disable full_stack when this application is "managed" by
66 another WSGI middleware.
66 another WSGI middleware.
67
67
68 ``app_conf``
68 ``app_conf``
69 The application's local configuration. Normally specified in
69 The application's local configuration. Normally specified in
70 the [app:<name>] section of the Paste ini file (where <name>
70 the [app:<name>] section of the Paste ini file (where <name>
71 defaults to main).
71 defaults to main).
72
72
73 """
73 """
74 # Apply compatibility patches
74 # Apply compatibility patches
75 patches.kombu_1_5_1_python_2_7_11()
75 patches.kombu_1_5_1_python_2_7_11()
76 patches.inspect_getargspec()
76 patches.inspect_getargspec()
77
77
78 # Configure the Pylons environment
78 # Configure the Pylons environment
79 config = load_environment(global_conf, app_conf)
79 config = load_environment(global_conf, app_conf)
80
80
81 # The Pylons WSGI app
81 # The Pylons WSGI app
82 app = PylonsApp(config=config)
82 app = PylonsApp(config=config)
83 if rhodecode.is_test:
83 if rhodecode.is_test:
84 app = csrf.CSRFDetector(app)
84 app = csrf.CSRFDetector(app)
85
85
86 expected_origin = config.get('expected_origin')
86 expected_origin = config.get('expected_origin')
87 if expected_origin:
87 if expected_origin:
88 # The API can be accessed from other Origins.
88 # The API can be accessed from other Origins.
89 app = csrf.OriginChecker(app, expected_origin,
89 app = csrf.OriginChecker(app, expected_origin,
90 skip_urls=[routes.util.url_for('api')])
90 skip_urls=[routes.util.url_for('api')])
91
91
92
92
93 if asbool(full_stack):
93 if asbool(full_stack):
94
94
95 # Appenlight monitoring and error handler
95 # Appenlight monitoring and error handler
96 app, appenlight_client = wrap_in_appenlight_if_enabled(app, config)
96 app, appenlight_client = wrap_in_appenlight_if_enabled(app, config)
97
97
98 # we want our low level middleware to get to the request ASAP. We don't
98 # we want our low level middleware to get to the request ASAP. We don't
99 # need any pylons stack middleware in them
99 # need any pylons stack middleware in them
100 app = VCSMiddleware(app, config, appenlight_client)
100 app = VCSMiddleware(app, config, appenlight_client)
101
101
102 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
102 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
103 app = HttpsFixup(app, config)
103 app = HttpsFixup(app, config)
104
104
105 # Establish the Registry for this application
105 # Establish the Registry for this application
106 app = RegistryManager(app)
106 app = RegistryManager(app)
107
107
108 app.config = config
108 app.config = config
109
109
110 return app
110 return app
111
111
112
112
113 def make_pyramid_app(global_config, **settings):
113 def make_pyramid_app(global_config, **settings):
114 """
114 """
115 Constructs the WSGI application based on Pyramid and wraps the Pylons based
115 Constructs the WSGI application based on Pyramid and wraps the Pylons based
116 application.
116 application.
117
117
118 Specials:
118 Specials:
119
119
120 * We migrate from Pylons to Pyramid. While doing this, we keep both
120 * We migrate from Pylons to Pyramid. While doing this, we keep both
121 frameworks functional. This involves moving some WSGI middlewares around
121 frameworks functional. This involves moving some WSGI middlewares around
122 and providing access to some data internals, so that the old code is
122 and providing access to some data internals, so that the old code is
123 still functional.
123 still functional.
124
124
125 * The application can also be integrated like a plugin via the call to
125 * The application can also be integrated like a plugin via the call to
126 `includeme`. This is accompanied with the other utility functions which
126 `includeme`. This is accompanied with the other utility functions which
127 are called. Changing this should be done with great care to not break
127 are called. Changing this should be done with great care to not break
128 cases when these fragments are assembled from another place.
128 cases when these fragments are assembled from another place.
129
129
130 """
130 """
131 # The edition string should be available in pylons too, so we add it here
131 # The edition string should be available in pylons too, so we add it here
132 # before copying the settings.
132 # before copying the settings.
133 settings.setdefault('rhodecode.edition', 'Community Edition')
133 settings.setdefault('rhodecode.edition', 'Community Edition')
134
134
135 # As long as our Pylons application does expect "unprepared" settings, make
135 # As long as our Pylons application does expect "unprepared" settings, make
136 # sure that we keep an unmodified copy. This avoids unintentional change of
136 # sure that we keep an unmodified copy. This avoids unintentional change of
137 # behavior in the old application.
137 # behavior in the old application.
138 settings_pylons = settings.copy()
138 settings_pylons = settings.copy()
139
139
140 sanitize_settings_and_apply_defaults(settings)
140 sanitize_settings_and_apply_defaults(settings)
141 config = Configurator(settings=settings)
141 config = Configurator(settings=settings)
142 add_pylons_compat_data(config.registry, global_config, settings_pylons)
142 add_pylons_compat_data(config.registry, global_config, settings_pylons)
143
143
144 load_pyramid_environment(global_config, settings)
144 load_pyramid_environment(global_config, settings)
145
145
146 includeme(config)
146 includeme(config)
147 includeme_last(config)
147 includeme_last(config)
148 pyramid_app = config.make_wsgi_app()
148 pyramid_app = config.make_wsgi_app()
149 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
149 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
150 return pyramid_app
150 return pyramid_app
151
151
152
152
153 def add_pylons_compat_data(registry, global_config, settings):
153 def add_pylons_compat_data(registry, global_config, settings):
154 """
154 """
155 Attach data to the registry to support the Pylons integration.
155 Attach data to the registry to support the Pylons integration.
156 """
156 """
157 registry._pylons_compat_global_config = global_config
157 registry._pylons_compat_global_config = global_config
158 registry._pylons_compat_settings = settings
158 registry._pylons_compat_settings = settings
159
159
160
160
161 def webob_to_pyramid_http_response(webob_response):
161 def webob_to_pyramid_http_response(webob_response):
162 ResponseClass = httpexceptions.status_map[webob_response.status_int]
162 ResponseClass = httpexceptions.status_map[webob_response.status_int]
163 pyramid_response = ResponseClass(webob_response.status)
163 pyramid_response = ResponseClass(webob_response.status)
164 pyramid_response.status = webob_response.status
164 pyramid_response.status = webob_response.status
165 pyramid_response.headers.update(webob_response.headers)
165 pyramid_response.headers.update(webob_response.headers)
166 if pyramid_response.headers['content-type'] == 'text/html':
166 if pyramid_response.headers['content-type'] == 'text/html':
167 pyramid_response.headers['content-type'] = 'text/html; charset=UTF-8'
167 pyramid_response.headers['content-type'] = 'text/html; charset=UTF-8'
168 return pyramid_response
168 return pyramid_response
169
169
170
170
171 def error_handler(exception, request):
171 def error_handler(exception, request):
172 # TODO: dan: replace the old pylons error controller with this
172 # TODO: dan: replace the old pylons error controller with this
173 from rhodecode.model.settings import SettingsModel
173 from rhodecode.model.settings import SettingsModel
174 from rhodecode.lib.utils2 import AttributeDict
174 from rhodecode.lib.utils2 import AttributeDict
175
175
176 try:
176 try:
177 rc_config = SettingsModel().get_all_settings()
177 rc_config = SettingsModel().get_all_settings()
178 except Exception:
178 except Exception:
179 log.exception('failed to fetch settings')
179 log.exception('failed to fetch settings')
180 rc_config = {}
180 rc_config = {}
181
181
182 base_response = HTTPInternalServerError()
182 base_response = HTTPInternalServerError()
183 # prefer original exception for the response since it may have headers set
183 # prefer original exception for the response since it may have headers set
184 if isinstance(exception, HTTPError):
184 if isinstance(exception, HTTPError):
185 base_response = exception
185 base_response = exception
186
186
187 c = AttributeDict()
187 c = AttributeDict()
188 c.error_message = base_response.status
188 c.error_message = base_response.status
189 c.error_explanation = base_response.explanation or str(base_response)
189 c.error_explanation = base_response.explanation or str(base_response)
190 c.visual = AttributeDict()
190 c.visual = AttributeDict()
191
191
192 c.visual.rhodecode_support_url = (
192 c.visual.rhodecode_support_url = (
193 request.registry.settings.get('rhodecode_support_url') or
193 request.registry.settings.get('rhodecode_support_url') or
194 request.route_url('rhodecode_support')
194 request.route_url('rhodecode_support')
195 )
195 )
196 c.redirect_time = 0
196 c.redirect_time = 0
197 c.rhodecode_name = rc_config.get('rhodecode_title', '')
197 c.rhodecode_name = rc_config.get('rhodecode_title', '')
198 if not c.rhodecode_name:
198 if not c.rhodecode_name:
199 c.rhodecode_name = 'Rhodecode'
199 c.rhodecode_name = 'Rhodecode'
200
200
201 response = render_to_response(
201 response = render_to_response(
202 '/errors/error_document.html', {'c': c}, request=request,
202 '/errors/error_document.html', {'c': c}, request=request,
203 response=base_response)
203 response=base_response)
204
204
205 return response
205 return response
206
206
207
207
208 def includeme(config):
208 def includeme(config):
209 settings = config.registry.settings
209 settings = config.registry.settings
210
210
211 if asbool(settings.get('appenlight', 'false')):
211 if asbool(settings.get('appenlight', 'false')):
212 config.include('appenlight_client.ext.pyramid_tween')
212 config.include('appenlight_client.ext.pyramid_tween')
213
213
214 # Includes which are required. The application would fail without them.
214 # Includes which are required. The application would fail without them.
215 config.include('pyramid_mako')
215 config.include('pyramid_mako')
216 config.include('pyramid_beaker')
216 config.include('pyramid_beaker')
217 config.include('rhodecode.admin')
217 config.include('rhodecode.authentication')
218 config.include('rhodecode.authentication')
218 config.include('rhodecode.login')
219 config.include('rhodecode.login')
219 config.include('rhodecode.tweens')
220 config.include('rhodecode.tweens')
220 config.include('rhodecode.api')
221 config.include('rhodecode.api')
221 config.add_route(
222 config.add_route(
222 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
223 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
223
224
224 # Set the authorization policy.
225 # Set the authorization policy.
225 authz_policy = ACLAuthorizationPolicy()
226 authz_policy = ACLAuthorizationPolicy()
226 config.set_authorization_policy(authz_policy)
227 config.set_authorization_policy(authz_policy)
227
228
228 # Set the default renderer for HTML templates to mako.
229 # Set the default renderer for HTML templates to mako.
229 config.add_mako_renderer('.html')
230 config.add_mako_renderer('.html')
230
231
231 # plugin information
232 # plugin information
232 config.registry.rhodecode_plugins = {}
233 config.registry.rhodecode_plugins = {}
233
234
234 config.add_directive(
235 config.add_directive(
235 'register_rhodecode_plugin', register_rhodecode_plugin)
236 'register_rhodecode_plugin', register_rhodecode_plugin)
236 # include RhodeCode plugins
237 # include RhodeCode plugins
237 includes = aslist(settings.get('rhodecode.includes', []))
238 includes = aslist(settings.get('rhodecode.includes', []))
238 for inc in includes:
239 for inc in includes:
239 config.include(inc)
240 config.include(inc)
240
241
241 pylons_app = make_app(
242 pylons_app = make_app(
242 config.registry._pylons_compat_global_config,
243 config.registry._pylons_compat_global_config,
243 **config.registry._pylons_compat_settings)
244 **config.registry._pylons_compat_settings)
244 config.registry._pylons_compat_config = pylons_app.config
245 config.registry._pylons_compat_config = pylons_app.config
245
246
246 pylons_app_as_view = wsgiapp(pylons_app)
247 pylons_app_as_view = wsgiapp(pylons_app)
247
248
248 # Protect from VCS Server error related pages when server is not available
249 # Protect from VCS Server error related pages when server is not available
249 vcs_server_enabled = asbool(settings.get('vcs.server.enable', 'true'))
250 vcs_server_enabled = asbool(settings.get('vcs.server.enable', 'true'))
250 if not vcs_server_enabled:
251 if not vcs_server_enabled:
251 pylons_app_as_view = DisableVCSPagesWrapper(pylons_app_as_view)
252 pylons_app_as_view = DisableVCSPagesWrapper(pylons_app_as_view)
252
253
253
254
254 def pylons_app_with_error_handler(context, request):
255 def pylons_app_with_error_handler(context, request):
255 """
256 """
256 Handle exceptions from rc pylons app:
257 Handle exceptions from rc pylons app:
257
258
258 - old webob type exceptions get converted to pyramid exceptions
259 - old webob type exceptions get converted to pyramid exceptions
259 - pyramid exceptions are passed to the error handler view
260 - pyramid exceptions are passed to the error handler view
260 """
261 """
261 try:
262 try:
262 response = pylons_app_as_view(context, request)
263 response = pylons_app_as_view(context, request)
263 if 400 <= response.status_int <= 599: # webob type error responses
264 if 400 <= response.status_int <= 599: # webob type error responses
264 return error_handler(
265 return error_handler(
265 webob_to_pyramid_http_response(response), request)
266 webob_to_pyramid_http_response(response), request)
266 except HTTPError as e: # pyramid type exceptions
267 except HTTPError as e: # pyramid type exceptions
267 return error_handler(e, request)
268 return error_handler(e, request)
268 except Exception:
269 except Exception:
269 if settings.get('debugtoolbar.enabled', False):
270 if settings.get('debugtoolbar.enabled', False):
270 raise
271 raise
271 return error_handler(HTTPInternalServerError(), request)
272 return error_handler(HTTPInternalServerError(), request)
272 return response
273 return response
273
274
274 # This is the glue which allows us to migrate in chunks. By registering the
275 # This is the glue which allows us to migrate in chunks. By registering the
275 # pylons based application as the "Not Found" view in Pyramid, we will
276 # pylons based application as the "Not Found" view in Pyramid, we will
276 # fallback to the old application each time the new one does not yet know
277 # fallback to the old application each time the new one does not yet know
277 # how to handle a request.
278 # how to handle a request.
278 config.add_notfound_view(pylons_app_with_error_handler)
279 config.add_notfound_view(pylons_app_with_error_handler)
279
280
280 if settings.get('debugtoolbar.enabled', False):
281 if settings.get('debugtoolbar.enabled', False):
281 # if toolbar, then only http type exceptions get caught and rendered
282 # if toolbar, then only http type exceptions get caught and rendered
282 ExcClass = HTTPError
283 ExcClass = HTTPError
283 else:
284 else:
284 # if no toolbar, then any exception gets caught and rendered
285 # if no toolbar, then any exception gets caught and rendered
285 ExcClass = Exception
286 ExcClass = Exception
286 config.add_view(error_handler, context=ExcClass)
287 config.add_view(error_handler, context=ExcClass)
287
288
288
289
289 def includeme_last(config):
290 def includeme_last(config):
290 """
291 """
291 The static file catchall needs to be last in the view configuration.
292 The static file catchall needs to be last in the view configuration.
292 """
293 """
293 settings = config.registry.settings
294 settings = config.registry.settings
294
295
295 # Note: johbo: I would prefer to register a prefix for static files at some
296 # Note: johbo: I would prefer to register a prefix for static files at some
296 # point, e.g. move them under '_static/'. This would fully avoid that we
297 # point, e.g. move them under '_static/'. This would fully avoid that we
297 # can have name clashes with a repository name. Imaging someone calling his
298 # can have name clashes with a repository name. Imaging someone calling his
298 # repo "css" ;-) Also having an external web server to serve out the static
299 # repo "css" ;-) Also having an external web server to serve out the static
299 # files seems to be easier to set up if they have a common prefix.
300 # files seems to be easier to set up if they have a common prefix.
300 #
301 #
301 # Example: config.add_static_view('_static', path='rhodecode:public')
302 # Example: config.add_static_view('_static', path='rhodecode:public')
302 #
303 #
303 # It might be an option to register both paths for a while and then migrate
304 # It might be an option to register both paths for a while and then migrate
304 # over to the new location.
305 # over to the new location.
305
306
306 # Serving static files with a catchall.
307 # Serving static files with a catchall.
307 if settings['static_files']:
308 if settings['static_files']:
308 config.add_route('catchall_static', '/*subpath')
309 config.add_route('catchall_static', '/*subpath')
309 config.add_view(
310 config.add_view(
310 static_view('rhodecode:public'), route_name='catchall_static')
311 static_view('rhodecode:public'), route_name='catchall_static')
311
312
312
313
313 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
314 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
314 """
315 """
315 Apply outer WSGI middlewares around the application.
316 Apply outer WSGI middlewares around the application.
316
317
317 Part of this has been moved up from the Pylons layer, so that the
318 Part of this has been moved up from the Pylons layer, so that the
318 data is also available if old Pylons code is hit through an already ported
319 data is also available if old Pylons code is hit through an already ported
319 view.
320 view.
320 """
321 """
321 settings = config.registry.settings
322 settings = config.registry.settings
322
323
323 # Add RoutesMiddleware to support the pylons compatibility tween during
324 # Add RoutesMiddleware to support the pylons compatibility tween during
324 # migration to pyramid.
325 # migration to pyramid.
325 pyramid_app = RoutesMiddleware(
326 pyramid_app = RoutesMiddleware(
326 pyramid_app, config.registry._pylons_compat_config['routes.map'])
327 pyramid_app, config.registry._pylons_compat_config['routes.map'])
327
328
328 if asbool(settings.get('appenlight', 'false')):
329 if asbool(settings.get('appenlight', 'false')):
329 pyramid_app, _ = wrap_in_appenlight_if_enabled(
330 pyramid_app, _ = wrap_in_appenlight_if_enabled(
330 pyramid_app, config.registry._pylons_compat_config)
331 pyramid_app, config.registry._pylons_compat_config)
331
332
332 # TODO: johbo: Don't really see why we enable the gzip middleware when
333 # TODO: johbo: Don't really see why we enable the gzip middleware when
333 # serving static files, might be something that should have its own setting
334 # serving static files, might be something that should have its own setting
334 # as well?
335 # as well?
335 if settings['static_files']:
336 if settings['static_files']:
336 pyramid_app = make_gzip_middleware(
337 pyramid_app = make_gzip_middleware(
337 pyramid_app, settings, compress_level=1)
338 pyramid_app, settings, compress_level=1)
338
339
339 return pyramid_app
340 return pyramid_app
340
341
341
342
342 def sanitize_settings_and_apply_defaults(settings):
343 def sanitize_settings_and_apply_defaults(settings):
343 """
344 """
344 Applies settings defaults and does all type conversion.
345 Applies settings defaults and does all type conversion.
345
346
346 We would move all settings parsing and preparation into this place, so that
347 We would move all settings parsing and preparation into this place, so that
347 we have only one place left which deals with this part. The remaining parts
348 we have only one place left which deals with this part. The remaining parts
348 of the application would start to rely fully on well prepared settings.
349 of the application would start to rely fully on well prepared settings.
349
350
350 This piece would later be split up per topic to avoid a big fat monster
351 This piece would later be split up per topic to avoid a big fat monster
351 function.
352 function.
352 """
353 """
353
354
354 # Pyramid's mako renderer has to search in the templates folder so that the
355 # Pyramid's mako renderer has to search in the templates folder so that the
355 # old templates still work. Ported and new templates are expected to use
356 # old templates still work. Ported and new templates are expected to use
356 # real asset specifications for the includes.
357 # real asset specifications for the includes.
357 mako_directories = settings.setdefault('mako.directories', [
358 mako_directories = settings.setdefault('mako.directories', [
358 # Base templates of the original Pylons application
359 # Base templates of the original Pylons application
359 'rhodecode:templates',
360 'rhodecode:templates',
360 ])
361 ])
361 log.debug(
362 log.debug(
362 "Using the following Mako template directories: %s",
363 "Using the following Mako template directories: %s",
363 mako_directories)
364 mako_directories)
364
365
365 # Default includes, possible to change as a user
366 # Default includes, possible to change as a user
366 pyramid_includes = settings.setdefault('pyramid.includes', [
367 pyramid_includes = settings.setdefault('pyramid.includes', [
367 'rhodecode.lib.middleware.request_wrapper',
368 'rhodecode.lib.middleware.request_wrapper',
368 ])
369 ])
369 log.debug(
370 log.debug(
370 "Using the following pyramid.includes: %s",
371 "Using the following pyramid.includes: %s",
371 pyramid_includes)
372 pyramid_includes)
372
373
373 # TODO: johbo: Re-think this, usually the call to config.include
374 # TODO: johbo: Re-think this, usually the call to config.include
374 # should allow to pass in a prefix.
375 # should allow to pass in a prefix.
375 settings.setdefault('rhodecode.api.url', '/_admin/api')
376 settings.setdefault('rhodecode.api.url', '/_admin/api')
376
377
377 _bool_setting(settings, 'vcs.server.enable', 'true')
378 _bool_setting(settings, 'vcs.server.enable', 'true')
378 _bool_setting(settings, 'static_files', 'true')
379 _bool_setting(settings, 'static_files', 'true')
379 _bool_setting(settings, 'is_test', 'false')
380 _bool_setting(settings, 'is_test', 'false')
380
381
381 return settings
382 return settings
382
383
383
384
384 def _bool_setting(settings, name, default):
385 def _bool_setting(settings, name, default):
385 settings[name] = asbool(settings.get(name, default))
386 settings[name] = asbool(settings.get(name, default))
@@ -1,852 +1,852 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 """
22 """
23 settings controller for rhodecode admin
23 settings controller for rhodecode admin
24 """
24 """
25
25
26 import collections
26 import collections
27 import logging
27 import logging
28 import urllib2
28 import urllib2
29
29
30 import datetime
30 import datetime
31 import formencode
31 import formencode
32 from formencode import htmlfill
32 from formencode import htmlfill
33 import packaging.version
33 import packaging.version
34 from pylons import request, tmpl_context as c, url, config
34 from pylons import request, tmpl_context as c, url, config
35 from pylons.controllers.util import redirect
35 from pylons.controllers.util import redirect
36 from pylons.i18n.translation import _, lazy_ugettext
36 from pylons.i18n.translation import _, lazy_ugettext
37 from webob.exc import HTTPBadRequest
37 from webob.exc import HTTPBadRequest
38
38
39 import rhodecode
39 import rhodecode
40 from rhodecode.lib import auth
40 from rhodecode.lib import auth
41 from rhodecode.lib import helpers as h
41 from rhodecode.lib import helpers as h
42 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
42 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
43 from rhodecode.lib.base import BaseController, render
43 from rhodecode.lib.base import BaseController, render
44 from rhodecode.lib.celerylib import tasks, run_task
44 from rhodecode.lib.celerylib import tasks, run_task
45 from rhodecode.lib.utils import repo2db_mapper
45 from rhodecode.lib.utils import repo2db_mapper
46 from rhodecode.lib.utils2 import (
46 from rhodecode.lib.utils2 import (
47 str2bool, safe_unicode, AttributeDict, safe_int)
47 str2bool, safe_unicode, AttributeDict, safe_int)
48 from rhodecode.lib.compat import OrderedDict
48 from rhodecode.lib.compat import OrderedDict
49 from rhodecode.lib.ext_json import json
49 from rhodecode.lib.ext_json import json
50 from rhodecode.lib.utils import jsonify
50 from rhodecode.lib.utils import jsonify
51
51
52 from rhodecode.model.db import RhodeCodeUi, Repository
52 from rhodecode.model.db import RhodeCodeUi, Repository
53 from rhodecode.model.forms import ApplicationSettingsForm, \
53 from rhodecode.model.forms import ApplicationSettingsForm, \
54 ApplicationUiSettingsForm, ApplicationVisualisationForm, \
54 ApplicationUiSettingsForm, ApplicationVisualisationForm, \
55 LabsSettingsForm, IssueTrackerPatternsForm
55 LabsSettingsForm, IssueTrackerPatternsForm
56
56
57 from rhodecode.model.scm import ScmModel
57 from rhodecode.model.scm import ScmModel
58 from rhodecode.model.notification import EmailNotificationModel
58 from rhodecode.model.notification import EmailNotificationModel
59 from rhodecode.model.meta import Session
59 from rhodecode.model.meta import Session
60 from rhodecode.model.settings import (
60 from rhodecode.model.settings import (
61 IssueTrackerSettingsModel, VcsSettingsModel, SettingNotFound,
61 IssueTrackerSettingsModel, VcsSettingsModel, SettingNotFound,
62 SettingsModel)
62 SettingsModel)
63 from rhodecode.model.supervisor import SupervisorModel, SUPERVISOR_MASTER
63 from rhodecode.model.supervisor import SupervisorModel, SUPERVISOR_MASTER
64
64
65
65
66 log = logging.getLogger(__name__)
66 log = logging.getLogger(__name__)
67
67
68
68
69 class SettingsController(BaseController):
69 class SettingsController(BaseController):
70 """REST Controller styled on the Atom Publishing Protocol"""
70 """REST Controller styled on the Atom Publishing Protocol"""
71 # To properly map this controller, ensure your config/routing.py
71 # To properly map this controller, ensure your config/routing.py
72 # file has a resource setup:
72 # file has a resource setup:
73 # map.resource('setting', 'settings', controller='admin/settings',
73 # map.resource('setting', 'settings', controller='admin/settings',
74 # path_prefix='/admin', name_prefix='admin_')
74 # path_prefix='/admin', name_prefix='admin_')
75
75
76 @LoginRequired()
76 @LoginRequired()
77 def __before__(self):
77 def __before__(self):
78 super(SettingsController, self).__before__()
78 super(SettingsController, self).__before__()
79 c.labs_active = str2bool(
79 c.labs_active = str2bool(
80 rhodecode.CONFIG.get('labs_settings_active', 'false'))
80 rhodecode.CONFIG.get('labs_settings_active', 'false'))
81 c.navlist = navigation.get_navlist(request)
81 c.navlist = navigation.get_navlist(request)
82
82
83 def _get_hg_ui_settings(self):
83 def _get_hg_ui_settings(self):
84 ret = RhodeCodeUi.query().all()
84 ret = RhodeCodeUi.query().all()
85
85
86 if not ret:
86 if not ret:
87 raise Exception('Could not get application ui settings !')
87 raise Exception('Could not get application ui settings !')
88 settings = {}
88 settings = {}
89 for each in ret:
89 for each in ret:
90 k = each.ui_key
90 k = each.ui_key
91 v = each.ui_value
91 v = each.ui_value
92 if k == '/':
92 if k == '/':
93 k = 'root_path'
93 k = 'root_path'
94
94
95 if k in ['push_ssl', 'publish']:
95 if k in ['push_ssl', 'publish']:
96 v = str2bool(v)
96 v = str2bool(v)
97
97
98 if k.find('.') != -1:
98 if k.find('.') != -1:
99 k = k.replace('.', '_')
99 k = k.replace('.', '_')
100
100
101 if each.ui_section in ['hooks', 'extensions']:
101 if each.ui_section in ['hooks', 'extensions']:
102 v = each.ui_active
102 v = each.ui_active
103
103
104 settings[each.ui_section + '_' + k] = v
104 settings[each.ui_section + '_' + k] = v
105 return settings
105 return settings
106
106
107 @HasPermissionAllDecorator('hg.admin')
107 @HasPermissionAllDecorator('hg.admin')
108 @auth.CSRFRequired()
108 @auth.CSRFRequired()
109 @jsonify
109 @jsonify
110 def delete_svn_pattern(self):
110 def delete_svn_pattern(self):
111 if not request.is_xhr:
111 if not request.is_xhr:
112 raise HTTPBadRequest()
112 raise HTTPBadRequest()
113
113
114 delete_pattern_id = request.POST.get('delete_svn_pattern')
114 delete_pattern_id = request.POST.get('delete_svn_pattern')
115 model = VcsSettingsModel()
115 model = VcsSettingsModel()
116 try:
116 try:
117 model.delete_global_svn_pattern(delete_pattern_id)
117 model.delete_global_svn_pattern(delete_pattern_id)
118 except SettingNotFound:
118 except SettingNotFound:
119 raise HTTPBadRequest()
119 raise HTTPBadRequest()
120
120
121 Session().commit()
121 Session().commit()
122 return True
122 return True
123
123
124 @HasPermissionAllDecorator('hg.admin')
124 @HasPermissionAllDecorator('hg.admin')
125 @auth.CSRFRequired()
125 @auth.CSRFRequired()
126 def settings_vcs_update(self):
126 def settings_vcs_update(self):
127 """POST /admin/settings: All items in the collection"""
127 """POST /admin/settings: All items in the collection"""
128 # url('admin_settings_vcs')
128 # url('admin_settings_vcs')
129 c.active = 'vcs'
129 c.active = 'vcs'
130
130
131 model = VcsSettingsModel()
131 model = VcsSettingsModel()
132 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
132 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
133 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
133 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
134
134
135 application_form = ApplicationUiSettingsForm()()
135 application_form = ApplicationUiSettingsForm()()
136 try:
136 try:
137 form_result = application_form.to_python(dict(request.POST))
137 form_result = application_form.to_python(dict(request.POST))
138 except formencode.Invalid as errors:
138 except formencode.Invalid as errors:
139 h.flash(
139 h.flash(
140 _("Some form inputs contain invalid data."),
140 _("Some form inputs contain invalid data."),
141 category='error')
141 category='error')
142 return htmlfill.render(
142 return htmlfill.render(
143 render('admin/settings/settings.html'),
143 render('admin/settings/settings.html'),
144 defaults=errors.value,
144 defaults=errors.value,
145 errors=errors.error_dict or {},
145 errors=errors.error_dict or {},
146 prefix_error=False,
146 prefix_error=False,
147 encoding="UTF-8",
147 encoding="UTF-8",
148 force_defaults=False
148 force_defaults=False
149 )
149 )
150
150
151 try:
151 try:
152 model.update_global_ssl_setting(form_result['web_push_ssl'])
152 model.update_global_ssl_setting(form_result['web_push_ssl'])
153 if c.visual.allow_repo_location_change:
153 if c.visual.allow_repo_location_change:
154 model.update_global_path_setting(
154 model.update_global_path_setting(
155 form_result['paths_root_path'])
155 form_result['paths_root_path'])
156 model.update_global_hook_settings(form_result)
156 model.update_global_hook_settings(form_result)
157 model.create_global_svn_settings(form_result)
157 model.create_global_svn_settings(form_result)
158 model.create_or_update_global_hg_settings(form_result)
158 model.create_or_update_global_hg_settings(form_result)
159 model.create_or_update_global_pr_settings(form_result)
159 model.create_or_update_global_pr_settings(form_result)
160 except Exception:
160 except Exception:
161 log.exception("Exception while updating settings")
161 log.exception("Exception while updating settings")
162 h.flash(_('Error occurred during updating '
162 h.flash(_('Error occurred during updating '
163 'application settings'), category='error')
163 'application settings'), category='error')
164 else:
164 else:
165 Session().commit()
165 Session().commit()
166 h.flash(_('Updated VCS settings'), category='success')
166 h.flash(_('Updated VCS settings'), category='success')
167 return redirect(url('admin_settings_vcs'))
167 return redirect(url('admin_settings_vcs'))
168
168
169 return htmlfill.render(
169 return htmlfill.render(
170 render('admin/settings/settings.html'),
170 render('admin/settings/settings.html'),
171 defaults=self._form_defaults(),
171 defaults=self._form_defaults(),
172 encoding="UTF-8",
172 encoding="UTF-8",
173 force_defaults=False)
173 force_defaults=False)
174
174
175 @HasPermissionAllDecorator('hg.admin')
175 @HasPermissionAllDecorator('hg.admin')
176 def settings_vcs(self):
176 def settings_vcs(self):
177 """GET /admin/settings: All items in the collection"""
177 """GET /admin/settings: All items in the collection"""
178 # url('admin_settings_vcs')
178 # url('admin_settings_vcs')
179 c.active = 'vcs'
179 c.active = 'vcs'
180 model = VcsSettingsModel()
180 model = VcsSettingsModel()
181 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
181 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
182 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
182 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
183
183
184 return htmlfill.render(
184 return htmlfill.render(
185 render('admin/settings/settings.html'),
185 render('admin/settings/settings.html'),
186 defaults=self._form_defaults(),
186 defaults=self._form_defaults(),
187 encoding="UTF-8",
187 encoding="UTF-8",
188 force_defaults=False)
188 force_defaults=False)
189
189
190 @HasPermissionAllDecorator('hg.admin')
190 @HasPermissionAllDecorator('hg.admin')
191 @auth.CSRFRequired()
191 @auth.CSRFRequired()
192 def settings_mapping_update(self):
192 def settings_mapping_update(self):
193 """POST /admin/settings/mapping: All items in the collection"""
193 """POST /admin/settings/mapping: All items in the collection"""
194 # url('admin_settings_mapping')
194 # url('admin_settings_mapping')
195 c.active = 'mapping'
195 c.active = 'mapping'
196 rm_obsolete = request.POST.get('destroy', False)
196 rm_obsolete = request.POST.get('destroy', False)
197 invalidate_cache = request.POST.get('invalidate', False)
197 invalidate_cache = request.POST.get('invalidate', False)
198 log.debug(
198 log.debug(
199 'rescanning repo location with destroy obsolete=%s', rm_obsolete)
199 'rescanning repo location with destroy obsolete=%s', rm_obsolete)
200
200
201 if invalidate_cache:
201 if invalidate_cache:
202 log.debug('invalidating all repositories cache')
202 log.debug('invalidating all repositories cache')
203 for repo in Repository.get_all():
203 for repo in Repository.get_all():
204 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
204 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
205
205
206 filesystem_repos = ScmModel().repo_scan()
206 filesystem_repos = ScmModel().repo_scan()
207 added, removed = repo2db_mapper(filesystem_repos, rm_obsolete)
207 added, removed = repo2db_mapper(filesystem_repos, rm_obsolete)
208 _repr = lambda l: ', '.join(map(safe_unicode, l)) or '-'
208 _repr = lambda l: ', '.join(map(safe_unicode, l)) or '-'
209 h.flash(_('Repositories successfully '
209 h.flash(_('Repositories successfully '
210 'rescanned added: %s ; removed: %s') %
210 'rescanned added: %s ; removed: %s') %
211 (_repr(added), _repr(removed)),
211 (_repr(added), _repr(removed)),
212 category='success')
212 category='success')
213 return redirect(url('admin_settings_mapping'))
213 return redirect(url('admin_settings_mapping'))
214
214
215 @HasPermissionAllDecorator('hg.admin')
215 @HasPermissionAllDecorator('hg.admin')
216 def settings_mapping(self):
216 def settings_mapping(self):
217 """GET /admin/settings/mapping: All items in the collection"""
217 """GET /admin/settings/mapping: All items in the collection"""
218 # url('admin_settings_mapping')
218 # url('admin_settings_mapping')
219 c.active = 'mapping'
219 c.active = 'mapping'
220
220
221 return htmlfill.render(
221 return htmlfill.render(
222 render('admin/settings/settings.html'),
222 render('admin/settings/settings.html'),
223 defaults=self._form_defaults(),
223 defaults=self._form_defaults(),
224 encoding="UTF-8",
224 encoding="UTF-8",
225 force_defaults=False)
225 force_defaults=False)
226
226
227 @HasPermissionAllDecorator('hg.admin')
227 @HasPermissionAllDecorator('hg.admin')
228 @auth.CSRFRequired()
228 @auth.CSRFRequired()
229 def settings_global_update(self):
229 def settings_global_update(self):
230 """POST /admin/settings/global: All items in the collection"""
230 """POST /admin/settings/global: All items in the collection"""
231 # url('admin_settings_global')
231 # url('admin_settings_global')
232 c.active = 'global'
232 c.active = 'global'
233 application_form = ApplicationSettingsForm()()
233 application_form = ApplicationSettingsForm()()
234 try:
234 try:
235 form_result = application_form.to_python(dict(request.POST))
235 form_result = application_form.to_python(dict(request.POST))
236 except formencode.Invalid as errors:
236 except formencode.Invalid as errors:
237 return htmlfill.render(
237 return htmlfill.render(
238 render('admin/settings/settings.html'),
238 render('admin/settings/settings.html'),
239 defaults=errors.value,
239 defaults=errors.value,
240 errors=errors.error_dict or {},
240 errors=errors.error_dict or {},
241 prefix_error=False,
241 prefix_error=False,
242 encoding="UTF-8",
242 encoding="UTF-8",
243 force_defaults=False)
243 force_defaults=False)
244
244
245 try:
245 try:
246 settings = [
246 settings = [
247 ('title', 'rhodecode_title'),
247 ('title', 'rhodecode_title'),
248 ('realm', 'rhodecode_realm'),
248 ('realm', 'rhodecode_realm'),
249 ('pre_code', 'rhodecode_pre_code'),
249 ('pre_code', 'rhodecode_pre_code'),
250 ('post_code', 'rhodecode_post_code'),
250 ('post_code', 'rhodecode_post_code'),
251 ('captcha_public_key', 'rhodecode_captcha_public_key'),
251 ('captcha_public_key', 'rhodecode_captcha_public_key'),
252 ('captcha_private_key', 'rhodecode_captcha_private_key'),
252 ('captcha_private_key', 'rhodecode_captcha_private_key'),
253 ]
253 ]
254 for setting, form_key in settings:
254 for setting, form_key in settings:
255 sett = SettingsModel().create_or_update_setting(
255 sett = SettingsModel().create_or_update_setting(
256 setting, form_result[form_key])
256 setting, form_result[form_key])
257 Session().add(sett)
257 Session().add(sett)
258
258
259 Session().commit()
259 Session().commit()
260 h.flash(_('Updated application settings'), category='success')
260 h.flash(_('Updated application settings'), category='success')
261
261
262 except Exception:
262 except Exception:
263 log.exception("Exception while updating application settings")
263 log.exception("Exception while updating application settings")
264 h.flash(
264 h.flash(
265 _('Error occurred during updating application settings'),
265 _('Error occurred during updating application settings'),
266 category='error')
266 category='error')
267
267
268 return redirect(url('admin_settings_global'))
268 return redirect(url('admin_settings_global'))
269
269
270 @HasPermissionAllDecorator('hg.admin')
270 @HasPermissionAllDecorator('hg.admin')
271 def settings_global(self):
271 def settings_global(self):
272 """GET /admin/settings/global: All items in the collection"""
272 """GET /admin/settings/global: All items in the collection"""
273 # url('admin_settings_global')
273 # url('admin_settings_global')
274 c.active = 'global'
274 c.active = 'global'
275
275
276 return htmlfill.render(
276 return htmlfill.render(
277 render('admin/settings/settings.html'),
277 render('admin/settings/settings.html'),
278 defaults=self._form_defaults(),
278 defaults=self._form_defaults(),
279 encoding="UTF-8",
279 encoding="UTF-8",
280 force_defaults=False)
280 force_defaults=False)
281
281
282 @HasPermissionAllDecorator('hg.admin')
282 @HasPermissionAllDecorator('hg.admin')
283 @auth.CSRFRequired()
283 @auth.CSRFRequired()
284 def settings_visual_update(self):
284 def settings_visual_update(self):
285 """POST /admin/settings/visual: All items in the collection"""
285 """POST /admin/settings/visual: All items in the collection"""
286 # url('admin_settings_visual')
286 # url('admin_settings_visual')
287 c.active = 'visual'
287 c.active = 'visual'
288 application_form = ApplicationVisualisationForm()()
288 application_form = ApplicationVisualisationForm()()
289 try:
289 try:
290 form_result = application_form.to_python(dict(request.POST))
290 form_result = application_form.to_python(dict(request.POST))
291 except formencode.Invalid as errors:
291 except formencode.Invalid as errors:
292 return htmlfill.render(
292 return htmlfill.render(
293 render('admin/settings/settings.html'),
293 render('admin/settings/settings.html'),
294 defaults=errors.value,
294 defaults=errors.value,
295 errors=errors.error_dict or {},
295 errors=errors.error_dict or {},
296 prefix_error=False,
296 prefix_error=False,
297 encoding="UTF-8",
297 encoding="UTF-8",
298 force_defaults=False
298 force_defaults=False
299 )
299 )
300
300
301 try:
301 try:
302 settings = [
302 settings = [
303 ('show_public_icon', 'rhodecode_show_public_icon', 'bool'),
303 ('show_public_icon', 'rhodecode_show_public_icon', 'bool'),
304 ('show_private_icon', 'rhodecode_show_private_icon', 'bool'),
304 ('show_private_icon', 'rhodecode_show_private_icon', 'bool'),
305 ('stylify_metatags', 'rhodecode_stylify_metatags', 'bool'),
305 ('stylify_metatags', 'rhodecode_stylify_metatags', 'bool'),
306 ('repository_fields', 'rhodecode_repository_fields', 'bool'),
306 ('repository_fields', 'rhodecode_repository_fields', 'bool'),
307 ('dashboard_items', 'rhodecode_dashboard_items', 'int'),
307 ('dashboard_items', 'rhodecode_dashboard_items', 'int'),
308 ('admin_grid_items', 'rhodecode_admin_grid_items', 'int'),
308 ('admin_grid_items', 'rhodecode_admin_grid_items', 'int'),
309 ('show_version', 'rhodecode_show_version', 'bool'),
309 ('show_version', 'rhodecode_show_version', 'bool'),
310 ('use_gravatar', 'rhodecode_use_gravatar', 'bool'),
310 ('use_gravatar', 'rhodecode_use_gravatar', 'bool'),
311 ('markup_renderer', 'rhodecode_markup_renderer', 'unicode'),
311 ('markup_renderer', 'rhodecode_markup_renderer', 'unicode'),
312 ('gravatar_url', 'rhodecode_gravatar_url', 'unicode'),
312 ('gravatar_url', 'rhodecode_gravatar_url', 'unicode'),
313 ('clone_uri_tmpl', 'rhodecode_clone_uri_tmpl', 'unicode'),
313 ('clone_uri_tmpl', 'rhodecode_clone_uri_tmpl', 'unicode'),
314 ('support_url', 'rhodecode_support_url', 'unicode'),
314 ('support_url', 'rhodecode_support_url', 'unicode'),
315 ('show_revision_number', 'rhodecode_show_revision_number', 'bool'),
315 ('show_revision_number', 'rhodecode_show_revision_number', 'bool'),
316 ('show_sha_length', 'rhodecode_show_sha_length', 'int'),
316 ('show_sha_length', 'rhodecode_show_sha_length', 'int'),
317 ]
317 ]
318 for setting, form_key, type_ in settings:
318 for setting, form_key, type_ in settings:
319 sett = SettingsModel().create_or_update_setting(
319 sett = SettingsModel().create_or_update_setting(
320 setting, form_result[form_key], type_)
320 setting, form_result[form_key], type_)
321 Session().add(sett)
321 Session().add(sett)
322
322
323 Session().commit()
323 Session().commit()
324
324
325 h.flash(_('Updated visualisation settings'), category='success')
325 h.flash(_('Updated visualisation settings'), category='success')
326 except Exception:
326 except Exception:
327 log.exception("Exception updating visualization settings")
327 log.exception("Exception updating visualization settings")
328 h.flash(_('Error occurred during updating '
328 h.flash(_('Error occurred during updating '
329 'visualisation settings'),
329 'visualisation settings'),
330 category='error')
330 category='error')
331
331
332 return redirect(url('admin_settings_visual'))
332 return redirect(url('admin_settings_visual'))
333
333
334 @HasPermissionAllDecorator('hg.admin')
334 @HasPermissionAllDecorator('hg.admin')
335 def settings_visual(self):
335 def settings_visual(self):
336 """GET /admin/settings/visual: All items in the collection"""
336 """GET /admin/settings/visual: All items in the collection"""
337 # url('admin_settings_visual')
337 # url('admin_settings_visual')
338 c.active = 'visual'
338 c.active = 'visual'
339
339
340 return htmlfill.render(
340 return htmlfill.render(
341 render('admin/settings/settings.html'),
341 render('admin/settings/settings.html'),
342 defaults=self._form_defaults(),
342 defaults=self._form_defaults(),
343 encoding="UTF-8",
343 encoding="UTF-8",
344 force_defaults=False)
344 force_defaults=False)
345
345
346 @HasPermissionAllDecorator('hg.admin')
346 @HasPermissionAllDecorator('hg.admin')
347 @auth.CSRFRequired()
347 @auth.CSRFRequired()
348 def settings_issuetracker_test(self):
348 def settings_issuetracker_test(self):
349 if request.is_xhr:
349 if request.is_xhr:
350 return h.urlify_commit_message(
350 return h.urlify_commit_message(
351 request.POST.get('test_text', ''),
351 request.POST.get('test_text', ''),
352 'repo_group/test_repo1')
352 'repo_group/test_repo1')
353 else:
353 else:
354 raise HTTPBadRequest()
354 raise HTTPBadRequest()
355
355
356 @HasPermissionAllDecorator('hg.admin')
356 @HasPermissionAllDecorator('hg.admin')
357 @auth.CSRFRequired()
357 @auth.CSRFRequired()
358 def settings_issuetracker_delete(self):
358 def settings_issuetracker_delete(self):
359 uid = request.POST.get('uid')
359 uid = request.POST.get('uid')
360 IssueTrackerSettingsModel().delete_entries(uid)
360 IssueTrackerSettingsModel().delete_entries(uid)
361 h.flash(_('Removed issue tracker entry'), category='success')
361 h.flash(_('Removed issue tracker entry'), category='success')
362 return redirect(url('admin_settings_issuetracker'))
362 return redirect(url('admin_settings_issuetracker'))
363
363
364 @HasPermissionAllDecorator('hg.admin')
364 @HasPermissionAllDecorator('hg.admin')
365 def settings_issuetracker(self):
365 def settings_issuetracker(self):
366 """GET /admin/settings/issue-tracker: All items in the collection"""
366 """GET /admin/settings/issue-tracker: All items in the collection"""
367 # url('admin_settings_issuetracker')
367 # url('admin_settings_issuetracker')
368 c.active = 'issuetracker'
368 c.active = 'issuetracker'
369 defaults = SettingsModel().get_all_settings()
369 defaults = SettingsModel().get_all_settings()
370
370
371 entry_key = 'rhodecode_issuetracker_pat_'
371 entry_key = 'rhodecode_issuetracker_pat_'
372
372
373 c.issuetracker_entries = {}
373 c.issuetracker_entries = {}
374 for k, v in defaults.items():
374 for k, v in defaults.items():
375 if k.startswith(entry_key):
375 if k.startswith(entry_key):
376 uid = k[len(entry_key):]
376 uid = k[len(entry_key):]
377 c.issuetracker_entries[uid] = None
377 c.issuetracker_entries[uid] = None
378
378
379 for uid in c.issuetracker_entries:
379 for uid in c.issuetracker_entries:
380 c.issuetracker_entries[uid] = AttributeDict({
380 c.issuetracker_entries[uid] = AttributeDict({
381 'pat': defaults.get('rhodecode_issuetracker_pat_' + uid),
381 'pat': defaults.get('rhodecode_issuetracker_pat_' + uid),
382 'url': defaults.get('rhodecode_issuetracker_url_' + uid),
382 'url': defaults.get('rhodecode_issuetracker_url_' + uid),
383 'pref': defaults.get('rhodecode_issuetracker_pref_' + uid),
383 'pref': defaults.get('rhodecode_issuetracker_pref_' + uid),
384 'desc': defaults.get('rhodecode_issuetracker_desc_' + uid),
384 'desc': defaults.get('rhodecode_issuetracker_desc_' + uid),
385 })
385 })
386
386
387 return render('admin/settings/settings.html')
387 return render('admin/settings/settings.html')
388
388
389 @HasPermissionAllDecorator('hg.admin')
389 @HasPermissionAllDecorator('hg.admin')
390 @auth.CSRFRequired()
390 @auth.CSRFRequired()
391 def settings_issuetracker_save(self):
391 def settings_issuetracker_save(self):
392 settings_model = IssueTrackerSettingsModel()
392 settings_model = IssueTrackerSettingsModel()
393
393
394 form = IssueTrackerPatternsForm()().to_python(request.POST)
394 form = IssueTrackerPatternsForm()().to_python(request.POST)
395 for uid in form['delete_patterns']:
395 for uid in form['delete_patterns']:
396 settings_model.delete_entries(uid)
396 settings_model.delete_entries(uid)
397
397
398 for pattern in form['patterns']:
398 for pattern in form['patterns']:
399 for setting, value, type_ in pattern:
399 for setting, value, type_ in pattern:
400 sett = settings_model.create_or_update_setting(
400 sett = settings_model.create_or_update_setting(
401 setting, value, type_)
401 setting, value, type_)
402 Session().add(sett)
402 Session().add(sett)
403
403
404 Session().commit()
404 Session().commit()
405
405
406 h.flash(_('Updated issue tracker entries'), category='success')
406 h.flash(_('Updated issue tracker entries'), category='success')
407 return redirect(url('admin_settings_issuetracker'))
407 return redirect(url('admin_settings_issuetracker'))
408
408
409 @HasPermissionAllDecorator('hg.admin')
409 @HasPermissionAllDecorator('hg.admin')
410 @auth.CSRFRequired()
410 @auth.CSRFRequired()
411 def settings_email_update(self):
411 def settings_email_update(self):
412 """POST /admin/settings/email: All items in the collection"""
412 """POST /admin/settings/email: All items in the collection"""
413 # url('admin_settings_email')
413 # url('admin_settings_email')
414 c.active = 'email'
414 c.active = 'email'
415
415
416 test_email = request.POST.get('test_email')
416 test_email = request.POST.get('test_email')
417
417
418 if not test_email:
418 if not test_email:
419 h.flash(_('Please enter email address'), category='error')
419 h.flash(_('Please enter email address'), category='error')
420 return redirect(url('admin_settings_email'))
420 return redirect(url('admin_settings_email'))
421
421
422 email_kwargs = {
422 email_kwargs = {
423 'date': datetime.datetime.now(),
423 'date': datetime.datetime.now(),
424 'user': c.rhodecode_user,
424 'user': c.rhodecode_user,
425 'rhodecode_version': c.rhodecode_version
425 'rhodecode_version': c.rhodecode_version
426 }
426 }
427
427
428 (subject, headers, email_body,
428 (subject, headers, email_body,
429 email_body_plaintext) = EmailNotificationModel().render_email(
429 email_body_plaintext) = EmailNotificationModel().render_email(
430 EmailNotificationModel.TYPE_EMAIL_TEST, **email_kwargs)
430 EmailNotificationModel.TYPE_EMAIL_TEST, **email_kwargs)
431
431
432 recipients = [test_email] if test_email else None
432 recipients = [test_email] if test_email else None
433
433
434 run_task(tasks.send_email, recipients, subject,
434 run_task(tasks.send_email, recipients, subject,
435 email_body_plaintext, email_body)
435 email_body_plaintext, email_body)
436
436
437 h.flash(_('Send email task created'), category='success')
437 h.flash(_('Send email task created'), category='success')
438 return redirect(url('admin_settings_email'))
438 return redirect(url('admin_settings_email'))
439
439
440 @HasPermissionAllDecorator('hg.admin')
440 @HasPermissionAllDecorator('hg.admin')
441 def settings_email(self):
441 def settings_email(self):
442 """GET /admin/settings/email: All items in the collection"""
442 """GET /admin/settings/email: All items in the collection"""
443 # url('admin_settings_email')
443 # url('admin_settings_email')
444 c.active = 'email'
444 c.active = 'email'
445 c.rhodecode_ini = rhodecode.CONFIG
445 c.rhodecode_ini = rhodecode.CONFIG
446
446
447 return htmlfill.render(
447 return htmlfill.render(
448 render('admin/settings/settings.html'),
448 render('admin/settings/settings.html'),
449 defaults=self._form_defaults(),
449 defaults=self._form_defaults(),
450 encoding="UTF-8",
450 encoding="UTF-8",
451 force_defaults=False)
451 force_defaults=False)
452
452
453 @HasPermissionAllDecorator('hg.admin')
453 @HasPermissionAllDecorator('hg.admin')
454 @auth.CSRFRequired()
454 @auth.CSRFRequired()
455 def settings_hooks_update(self):
455 def settings_hooks_update(self):
456 """POST or DELETE /admin/settings/hooks: All items in the collection"""
456 """POST or DELETE /admin/settings/hooks: All items in the collection"""
457 # url('admin_settings_hooks')
457 # url('admin_settings_hooks')
458 c.active = 'hooks'
458 c.active = 'hooks'
459 if c.visual.allow_custom_hooks_settings:
459 if c.visual.allow_custom_hooks_settings:
460 ui_key = request.POST.get('new_hook_ui_key')
460 ui_key = request.POST.get('new_hook_ui_key')
461 ui_value = request.POST.get('new_hook_ui_value')
461 ui_value = request.POST.get('new_hook_ui_value')
462
462
463 hook_id = request.POST.get('hook_id')
463 hook_id = request.POST.get('hook_id')
464 new_hook = False
464 new_hook = False
465
465
466 model = SettingsModel()
466 model = SettingsModel()
467 try:
467 try:
468 if ui_value and ui_key:
468 if ui_value and ui_key:
469 model.create_or_update_hook(ui_key, ui_value)
469 model.create_or_update_hook(ui_key, ui_value)
470 h.flash(_('Added new hook'), category='success')
470 h.flash(_('Added new hook'), category='success')
471 new_hook = True
471 new_hook = True
472 elif hook_id:
472 elif hook_id:
473 RhodeCodeUi.delete(hook_id)
473 RhodeCodeUi.delete(hook_id)
474 Session().commit()
474 Session().commit()
475
475
476 # check for edits
476 # check for edits
477 update = False
477 update = False
478 _d = request.POST.dict_of_lists()
478 _d = request.POST.dict_of_lists()
479 for k, v in zip(_d.get('hook_ui_key', []),
479 for k, v in zip(_d.get('hook_ui_key', []),
480 _d.get('hook_ui_value_new', [])):
480 _d.get('hook_ui_value_new', [])):
481 model.create_or_update_hook(k, v)
481 model.create_or_update_hook(k, v)
482 update = True
482 update = True
483
483
484 if update and not new_hook:
484 if update and not new_hook:
485 h.flash(_('Updated hooks'), category='success')
485 h.flash(_('Updated hooks'), category='success')
486 Session().commit()
486 Session().commit()
487 except Exception:
487 except Exception:
488 log.exception("Exception during hook creation")
488 log.exception("Exception during hook creation")
489 h.flash(_('Error occurred during hook creation'),
489 h.flash(_('Error occurred during hook creation'),
490 category='error')
490 category='error')
491
491
492 return redirect(url('admin_settings_hooks'))
492 return redirect(url('admin_settings_hooks'))
493
493
494 @HasPermissionAllDecorator('hg.admin')
494 @HasPermissionAllDecorator('hg.admin')
495 def settings_hooks(self):
495 def settings_hooks(self):
496 """GET /admin/settings/hooks: All items in the collection"""
496 """GET /admin/settings/hooks: All items in the collection"""
497 # url('admin_settings_hooks')
497 # url('admin_settings_hooks')
498 c.active = 'hooks'
498 c.active = 'hooks'
499
499
500 model = SettingsModel()
500 model = SettingsModel()
501 c.hooks = model.get_builtin_hooks()
501 c.hooks = model.get_builtin_hooks()
502 c.custom_hooks = model.get_custom_hooks()
502 c.custom_hooks = model.get_custom_hooks()
503
503
504 return htmlfill.render(
504 return htmlfill.render(
505 render('admin/settings/settings.html'),
505 render('admin/settings/settings.html'),
506 defaults=self._form_defaults(),
506 defaults=self._form_defaults(),
507 encoding="UTF-8",
507 encoding="UTF-8",
508 force_defaults=False)
508 force_defaults=False)
509
509
510 @HasPermissionAllDecorator('hg.admin')
510 @HasPermissionAllDecorator('hg.admin')
511 def settings_search(self):
511 def settings_search(self):
512 """GET /admin/settings/search: All items in the collection"""
512 """GET /admin/settings/search: All items in the collection"""
513 # url('admin_settings_search')
513 # url('admin_settings_search')
514 c.active = 'search'
514 c.active = 'search'
515
515
516 from rhodecode.lib.index import searcher_from_config
516 from rhodecode.lib.index import searcher_from_config
517 searcher = searcher_from_config(config)
517 searcher = searcher_from_config(config)
518 c.statistics = searcher.statistics()
518 c.statistics = searcher.statistics()
519
519
520 return render('admin/settings/settings.html')
520 return render('admin/settings/settings.html')
521
521
522 @HasPermissionAllDecorator('hg.admin')
522 @HasPermissionAllDecorator('hg.admin')
523 def settings_system(self):
523 def settings_system(self):
524 """GET /admin/settings/system: All items in the collection"""
524 """GET /admin/settings/system: All items in the collection"""
525 # url('admin_settings_system')
525 # url('admin_settings_system')
526 c.active = 'system'
526 c.active = 'system'
527
527
528 defaults = self._form_defaults()
528 defaults = self._form_defaults()
529 c.rhodecode_ini = rhodecode.CONFIG
529 c.rhodecode_ini = rhodecode.CONFIG
530 c.rhodecode_update_url = defaults.get('rhodecode_update_url')
530 c.rhodecode_update_url = defaults.get('rhodecode_update_url')
531 server_info = ScmModel().get_server_info(request.environ)
531 server_info = ScmModel().get_server_info(request.environ)
532 for key, val in server_info.iteritems():
532 for key, val in server_info.iteritems():
533 setattr(c, key, val)
533 setattr(c, key, val)
534
534
535 if c.disk['percent'] > 90:
535 if c.disk['percent'] > 90:
536 h.flash(h.literal(_(
536 h.flash(h.literal(_(
537 'Critical: your disk space is very low <b>%s%%</b> used' %
537 'Critical: your disk space is very low <b>%s%%</b> used' %
538 c.disk['percent'])), 'error')
538 c.disk['percent'])), 'error')
539 elif c.disk['percent'] > 70:
539 elif c.disk['percent'] > 70:
540 h.flash(h.literal(_(
540 h.flash(h.literal(_(
541 'Warning: your disk space is running low <b>%s%%</b> used' %
541 'Warning: your disk space is running low <b>%s%%</b> used' %
542 c.disk['percent'])), 'warning')
542 c.disk['percent'])), 'warning')
543
543
544 try:
544 try:
545 c.uptime_age = h._age(
545 c.uptime_age = h._age(
546 h.time_to_datetime(c.boot_time), False, show_suffix=False)
546 h.time_to_datetime(c.boot_time), False, show_suffix=False)
547 except TypeError:
547 except TypeError:
548 c.uptime_age = c.boot_time
548 c.uptime_age = c.boot_time
549
549
550 try:
550 try:
551 c.system_memory = '%s/%s, %s%% (%s%%) used%s' % (
551 c.system_memory = '%s/%s, %s%% (%s%%) used%s' % (
552 h.format_byte_size_binary(c.memory['used']),
552 h.format_byte_size_binary(c.memory['used']),
553 h.format_byte_size_binary(c.memory['total']),
553 h.format_byte_size_binary(c.memory['total']),
554 c.memory['percent2'],
554 c.memory['percent2'],
555 c.memory['percent'],
555 c.memory['percent'],
556 ' %s' % c.memory['error'] if 'error' in c.memory else '')
556 ' %s' % c.memory['error'] if 'error' in c.memory else '')
557 except TypeError:
557 except TypeError:
558 c.system_memory = 'NOT AVAILABLE'
558 c.system_memory = 'NOT AVAILABLE'
559
559
560 return htmlfill.render(
560 return htmlfill.render(
561 render('admin/settings/settings.html'),
561 render('admin/settings/settings.html'),
562 defaults=defaults,
562 defaults=defaults,
563 encoding="UTF-8",
563 encoding="UTF-8",
564 force_defaults=False)
564 force_defaults=False)
565
565
566 @staticmethod
566 @staticmethod
567 def get_update_data(update_url):
567 def get_update_data(update_url):
568 """Return the JSON update data."""
568 """Return the JSON update data."""
569 ver = rhodecode.__version__
569 ver = rhodecode.__version__
570 log.debug('Checking for upgrade on `%s` server', update_url)
570 log.debug('Checking for upgrade on `%s` server', update_url)
571 opener = urllib2.build_opener()
571 opener = urllib2.build_opener()
572 opener.addheaders = [('User-agent', 'RhodeCode-SCM/%s' % ver)]
572 opener.addheaders = [('User-agent', 'RhodeCode-SCM/%s' % ver)]
573 response = opener.open(update_url)
573 response = opener.open(update_url)
574 response_data = response.read()
574 response_data = response.read()
575 data = json.loads(response_data)
575 data = json.loads(response_data)
576
576
577 return data
577 return data
578
578
579 @HasPermissionAllDecorator('hg.admin')
579 @HasPermissionAllDecorator('hg.admin')
580 def settings_system_update(self):
580 def settings_system_update(self):
581 """GET /admin/settings/system/updates: All items in the collection"""
581 """GET /admin/settings/system/updates: All items in the collection"""
582 # url('admin_settings_system_update')
582 # url('admin_settings_system_update')
583 defaults = self._form_defaults()
583 defaults = self._form_defaults()
584 update_url = defaults.get('rhodecode_update_url', '')
584 update_url = defaults.get('rhodecode_update_url', '')
585
585
586 _err = lambda s: '<div style="color:#ff8888; padding:4px 0px">%s</div>' % (s)
586 _err = lambda s: '<div style="color:#ff8888; padding:4px 0px">%s</div>' % (s)
587 try:
587 try:
588 data = self.get_update_data(update_url)
588 data = self.get_update_data(update_url)
589 except urllib2.URLError as e:
589 except urllib2.URLError as e:
590 log.exception("Exception contacting upgrade server")
590 log.exception("Exception contacting upgrade server")
591 return _err('Failed to contact upgrade server: %r' % e)
591 return _err('Failed to contact upgrade server: %r' % e)
592 except ValueError as e:
592 except ValueError as e:
593 log.exception("Bad data sent from update server")
593 log.exception("Bad data sent from update server")
594 return _err('Bad data sent from update server')
594 return _err('Bad data sent from update server')
595
595
596 latest = data['versions'][0]
596 latest = data['versions'][0]
597
597
598 c.update_url = update_url
598 c.update_url = update_url
599 c.latest_data = latest
599 c.latest_data = latest
600 c.latest_ver = latest['version']
600 c.latest_ver = latest['version']
601 c.cur_ver = rhodecode.__version__
601 c.cur_ver = rhodecode.__version__
602 c.should_upgrade = False
602 c.should_upgrade = False
603
603
604 if (packaging.version.Version(c.latest_ver) >
604 if (packaging.version.Version(c.latest_ver) >
605 packaging.version.Version(c.cur_ver)):
605 packaging.version.Version(c.cur_ver)):
606 c.should_upgrade = True
606 c.should_upgrade = True
607 c.important_notices = latest['general']
607 c.important_notices = latest['general']
608
608
609 return render('admin/settings/settings_system_update.html')
609 return render('admin/settings/settings_system_update.html')
610
610
611 @HasPermissionAllDecorator('hg.admin')
611 @HasPermissionAllDecorator('hg.admin')
612 def settings_supervisor(self):
612 def settings_supervisor(self):
613 c.rhodecode_ini = rhodecode.CONFIG
613 c.rhodecode_ini = rhodecode.CONFIG
614 c.active = 'supervisor'
614 c.active = 'supervisor'
615
615
616 c.supervisor_procs = OrderedDict([
616 c.supervisor_procs = OrderedDict([
617 (SUPERVISOR_MASTER, {}),
617 (SUPERVISOR_MASTER, {}),
618 ])
618 ])
619
619
620 c.log_size = 10240
620 c.log_size = 10240
621 supervisor = SupervisorModel()
621 supervisor = SupervisorModel()
622
622
623 _connection = supervisor.get_connection(
623 _connection = supervisor.get_connection(
624 c.rhodecode_ini.get('supervisor.uri'))
624 c.rhodecode_ini.get('supervisor.uri'))
625 c.connection_error = None
625 c.connection_error = None
626 try:
626 try:
627 _connection.supervisor.getAllProcessInfo()
627 _connection.supervisor.getAllProcessInfo()
628 except Exception as e:
628 except Exception as e:
629 c.connection_error = str(e)
629 c.connection_error = str(e)
630 log.exception("Exception reading supervisor data")
630 log.exception("Exception reading supervisor data")
631 return render('admin/settings/settings.html')
631 return render('admin/settings/settings.html')
632
632
633 groupid = c.rhodecode_ini.get('supervisor.group_id')
633 groupid = c.rhodecode_ini.get('supervisor.group_id')
634
634
635 # feed our group processes to the main
635 # feed our group processes to the main
636 for proc in supervisor.get_group_processes(_connection, groupid):
636 for proc in supervisor.get_group_processes(_connection, groupid):
637 c.supervisor_procs[proc['name']] = {}
637 c.supervisor_procs[proc['name']] = {}
638
638
639 for k in c.supervisor_procs.keys():
639 for k in c.supervisor_procs.keys():
640 try:
640 try:
641 # master process info
641 # master process info
642 if k == SUPERVISOR_MASTER:
642 if k == SUPERVISOR_MASTER:
643 _data = supervisor.get_master_state(_connection)
643 _data = supervisor.get_master_state(_connection)
644 _data['name'] = 'supervisor master'
644 _data['name'] = 'supervisor master'
645 _data['description'] = 'pid %s, id: %s, ver: %s' % (
645 _data['description'] = 'pid %s, id: %s, ver: %s' % (
646 _data['pid'], _data['id'], _data['ver'])
646 _data['pid'], _data['id'], _data['ver'])
647 c.supervisor_procs[k] = _data
647 c.supervisor_procs[k] = _data
648 else:
648 else:
649 procid = groupid + ":" + k
649 procid = groupid + ":" + k
650 c.supervisor_procs[k] = supervisor.get_process_info(_connection, procid)
650 c.supervisor_procs[k] = supervisor.get_process_info(_connection, procid)
651 except Exception as e:
651 except Exception as e:
652 log.exception("Exception reading supervisor data")
652 log.exception("Exception reading supervisor data")
653 c.supervisor_procs[k] = {'_rhodecode_error': str(e)}
653 c.supervisor_procs[k] = {'_rhodecode_error': str(e)}
654
654
655 return render('admin/settings/settings.html')
655 return render('admin/settings/settings.html')
656
656
657 @HasPermissionAllDecorator('hg.admin')
657 @HasPermissionAllDecorator('hg.admin')
658 def settings_supervisor_log(self, procid):
658 def settings_supervisor_log(self, procid):
659 import rhodecode
659 import rhodecode
660 c.rhodecode_ini = rhodecode.CONFIG
660 c.rhodecode_ini = rhodecode.CONFIG
661 c.active = 'supervisor_tail'
661 c.active = 'supervisor_tail'
662
662
663 supervisor = SupervisorModel()
663 supervisor = SupervisorModel()
664 _connection = supervisor.get_connection(c.rhodecode_ini.get('supervisor.uri'))
664 _connection = supervisor.get_connection(c.rhodecode_ini.get('supervisor.uri'))
665 groupid = c.rhodecode_ini.get('supervisor.group_id')
665 groupid = c.rhodecode_ini.get('supervisor.group_id')
666 procid = groupid + ":" + procid if procid != SUPERVISOR_MASTER else procid
666 procid = groupid + ":" + procid if procid != SUPERVISOR_MASTER else procid
667
667
668 c.log_size = 10240
668 c.log_size = 10240
669 offset = abs(safe_int(request.GET.get('offset', c.log_size))) * -1
669 offset = abs(safe_int(request.GET.get('offset', c.log_size))) * -1
670 c.log = supervisor.read_process_log(_connection, procid, offset, 0)
670 c.log = supervisor.read_process_log(_connection, procid, offset, 0)
671
671
672 return render('admin/settings/settings.html')
672 return render('admin/settings/settings.html')
673
673
674 @HasPermissionAllDecorator('hg.admin')
674 @HasPermissionAllDecorator('hg.admin')
675 @auth.CSRFRequired()
675 @auth.CSRFRequired()
676 def settings_labs_update(self):
676 def settings_labs_update(self):
677 """POST /admin/settings/labs: All items in the collection"""
677 """POST /admin/settings/labs: All items in the collection"""
678 # url('admin_settings/labs', method={'POST'})
678 # url('admin_settings/labs', method={'POST'})
679 c.active = 'labs'
679 c.active = 'labs'
680
680
681 application_form = LabsSettingsForm()()
681 application_form = LabsSettingsForm()()
682 try:
682 try:
683 form_result = application_form.to_python(dict(request.POST))
683 form_result = application_form.to_python(dict(request.POST))
684 except formencode.Invalid as errors:
684 except formencode.Invalid as errors:
685 h.flash(
685 h.flash(
686 _('Some form inputs contain invalid data.'),
686 _('Some form inputs contain invalid data.'),
687 category='error')
687 category='error')
688 return htmlfill.render(
688 return htmlfill.render(
689 render('admin/settings/settings.html'),
689 render('admin/settings/settings.html'),
690 defaults=errors.value,
690 defaults=errors.value,
691 errors=errors.error_dict or {},
691 errors=errors.error_dict or {},
692 prefix_error=False,
692 prefix_error=False,
693 encoding='UTF-8',
693 encoding='UTF-8',
694 force_defaults=False
694 force_defaults=False
695 )
695 )
696
696
697 try:
697 try:
698 session = Session()
698 session = Session()
699 for setting in _LAB_SETTINGS:
699 for setting in _LAB_SETTINGS:
700 setting_name = setting.key[len('rhodecode_'):]
700 setting_name = setting.key[len('rhodecode_'):]
701 sett = SettingsModel().create_or_update_setting(
701 sett = SettingsModel().create_or_update_setting(
702 setting_name, form_result[setting.key], setting.type)
702 setting_name, form_result[setting.key], setting.type)
703 session.add(sett)
703 session.add(sett)
704
704
705 except Exception:
705 except Exception:
706 log.exception('Exception while updating lab settings')
706 log.exception('Exception while updating lab settings')
707 h.flash(_('Error occurred during updating labs settings'),
707 h.flash(_('Error occurred during updating labs settings'),
708 category='error')
708 category='error')
709 else:
709 else:
710 Session().commit()
710 Session().commit()
711 h.flash(_('Updated Labs settings'), category='success')
711 h.flash(_('Updated Labs settings'), category='success')
712 return redirect(url('admin_settings_labs'))
712 return redirect(url('admin_settings_labs'))
713
713
714 return htmlfill.render(
714 return htmlfill.render(
715 render('admin/settings/settings.html'),
715 render('admin/settings/settings.html'),
716 defaults=self._form_defaults(),
716 defaults=self._form_defaults(),
717 encoding='UTF-8',
717 encoding='UTF-8',
718 force_defaults=False)
718 force_defaults=False)
719
719
720 @HasPermissionAllDecorator('hg.admin')
720 @HasPermissionAllDecorator('hg.admin')
721 def settings_labs(self):
721 def settings_labs(self):
722 """GET /admin/settings/labs: All items in the collection"""
722 """GET /admin/settings/labs: All items in the collection"""
723 # url('admin_settings_labs')
723 # url('admin_settings_labs')
724 if not c.labs_active:
724 if not c.labs_active:
725 redirect(url('admin_settings'))
725 redirect(url('admin_settings'))
726
726
727 c.active = 'labs'
727 c.active = 'labs'
728 c.lab_settings = _LAB_SETTINGS
728 c.lab_settings = _LAB_SETTINGS
729
729
730 return htmlfill.render(
730 return htmlfill.render(
731 render('admin/settings/settings.html'),
731 render('admin/settings/settings.html'),
732 defaults=self._form_defaults(),
732 defaults=self._form_defaults(),
733 encoding='UTF-8',
733 encoding='UTF-8',
734 force_defaults=False)
734 force_defaults=False)
735
735
736 def _form_defaults(self):
736 def _form_defaults(self):
737 defaults = SettingsModel().get_all_settings()
737 defaults = SettingsModel().get_all_settings()
738 defaults.update(self._get_hg_ui_settings())
738 defaults.update(self._get_hg_ui_settings())
739 defaults.update({
739 defaults.update({
740 'new_svn_branch': '',
740 'new_svn_branch': '',
741 'new_svn_tag': '',
741 'new_svn_tag': '',
742 })
742 })
743 return defaults
743 return defaults
744
744
745
745
746 # :param key: name of the setting including the 'rhodecode_' prefix
746 # :param key: name of the setting including the 'rhodecode_' prefix
747 # :param type: the RhodeCodeSetting type to use.
747 # :param type: the RhodeCodeSetting type to use.
748 # :param group: the i18ned group in which we should dispaly this setting
748 # :param group: the i18ned group in which we should dispaly this setting
749 # :param label: the i18ned label we should display for this setting
749 # :param label: the i18ned label we should display for this setting
750 # :param help: the i18ned help we should dispaly for this setting
750 # :param help: the i18ned help we should dispaly for this setting
751 LabSetting = collections.namedtuple(
751 LabSetting = collections.namedtuple(
752 'LabSetting', ('key', 'type', 'group', 'label', 'help'))
752 'LabSetting', ('key', 'type', 'group', 'label', 'help'))
753
753
754
754
755 # This list has to be kept in sync with the form
755 # This list has to be kept in sync with the form
756 # rhodecode.model.forms.LabsSettingsForm.
756 # rhodecode.model.forms.LabsSettingsForm.
757 _LAB_SETTINGS = [
757 _LAB_SETTINGS = [
758 LabSetting(
758 LabSetting(
759 key='rhodecode_hg_use_rebase_for_merging',
759 key='rhodecode_hg_use_rebase_for_merging',
760 type='bool',
760 type='bool',
761 group=lazy_ugettext('Mercurial server-side merge'),
761 group=lazy_ugettext('Mercurial server-side merge'),
762 label=lazy_ugettext('Use rebase instead of creating a merge commit when merging via web interface'),
762 label=lazy_ugettext('Use rebase instead of creating a merge commit when merging via web interface'),
763 help='' # Do not translate the empty string!
763 help='' # Do not translate the empty string!
764 ),
764 ),
765 LabSetting(
765 LabSetting(
766 key='rhodecode_proxy_subversion_http_requests',
766 key='rhodecode_proxy_subversion_http_requests',
767 type='bool',
767 type='bool',
768 group=lazy_ugettext('Subversion HTTP Support'),
768 group=lazy_ugettext('Subversion HTTP Support'),
769 label=lazy_ugettext('Proxy subversion HTTP requests'),
769 label=lazy_ugettext('Proxy subversion HTTP requests'),
770 help='' # Do not translate the empty string!
770 help='' # Do not translate the empty string!
771 ),
771 ),
772 LabSetting(
772 LabSetting(
773 key='rhodecode_subversion_http_server_url',
773 key='rhodecode_subversion_http_server_url',
774 type='str',
774 type='str',
775 group=lazy_ugettext('Subversion HTTP Server URL'),
775 group=lazy_ugettext('Subversion HTTP Server URL'),
776 label='', # Do not translate the empty string!
776 label='', # Do not translate the empty string!
777 help=lazy_ugettext('e.g. http://localhost:8080/')
777 help=lazy_ugettext('e.g. http://localhost:8080/')
778 ),
778 ),
779 ]
779 ]
780
780
781
781
782 NavListEntry = collections.namedtuple('NavListEntry', ['key', 'name', 'url'])
782 NavListEntry = collections.namedtuple('NavListEntry', ['key', 'name', 'url'])
783
783
784
784
785 class NavEntry(object):
785 class NavEntry(object):
786
786
787 def __init__(self, key, name, view_name, pyramid=False):
787 def __init__(self, key, name, view_name, pyramid=False):
788 self.key = key
788 self.key = key
789 self.name = name
789 self.name = name
790 self.view_name = view_name
790 self.view_name = view_name
791 self.pyramid = pyramid
791 self.pyramid = pyramid
792
792
793 def generate_url(self, request):
793 def generate_url(self, request):
794 if self.pyramid:
794 if self.pyramid:
795 if hasattr(request, 'route_path'):
795 if hasattr(request, 'route_path'):
796 return request.route_path(self.view_name)
796 return request.route_path(self.view_name)
797 else:
797 else:
798 # TODO: johbo: Remove this after migrating to pyramid.
798 # TODO: johbo: Remove this after migrating to pyramid.
799 # We need the pyramid request here to generate URLs to pyramid
799 # We need the pyramid request here to generate URLs to pyramid
800 # views from within pylons views.
800 # views from within pylons views.
801 from pyramid.threadlocal import get_current_request
801 from pyramid.threadlocal import get_current_request
802 pyramid_request = get_current_request()
802 pyramid_request = get_current_request()
803 return pyramid_request.route_path(self.view_name)
803 return pyramid_request.route_path(self.view_name)
804 else:
804 else:
805 return url(self.view_name)
805 return url(self.view_name)
806
806
807
807
808 class NavigationRegistry(object):
808 class NavigationRegistry(object):
809
809
810 _base_entries = [
810 _base_entries = [
811 NavEntry('global', lazy_ugettext('Global'), 'admin_settings_global'),
811 NavEntry('global', lazy_ugettext('Global'), 'admin_settings_global'),
812 NavEntry('vcs', lazy_ugettext('VCS'), 'admin_settings_vcs'),
812 NavEntry('vcs', lazy_ugettext('VCS'), 'admin_settings_vcs'),
813 NavEntry('visual', lazy_ugettext('Visual'), 'admin_settings_visual'),
813 NavEntry('visual', lazy_ugettext('Visual'), 'admin_settings_visual'),
814 NavEntry('mapping', lazy_ugettext('Remap and Rescan'),
814 NavEntry('mapping', lazy_ugettext('Remap and Rescan'),
815 'admin_settings_mapping'),
815 'admin_settings_mapping'),
816 NavEntry('issuetracker', lazy_ugettext('Issue Tracker'),
816 NavEntry('issuetracker', lazy_ugettext('Issue Tracker'),
817 'admin_settings_issuetracker'),
817 'admin_settings_issuetracker'),
818 NavEntry('email', lazy_ugettext('Email'), 'admin_settings_email'),
818 NavEntry('email', lazy_ugettext('Email'), 'admin_settings_email'),
819 NavEntry('hooks', lazy_ugettext('Hooks'), 'admin_settings_hooks'),
819 NavEntry('hooks', lazy_ugettext('Hooks'), 'admin_settings_hooks'),
820 NavEntry('search', lazy_ugettext('Full Text Search'),
820 NavEntry('search', lazy_ugettext('Full Text Search'),
821 'admin_settings_search'),
821 'admin_settings_search'),
822 NavEntry('system', lazy_ugettext('System Info'),
822 NavEntry('system', lazy_ugettext('System Info'),
823 'admin_settings_system'),
823 'admin_settings_system'),
824 NavEntry('open_source', lazy_ugettext('Open Source Licenses'),
824 NavEntry('open_source', lazy_ugettext('Open Source Licenses'),
825 'admin_settings_open_source'),
825 'admin_settings_open_source', pyramid=True),
826 # TODO: marcink: we disable supervisor now until the supervisor stats
826 # TODO: marcink: we disable supervisor now until the supervisor stats
827 # page is fixed in the nix configuration
827 # page is fixed in the nix configuration
828 # NavEntry('supervisor', lazy_ugettext('Supervisor'),
828 # NavEntry('supervisor', lazy_ugettext('Supervisor'),
829 # 'admin_settings_supervisor'),
829 # 'admin_settings_supervisor'),
830 ]
830 ]
831
831
832 def __init__(self):
832 def __init__(self):
833 self._registered_entries = collections.OrderedDict([
833 self._registered_entries = collections.OrderedDict([
834 (item.key, item) for item in self.__class__._base_entries
834 (item.key, item) for item in self.__class__._base_entries
835 ])
835 ])
836
836
837 # Add the labs entry when it's activated.
837 # Add the labs entry when it's activated.
838 labs_active = str2bool(
838 labs_active = str2bool(
839 rhodecode.CONFIG.get('labs_settings_active', 'false'))
839 rhodecode.CONFIG.get('labs_settings_active', 'false'))
840 if labs_active:
840 if labs_active:
841 self.add_entry(
841 self.add_entry(
842 NavEntry('labs', lazy_ugettext('Labs'), 'admin_settings_labs'))
842 NavEntry('labs', lazy_ugettext('Labs'), 'admin_settings_labs'))
843
843
844 def add_entry(self, entry):
844 def add_entry(self, entry):
845 self._registered_entries[entry.key] = entry
845 self._registered_entries[entry.key] = entry
846
846
847 def get_navlist(self, request):
847 def get_navlist(self, request):
848 navlist = [NavListEntry(i.key, i.name, i.generate_url(request))
848 navlist = [NavListEntry(i.key, i.name, i.generate_url(request))
849 for i in self._registered_entries.values()]
849 for i in self._registered_entries.values()]
850 return navlist
850 return navlist
851
851
852 navigation = NavigationRegistry()
852 navigation = NavigationRegistry()
General Comments 0
You need to be logged in to leave comments. Login now