##// END OF EJS Templates
caches: remove settings cache to use invalidation context as it's slowing down much usage
super-admin -
r4833:54305e88 default
parent child Browse files
Show More
@@ -1,614 +1,619 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import os
21 import os
22 import sys
22 import sys
23 import collections
23 import collections
24 import tempfile
24 import tempfile
25 import time
25 import time
26 import logging.config
26 import logging.config
27
27
28 from paste.gzipper import make_gzip_middleware
28 from paste.gzipper import make_gzip_middleware
29 import pyramid.events
29 import pyramid.events
30 from pyramid.wsgi import wsgiapp
30 from pyramid.wsgi import wsgiapp
31 from pyramid.authorization import ACLAuthorizationPolicy
31 from pyramid.authorization import ACLAuthorizationPolicy
32 from pyramid.config import Configurator
32 from pyramid.config import Configurator
33 from pyramid.settings import asbool, aslist
33 from pyramid.settings import asbool, aslist
34 from pyramid.httpexceptions import (
34 from pyramid.httpexceptions import (
35 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound, HTTPNotFound)
35 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound, HTTPNotFound)
36 from pyramid.renderers import render_to_response
36 from pyramid.renderers import render_to_response
37
37
38 from rhodecode.model import meta
38 from rhodecode.model import meta
39 from rhodecode.config import patches
39 from rhodecode.config import patches
40 from rhodecode.config import utils as config_utils
40 from rhodecode.config import utils as config_utils
41 from rhodecode.config.settings_maker import SettingsMaker
41 from rhodecode.config.settings_maker import SettingsMaker
42 from rhodecode.config.environment import load_pyramid_environment
42 from rhodecode.config.environment import load_pyramid_environment
43
43
44 import rhodecode.events
44 import rhodecode.events
45 from rhodecode.lib.middleware.vcs import VCSMiddleware
45 from rhodecode.lib.middleware.vcs import VCSMiddleware
46 from rhodecode.lib.request import Request
46 from rhodecode.lib.request import Request
47 from rhodecode.lib.vcs import VCSCommunicationError
47 from rhodecode.lib.vcs import VCSCommunicationError
48 from rhodecode.lib.exceptions import VCSServerUnavailable
48 from rhodecode.lib.exceptions import VCSServerUnavailable
49 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
49 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
50 from rhodecode.lib.middleware.https_fixup import HttpsFixup
50 from rhodecode.lib.middleware.https_fixup import HttpsFixup
51 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
51 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
52 from rhodecode.lib.utils2 import AttributeDict
52 from rhodecode.lib.utils2 import AttributeDict
53 from rhodecode.lib.exc_tracking import store_exception
53 from rhodecode.lib.exc_tracking import store_exception
54 from rhodecode.subscribers import (
54 from rhodecode.subscribers import (
55 scan_repositories_if_enabled, write_js_routes_if_enabled,
55 scan_repositories_if_enabled, write_js_routes_if_enabled,
56 write_metadata_if_needed, write_usage_data)
56 write_metadata_if_needed, write_usage_data)
57 from rhodecode.lib.statsd_client import StatsdClient
57 from rhodecode.lib.statsd_client import StatsdClient
58
58
59 log = logging.getLogger(__name__)
59 log = logging.getLogger(__name__)
60
60
61
61
62 def is_http_error(response):
62 def is_http_error(response):
63 # error which should have traceback
63 # error which should have traceback
64 return response.status_code > 499
64 return response.status_code > 499
65
65
66
66
67 def should_load_all():
67 def should_load_all():
68 """
68 """
69 Returns if all application components should be loaded. In some cases it's
69 Returns if all application components should be loaded. In some cases it's
70 desired to skip apps loading for faster shell script execution
70 desired to skip apps loading for faster shell script execution
71 """
71 """
72 ssh_cmd = os.environ.get('RC_CMD_SSH_WRAPPER')
72 ssh_cmd = os.environ.get('RC_CMD_SSH_WRAPPER')
73 if ssh_cmd:
73 if ssh_cmd:
74 return False
74 return False
75
75
76 return True
76 return True
77
77
78
78
79 def make_pyramid_app(global_config, **settings):
79 def make_pyramid_app(global_config, **settings):
80 """
80 """
81 Constructs the WSGI application based on Pyramid.
81 Constructs the WSGI application based on Pyramid.
82
82
83 Specials:
83 Specials:
84
84
85 * The application can also be integrated like a plugin via the call to
85 * The application can also be integrated like a plugin via the call to
86 `includeme`. This is accompanied with the other utility functions which
86 `includeme`. This is accompanied with the other utility functions which
87 are called. Changing this should be done with great care to not break
87 are called. Changing this should be done with great care to not break
88 cases when these fragments are assembled from another place.
88 cases when these fragments are assembled from another place.
89
89
90 """
90 """
91 start_time = time.time()
91 start_time = time.time()
92 log.info('Pyramid app config starting')
92 log.info('Pyramid app config starting')
93
93
94 sanitize_settings_and_apply_defaults(global_config, settings)
94 sanitize_settings_and_apply_defaults(global_config, settings)
95
95
96 # init and bootstrap StatsdClient
96 # init and bootstrap StatsdClient
97 StatsdClient.setup(settings)
97 StatsdClient.setup(settings)
98
98
99 config = Configurator(settings=settings)
99 config = Configurator(settings=settings)
100 # Init our statsd at very start
100 # Init our statsd at very start
101 config.registry.statsd = StatsdClient.statsd
101 config.registry.statsd = StatsdClient.statsd
102
102
103 # Apply compatibility patches
103 # Apply compatibility patches
104 patches.inspect_getargspec()
104 patches.inspect_getargspec()
105
105
106 load_pyramid_environment(global_config, settings)
106 load_pyramid_environment(global_config, settings)
107
107
108 # Static file view comes first
108 # Static file view comes first
109 includeme_first(config)
109 includeme_first(config)
110
110
111 includeme(config)
111 includeme(config)
112
112
113 pyramid_app = config.make_wsgi_app()
113 pyramid_app = config.make_wsgi_app()
114 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
114 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
115 pyramid_app.config = config
115 pyramid_app.config = config
116
116
117 celery_settings = get_celery_config(settings)
117 celery_settings = get_celery_config(settings)
118 config.configure_celery(celery_settings)
118 config.configure_celery(celery_settings)
119
119
120 # creating the app uses a connection - return it after we are done
120 # creating the app uses a connection - return it after we are done
121 meta.Session.remove()
121 meta.Session.remove()
122
122
123 total_time = time.time() - start_time
123 total_time = time.time() - start_time
124 log.info('Pyramid app `%s` created and configured in %.2fs',
124 log.info('Pyramid app `%s` created and configured in %.2fs',
125 getattr(pyramid_app, 'func_name', 'pyramid_app'), total_time)
125 getattr(pyramid_app, 'func_name', 'pyramid_app'), total_time)
126 return pyramid_app
126 return pyramid_app
127
127
128
128
129 def get_celery_config(settings):
129 def get_celery_config(settings):
130 """
130 """
131 Converts basic ini configuration into celery 4.X options
131 Converts basic ini configuration into celery 4.X options
132 """
132 """
133
133
134 def key_converter(key_name):
134 def key_converter(key_name):
135 pref = 'celery.'
135 pref = 'celery.'
136 if key_name.startswith(pref):
136 if key_name.startswith(pref):
137 return key_name[len(pref):].replace('.', '_').lower()
137 return key_name[len(pref):].replace('.', '_').lower()
138
138
139 def type_converter(parsed_key, value):
139 def type_converter(parsed_key, value):
140 # cast to int
140 # cast to int
141 if value.isdigit():
141 if value.isdigit():
142 return int(value)
142 return int(value)
143
143
144 # cast to bool
144 # cast to bool
145 if value.lower() in ['true', 'false', 'True', 'False']:
145 if value.lower() in ['true', 'false', 'True', 'False']:
146 return value.lower() == 'true'
146 return value.lower() == 'true'
147 return value
147 return value
148
148
149 celery_config = {}
149 celery_config = {}
150 for k, v in settings.items():
150 for k, v in settings.items():
151 pref = 'celery.'
151 pref = 'celery.'
152 if k.startswith(pref):
152 if k.startswith(pref):
153 celery_config[key_converter(k)] = type_converter(key_converter(k), v)
153 celery_config[key_converter(k)] = type_converter(key_converter(k), v)
154
154
155 # TODO:rethink if we want to support celerybeat based file config, probably NOT
155 # TODO:rethink if we want to support celerybeat based file config, probably NOT
156 # beat_config = {}
156 # beat_config = {}
157 # for section in parser.sections():
157 # for section in parser.sections():
158 # if section.startswith('celerybeat:'):
158 # if section.startswith('celerybeat:'):
159 # name = section.split(':', 1)[1]
159 # name = section.split(':', 1)[1]
160 # beat_config[name] = get_beat_config(parser, section)
160 # beat_config[name] = get_beat_config(parser, section)
161
161
162 # final compose of settings
162 # final compose of settings
163 celery_settings = {}
163 celery_settings = {}
164
164
165 if celery_config:
165 if celery_config:
166 celery_settings.update(celery_config)
166 celery_settings.update(celery_config)
167 # if beat_config:
167 # if beat_config:
168 # celery_settings.update({'beat_schedule': beat_config})
168 # celery_settings.update({'beat_schedule': beat_config})
169
169
170 return celery_settings
170 return celery_settings
171
171
172
172
173 def not_found_view(request):
173 def not_found_view(request):
174 """
174 """
175 This creates the view which should be registered as not-found-view to
175 This creates the view which should be registered as not-found-view to
176 pyramid.
176 pyramid.
177 """
177 """
178
178
179 if not getattr(request, 'vcs_call', None):
179 if not getattr(request, 'vcs_call', None):
180 # handle like regular case with our error_handler
180 # handle like regular case with our error_handler
181 return error_handler(HTTPNotFound(), request)
181 return error_handler(HTTPNotFound(), request)
182
182
183 # handle not found view as a vcs call
183 # handle not found view as a vcs call
184 settings = request.registry.settings
184 settings = request.registry.settings
185 ae_client = getattr(request, 'ae_client', None)
185 ae_client = getattr(request, 'ae_client', None)
186 vcs_app = VCSMiddleware(
186 vcs_app = VCSMiddleware(
187 HTTPNotFound(), request.registry, settings,
187 HTTPNotFound(), request.registry, settings,
188 appenlight_client=ae_client)
188 appenlight_client=ae_client)
189
189
190 return wsgiapp(vcs_app)(None, request)
190 return wsgiapp(vcs_app)(None, request)
191
191
192
192
193 def error_handler(exception, request):
193 def error_handler(exception, request):
194 import rhodecode
194 import rhodecode
195 from rhodecode.lib import helpers
195 from rhodecode.lib import helpers
196 from rhodecode.lib.utils2 import str2bool
196 from rhodecode.lib.utils2 import str2bool
197
197
198 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
198 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
199
199
200 base_response = HTTPInternalServerError()
200 base_response = HTTPInternalServerError()
201 # prefer original exception for the response since it may have headers set
201 # prefer original exception for the response since it may have headers set
202 if isinstance(exception, HTTPException):
202 if isinstance(exception, HTTPException):
203 base_response = exception
203 base_response = exception
204 elif isinstance(exception, VCSCommunicationError):
204 elif isinstance(exception, VCSCommunicationError):
205 base_response = VCSServerUnavailable()
205 base_response = VCSServerUnavailable()
206
206
207 if is_http_error(base_response):
207 if is_http_error(base_response):
208 log.exception(
208 log.exception(
209 'error occurred handling this request for path: %s', request.path)
209 'error occurred handling this request for path: %s', request.path)
210
210
211 error_explanation = base_response.explanation or str(base_response)
211 error_explanation = base_response.explanation or str(base_response)
212 if base_response.status_code == 404:
212 if base_response.status_code == 404:
213 error_explanation += " Optionally you don't have permission to access this page."
213 error_explanation += " Optionally you don't have permission to access this page."
214 c = AttributeDict()
214 c = AttributeDict()
215 c.error_message = base_response.status
215 c.error_message = base_response.status
216 c.error_explanation = error_explanation
216 c.error_explanation = error_explanation
217 c.visual = AttributeDict()
217 c.visual = AttributeDict()
218
218
219 c.visual.rhodecode_support_url = (
219 c.visual.rhodecode_support_url = (
220 request.registry.settings.get('rhodecode_support_url') or
220 request.registry.settings.get('rhodecode_support_url') or
221 request.route_url('rhodecode_support')
221 request.route_url('rhodecode_support')
222 )
222 )
223 c.redirect_time = 0
223 c.redirect_time = 0
224 c.rhodecode_name = rhodecode_title
224 c.rhodecode_name = rhodecode_title
225 if not c.rhodecode_name:
225 if not c.rhodecode_name:
226 c.rhodecode_name = 'Rhodecode'
226 c.rhodecode_name = 'Rhodecode'
227
227
228 c.causes = []
228 c.causes = []
229 if is_http_error(base_response):
229 if is_http_error(base_response):
230 c.causes.append('Server is overloaded.')
230 c.causes.append('Server is overloaded.')
231 c.causes.append('Server database connection is lost.')
231 c.causes.append('Server database connection is lost.')
232 c.causes.append('Server expected unhandled error.')
232 c.causes.append('Server expected unhandled error.')
233
233
234 if hasattr(base_response, 'causes'):
234 if hasattr(base_response, 'causes'):
235 c.causes = base_response.causes
235 c.causes = base_response.causes
236
236
237 c.messages = helpers.flash.pop_messages(request=request)
237 c.messages = helpers.flash.pop_messages(request=request)
238
238
239 exc_info = sys.exc_info()
239 exc_info = sys.exc_info()
240 c.exception_id = id(exc_info)
240 c.exception_id = id(exc_info)
241 c.show_exception_id = isinstance(base_response, VCSServerUnavailable) \
241 c.show_exception_id = isinstance(base_response, VCSServerUnavailable) \
242 or base_response.status_code > 499
242 or base_response.status_code > 499
243 c.exception_id_url = request.route_url(
243 c.exception_id_url = request.route_url(
244 'admin_settings_exception_tracker_show', exception_id=c.exception_id)
244 'admin_settings_exception_tracker_show', exception_id=c.exception_id)
245
245
246 if c.show_exception_id:
246 if c.show_exception_id:
247 store_exception(c.exception_id, exc_info)
247 store_exception(c.exception_id, exc_info)
248 c.exception_debug = str2bool(rhodecode.CONFIG.get('debug'))
248 c.exception_debug = str2bool(rhodecode.CONFIG.get('debug'))
249 c.exception_config_ini = rhodecode.CONFIG.get('__file__')
249 c.exception_config_ini = rhodecode.CONFIG.get('__file__')
250
250
251 response = render_to_response(
251 response = render_to_response(
252 '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request,
252 '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request,
253 response=base_response)
253 response=base_response)
254
254
255 statsd = request.registry.statsd
255 statsd = request.registry.statsd
256 if statsd and base_response.status_code > 499:
256 if statsd and base_response.status_code > 499:
257 exc_type = "{}.{}".format(exception.__class__.__module__, exception.__class__.__name__)
257 exc_type = "{}.{}".format(exception.__class__.__module__, exception.__class__.__name__)
258 statsd.incr('rhodecode_exception_total',
258 statsd.incr('rhodecode_exception_total',
259 tags=["exc_source:web",
259 tags=["exc_source:web",
260 "http_code:{}".format(base_response.status_code),
260 "http_code:{}".format(base_response.status_code),
261 "type:{}".format(exc_type)])
261 "type:{}".format(exc_type)])
262
262
263 return response
263 return response
264
264
265
265
266 def includeme_first(config):
266 def includeme_first(config):
267 # redirect automatic browser favicon.ico requests to correct place
267 # redirect automatic browser favicon.ico requests to correct place
268 def favicon_redirect(context, request):
268 def favicon_redirect(context, request):
269 return HTTPFound(
269 return HTTPFound(
270 request.static_path('rhodecode:public/images/favicon.ico'))
270 request.static_path('rhodecode:public/images/favicon.ico'))
271
271
272 config.add_view(favicon_redirect, route_name='favicon')
272 config.add_view(favicon_redirect, route_name='favicon')
273 config.add_route('favicon', '/favicon.ico')
273 config.add_route('favicon', '/favicon.ico')
274
274
275 def robots_redirect(context, request):
275 def robots_redirect(context, request):
276 return HTTPFound(
276 return HTTPFound(
277 request.static_path('rhodecode:public/robots.txt'))
277 request.static_path('rhodecode:public/robots.txt'))
278
278
279 config.add_view(robots_redirect, route_name='robots')
279 config.add_view(robots_redirect, route_name='robots')
280 config.add_route('robots', '/robots.txt')
280 config.add_route('robots', '/robots.txt')
281
281
282 config.add_static_view(
282 config.add_static_view(
283 '_static/deform', 'deform:static')
283 '_static/deform', 'deform:static')
284 config.add_static_view(
284 config.add_static_view(
285 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
285 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
286
286
287
287
288 def includeme(config, auth_resources=None):
288 def includeme(config, auth_resources=None):
289 from rhodecode.lib.celerylib.loader import configure_celery
289 from rhodecode.lib.celerylib.loader import configure_celery
290 log.debug('Initializing main includeme from %s', os.path.basename(__file__))
290 log.debug('Initializing main includeme from %s', os.path.basename(__file__))
291 settings = config.registry.settings
291 settings = config.registry.settings
292 config.set_request_factory(Request)
292 config.set_request_factory(Request)
293
293
294 # plugin information
294 # plugin information
295 config.registry.rhodecode_plugins = collections.OrderedDict()
295 config.registry.rhodecode_plugins = collections.OrderedDict()
296
296
297 config.add_directive(
297 config.add_directive(
298 'register_rhodecode_plugin', register_rhodecode_plugin)
298 'register_rhodecode_plugin', register_rhodecode_plugin)
299
299
300 config.add_directive('configure_celery', configure_celery)
300 config.add_directive('configure_celery', configure_celery)
301
301
302 if settings.get('appenlight', False):
302 if settings.get('appenlight', False):
303 config.include('appenlight_client.ext.pyramid_tween')
303 config.include('appenlight_client.ext.pyramid_tween')
304
304
305 load_all = should_load_all()
305 load_all = should_load_all()
306
306
307 # Includes which are required. The application would fail without them.
307 # Includes which are required. The application would fail without them.
308 config.include('pyramid_mako')
308 config.include('pyramid_mako')
309 config.include('rhodecode.lib.rc_beaker')
309 config.include('rhodecode.lib.rc_beaker')
310 config.include('rhodecode.lib.rc_cache')
310 config.include('rhodecode.lib.rc_cache')
311 config.include('rhodecode.apps._base.navigation')
311 config.include('rhodecode.apps._base.navigation')
312 config.include('rhodecode.apps._base.subscribers')
312 config.include('rhodecode.apps._base.subscribers')
313 config.include('rhodecode.tweens')
313 config.include('rhodecode.tweens')
314 config.include('rhodecode.authentication')
314 config.include('rhodecode.authentication')
315
315
316 if load_all:
316 if load_all:
317 ce_auth_resources = [
317 ce_auth_resources = [
318 'rhodecode.authentication.plugins.auth_crowd',
318 'rhodecode.authentication.plugins.auth_crowd',
319 'rhodecode.authentication.plugins.auth_headers',
319 'rhodecode.authentication.plugins.auth_headers',
320 'rhodecode.authentication.plugins.auth_jasig_cas',
320 'rhodecode.authentication.plugins.auth_jasig_cas',
321 'rhodecode.authentication.plugins.auth_ldap',
321 'rhodecode.authentication.plugins.auth_ldap',
322 'rhodecode.authentication.plugins.auth_pam',
322 'rhodecode.authentication.plugins.auth_pam',
323 'rhodecode.authentication.plugins.auth_rhodecode',
323 'rhodecode.authentication.plugins.auth_rhodecode',
324 'rhodecode.authentication.plugins.auth_token',
324 'rhodecode.authentication.plugins.auth_token',
325 ]
325 ]
326
326
327 # load CE authentication plugins
327 # load CE authentication plugins
328
328
329 if auth_resources:
329 if auth_resources:
330 ce_auth_resources.extend(auth_resources)
330 ce_auth_resources.extend(auth_resources)
331
331
332 for resource in ce_auth_resources:
332 for resource in ce_auth_resources:
333 config.include(resource)
333 config.include(resource)
334
334
335 # Auto discover authentication plugins and include their configuration.
335 # Auto discover authentication plugins and include their configuration.
336 if asbool(settings.get('auth_plugin.import_legacy_plugins', 'true')):
336 if asbool(settings.get('auth_plugin.import_legacy_plugins', 'true')):
337 from rhodecode.authentication import discover_legacy_plugins
337 from rhodecode.authentication import discover_legacy_plugins
338 discover_legacy_plugins(config)
338 discover_legacy_plugins(config)
339
339
340 # apps
340 # apps
341 if load_all:
341 if load_all:
342 config.include('rhodecode.api')
342 config.include('rhodecode.api')
343 config.include('rhodecode.apps._base')
343 config.include('rhodecode.apps._base')
344 config.include('rhodecode.apps.hovercards')
344 config.include('rhodecode.apps.hovercards')
345 config.include('rhodecode.apps.ops')
345 config.include('rhodecode.apps.ops')
346 config.include('rhodecode.apps.channelstream')
346 config.include('rhodecode.apps.channelstream')
347 config.include('rhodecode.apps.file_store')
347 config.include('rhodecode.apps.file_store')
348 config.include('rhodecode.apps.admin')
348 config.include('rhodecode.apps.admin')
349 config.include('rhodecode.apps.login')
349 config.include('rhodecode.apps.login')
350 config.include('rhodecode.apps.home')
350 config.include('rhodecode.apps.home')
351 config.include('rhodecode.apps.journal')
351 config.include('rhodecode.apps.journal')
352
352
353 config.include('rhodecode.apps.repository')
353 config.include('rhodecode.apps.repository')
354 config.include('rhodecode.apps.repo_group')
354 config.include('rhodecode.apps.repo_group')
355 config.include('rhodecode.apps.user_group')
355 config.include('rhodecode.apps.user_group')
356 config.include('rhodecode.apps.search')
356 config.include('rhodecode.apps.search')
357 config.include('rhodecode.apps.user_profile')
357 config.include('rhodecode.apps.user_profile')
358 config.include('rhodecode.apps.user_group_profile')
358 config.include('rhodecode.apps.user_group_profile')
359 config.include('rhodecode.apps.my_account')
359 config.include('rhodecode.apps.my_account')
360 config.include('rhodecode.apps.gist')
360 config.include('rhodecode.apps.gist')
361
361
362 config.include('rhodecode.apps.svn_support')
362 config.include('rhodecode.apps.svn_support')
363 config.include('rhodecode.apps.ssh_support')
363 config.include('rhodecode.apps.ssh_support')
364 config.include('rhodecode.apps.debug_style')
364 config.include('rhodecode.apps.debug_style')
365
365
366 if load_all:
366 if load_all:
367 config.include('rhodecode.integrations')
367 config.include('rhodecode.integrations')
368
368
369 config.add_route('rhodecode_support', 'https://rhodecode.com/help/', static=True)
369 config.add_route('rhodecode_support', 'https://rhodecode.com/help/', static=True)
370 config.add_translation_dirs('rhodecode:i18n/')
370 config.add_translation_dirs('rhodecode:i18n/')
371 settings['default_locale_name'] = settings.get('lang', 'en')
371 settings['default_locale_name'] = settings.get('lang', 'en')
372
372
373 # Add subscribers.
373 # Add subscribers.
374 if load_all:
374 if load_all:
375 config.add_subscriber(scan_repositories_if_enabled,
375 config.add_subscriber(scan_repositories_if_enabled,
376 pyramid.events.ApplicationCreated)
376 pyramid.events.ApplicationCreated)
377 config.add_subscriber(write_metadata_if_needed,
377 config.add_subscriber(write_metadata_if_needed,
378 pyramid.events.ApplicationCreated)
378 pyramid.events.ApplicationCreated)
379 config.add_subscriber(write_usage_data,
379 config.add_subscriber(write_usage_data,
380 pyramid.events.ApplicationCreated)
380 pyramid.events.ApplicationCreated)
381 config.add_subscriber(write_js_routes_if_enabled,
381 config.add_subscriber(write_js_routes_if_enabled,
382 pyramid.events.ApplicationCreated)
382 pyramid.events.ApplicationCreated)
383
383
384 # request custom methods
384 # request custom methods
385 config.add_request_method(
385 config.add_request_method(
386 'rhodecode.lib.partial_renderer.get_partial_renderer',
386 'rhodecode.lib.partial_renderer.get_partial_renderer',
387 'get_partial_renderer')
387 'get_partial_renderer')
388
388
389 config.add_request_method(
389 config.add_request_method(
390 'rhodecode.lib.request_counter.get_request_counter',
390 'rhodecode.lib.request_counter.get_request_counter',
391 'request_count')
391 'request_count')
392
392
393 # Set the authorization policy.
393 # Set the authorization policy.
394 authz_policy = ACLAuthorizationPolicy()
394 authz_policy = ACLAuthorizationPolicy()
395 config.set_authorization_policy(authz_policy)
395 config.set_authorization_policy(authz_policy)
396
396
397 # Set the default renderer for HTML templates to mako.
397 # Set the default renderer for HTML templates to mako.
398 config.add_mako_renderer('.html')
398 config.add_mako_renderer('.html')
399
399
400 config.add_renderer(
400 config.add_renderer(
401 name='json_ext',
401 name='json_ext',
402 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
402 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
403
403
404 config.add_renderer(
404 config.add_renderer(
405 name='string_html',
405 name='string_html',
406 factory='rhodecode.lib.string_renderer.html')
406 factory='rhodecode.lib.string_renderer.html')
407
407
408 # include RhodeCode plugins
408 # include RhodeCode plugins
409 includes = aslist(settings.get('rhodecode.includes', []))
409 includes = aslist(settings.get('rhodecode.includes', []))
410 for inc in includes:
410 for inc in includes:
411 config.include(inc)
411 config.include(inc)
412
412
413 # custom not found view, if our pyramid app doesn't know how to handle
413 # custom not found view, if our pyramid app doesn't know how to handle
414 # the request pass it to potential VCS handling ap
414 # the request pass it to potential VCS handling ap
415 config.add_notfound_view(not_found_view)
415 config.add_notfound_view(not_found_view)
416 if not settings.get('debugtoolbar.enabled', False):
416 if not settings.get('debugtoolbar.enabled', False):
417 # disabled debugtoolbar handle all exceptions via the error_handlers
417 # disabled debugtoolbar handle all exceptions via the error_handlers
418 config.add_view(error_handler, context=Exception)
418 config.add_view(error_handler, context=Exception)
419
419
420 # all errors including 403/404/50X
420 # all errors including 403/404/50X
421 config.add_view(error_handler, context=HTTPError)
421 config.add_view(error_handler, context=HTTPError)
422
422
423
423
424 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
424 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
425 """
425 """
426 Apply outer WSGI middlewares around the application.
426 Apply outer WSGI middlewares around the application.
427 """
427 """
428 registry = config.registry
428 registry = config.registry
429 settings = registry.settings
429 settings = registry.settings
430
430
431 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
431 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
432 pyramid_app = HttpsFixup(pyramid_app, settings)
432 pyramid_app = HttpsFixup(pyramid_app, settings)
433
433
434 pyramid_app, _ae_client = wrap_in_appenlight_if_enabled(
434 pyramid_app, _ae_client = wrap_in_appenlight_if_enabled(
435 pyramid_app, settings)
435 pyramid_app, settings)
436 registry.ae_client = _ae_client
436 registry.ae_client = _ae_client
437
437
438 if settings['gzip_responses']:
438 if settings['gzip_responses']:
439 pyramid_app = make_gzip_middleware(
439 pyramid_app = make_gzip_middleware(
440 pyramid_app, settings, compress_level=1)
440 pyramid_app, settings, compress_level=1)
441
441
442 # this should be the outer most middleware in the wsgi stack since
442 # this should be the outer most middleware in the wsgi stack since
443 # middleware like Routes make database calls
443 # middleware like Routes make database calls
444 def pyramid_app_with_cleanup(environ, start_response):
444 def pyramid_app_with_cleanup(environ, start_response):
445 try:
445 try:
446 return pyramid_app(environ, start_response)
446 return pyramid_app(environ, start_response)
447 finally:
447 finally:
448 # Dispose current database session and rollback uncommitted
448 # Dispose current database session and rollback uncommitted
449 # transactions.
449 # transactions.
450 meta.Session.remove()
450 meta.Session.remove()
451
451
452 # In a single threaded mode server, on non sqlite db we should have
452 # In a single threaded mode server, on non sqlite db we should have
453 # '0 Current Checked out connections' at the end of a request,
453 # '0 Current Checked out connections' at the end of a request,
454 # if not, then something, somewhere is leaving a connection open
454 # if not, then something, somewhere is leaving a connection open
455 pool = meta.Base.metadata.bind.engine.pool
455 pool = meta.Base.metadata.bind.engine.pool
456 log.debug('sa pool status: %s', pool.status())
456 log.debug('sa pool status: %s', pool.status())
457 log.debug('Request processing finalized')
457 log.debug('Request processing finalized')
458
458
459 return pyramid_app_with_cleanup
459 return pyramid_app_with_cleanup
460
460
461
461
462 def sanitize_settings_and_apply_defaults(global_config, settings):
462 def sanitize_settings_and_apply_defaults(global_config, settings):
463 """
463 """
464 Applies settings defaults and does all type conversion.
464 Applies settings defaults and does all type conversion.
465
465
466 We would move all settings parsing and preparation into this place, so that
466 We would move all settings parsing and preparation into this place, so that
467 we have only one place left which deals with this part. The remaining parts
467 we have only one place left which deals with this part. The remaining parts
468 of the application would start to rely fully on well prepared settings.
468 of the application would start to rely fully on well prepared settings.
469
469
470 This piece would later be split up per topic to avoid a big fat monster
470 This piece would later be split up per topic to avoid a big fat monster
471 function.
471 function.
472 """
472 """
473
473
474 global_settings_maker = SettingsMaker(global_config)
474 global_settings_maker = SettingsMaker(global_config)
475 global_settings_maker.make_setting('debug', default=False, parser='bool')
475 global_settings_maker.make_setting('debug', default=False, parser='bool')
476 debug_enabled = asbool(global_config.get('debug'))
476 debug_enabled = asbool(global_config.get('debug'))
477
477
478 settings_maker = SettingsMaker(settings)
478 settings_maker = SettingsMaker(settings)
479
479
480 settings_maker.make_setting(
480 settings_maker.make_setting(
481 'logging.autoconfigure',
481 'logging.autoconfigure',
482 default=True,
482 default=True,
483 parser='bool')
483 parser='bool')
484
484
485 logging_conf = os.path.join(os.path.dirname(global_config.get('__file__')), 'logging.ini')
485 logging_conf = os.path.join(os.path.dirname(global_config.get('__file__')), 'logging.ini')
486 settings_maker.enable_logging(logging_conf, level='INFO' if debug_enabled else 'DEBUG')
486 settings_maker.enable_logging(logging_conf, level='INFO' if debug_enabled else 'DEBUG')
487
487
488 # Default includes, possible to change as a user
488 # Default includes, possible to change as a user
489 pyramid_includes = settings_maker.make_setting('pyramid.includes', [], parser='list:newline')
489 pyramid_includes = settings_maker.make_setting('pyramid.includes', [], parser='list:newline')
490 log.debug(
490 log.debug(
491 "Using the following pyramid.includes: %s",
491 "Using the following pyramid.includes: %s",
492 pyramid_includes)
492 pyramid_includes)
493
493
494 settings_maker.make_setting('rhodecode.edition', 'Community Edition')
494 settings_maker.make_setting('rhodecode.edition', 'Community Edition')
495 settings_maker.make_setting('rhodecode.edition_id', 'CE')
495 settings_maker.make_setting('rhodecode.edition_id', 'CE')
496
496
497 if 'mako.default_filters' not in settings:
497 if 'mako.default_filters' not in settings:
498 # set custom default filters if we don't have it defined
498 # set custom default filters if we don't have it defined
499 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
499 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
500 settings['mako.default_filters'] = 'h_filter'
500 settings['mako.default_filters'] = 'h_filter'
501
501
502 if 'mako.directories' not in settings:
502 if 'mako.directories' not in settings:
503 mako_directories = settings.setdefault('mako.directories', [
503 mako_directories = settings.setdefault('mako.directories', [
504 # Base templates of the original application
504 # Base templates of the original application
505 'rhodecode:templates',
505 'rhodecode:templates',
506 ])
506 ])
507 log.debug(
507 log.debug(
508 "Using the following Mako template directories: %s",
508 "Using the following Mako template directories: %s",
509 mako_directories)
509 mako_directories)
510
510
511 # NOTE(marcink): fix redis requirement for schema of connection since 3.X
511 # NOTE(marcink): fix redis requirement for schema of connection since 3.X
512 if 'beaker.session.type' in settings and settings['beaker.session.type'] == 'ext:redis':
512 if 'beaker.session.type' in settings and settings['beaker.session.type'] == 'ext:redis':
513 raw_url = settings['beaker.session.url']
513 raw_url = settings['beaker.session.url']
514 if not raw_url.startswith(('redis://', 'rediss://', 'unix://')):
514 if not raw_url.startswith(('redis://', 'rediss://', 'unix://')):
515 settings['beaker.session.url'] = 'redis://' + raw_url
515 settings['beaker.session.url'] = 'redis://' + raw_url
516
516
517 settings_maker.make_setting('__file__', global_config.get('__file__'))
517 settings_maker.make_setting('__file__', global_config.get('__file__'))
518
518
519 # TODO: johbo: Re-think this, usually the call to config.include
519 # TODO: johbo: Re-think this, usually the call to config.include
520 # should allow to pass in a prefix.
520 # should allow to pass in a prefix.
521 settings_maker.make_setting('rhodecode.api.url', '/_admin/api')
521 settings_maker.make_setting('rhodecode.api.url', '/_admin/api')
522
522
523 # Sanitize generic settings.
523 # Sanitize generic settings.
524 settings_maker.make_setting('default_encoding', 'UTF-8', parser='list')
524 settings_maker.make_setting('default_encoding', 'UTF-8', parser='list')
525 settings_maker.make_setting('is_test', False, parser='bool')
525 settings_maker.make_setting('is_test', False, parser='bool')
526 settings_maker.make_setting('gzip_responses', False, parser='bool')
526 settings_maker.make_setting('gzip_responses', False, parser='bool')
527
527
528 # statsd
528 # statsd
529 settings_maker.make_setting('statsd.enabled', False, parser='bool')
529 settings_maker.make_setting('statsd.enabled', False, parser='bool')
530 settings_maker.make_setting('statsd.statsd_host', 'statsd-exporter', parser='string')
530 settings_maker.make_setting('statsd.statsd_host', 'statsd-exporter', parser='string')
531 settings_maker.make_setting('statsd.statsd_port', 9125, parser='int')
531 settings_maker.make_setting('statsd.statsd_port', 9125, parser='int')
532 settings_maker.make_setting('statsd.statsd_prefix', '')
532 settings_maker.make_setting('statsd.statsd_prefix', '')
533 settings_maker.make_setting('statsd.statsd_ipv6', False, parser='bool')
533 settings_maker.make_setting('statsd.statsd_ipv6', False, parser='bool')
534
534
535 settings_maker.make_setting('vcs.svn.compatible_version', '')
535 settings_maker.make_setting('vcs.svn.compatible_version', '')
536 settings_maker.make_setting('vcs.hooks.protocol', 'http')
536 settings_maker.make_setting('vcs.hooks.protocol', 'http')
537 settings_maker.make_setting('vcs.hooks.host', '127.0.0.1')
537 settings_maker.make_setting('vcs.hooks.host', '127.0.0.1')
538 settings_maker.make_setting('vcs.scm_app_implementation', 'http')
538 settings_maker.make_setting('vcs.scm_app_implementation', 'http')
539 settings_maker.make_setting('vcs.server', '')
539 settings_maker.make_setting('vcs.server', '')
540 settings_maker.make_setting('vcs.server.protocol', 'http')
540 settings_maker.make_setting('vcs.server.protocol', 'http')
541 settings_maker.make_setting('startup.import_repos', 'false', parser='bool')
541 settings_maker.make_setting('startup.import_repos', 'false', parser='bool')
542 settings_maker.make_setting('vcs.hooks.direct_calls', 'false', parser='bool')
542 settings_maker.make_setting('vcs.hooks.direct_calls', 'false', parser='bool')
543 settings_maker.make_setting('vcs.server.enable', 'true', parser='bool')
543 settings_maker.make_setting('vcs.server.enable', 'true', parser='bool')
544 settings_maker.make_setting('vcs.start_server', 'false', parser='bool')
544 settings_maker.make_setting('vcs.start_server', 'false', parser='bool')
545 settings_maker.make_setting('vcs.backends', 'hg, git, svn', parser='list')
545 settings_maker.make_setting('vcs.backends', 'hg, git, svn', parser='list')
546 settings_maker.make_setting('vcs.connection_timeout', 3600, parser='int')
546 settings_maker.make_setting('vcs.connection_timeout', 3600, parser='int')
547
547
548 settings_maker.make_setting('vcs.methods.cache', True, parser='bool')
548 settings_maker.make_setting('vcs.methods.cache', True, parser='bool')
549
549
550 # Support legacy values of vcs.scm_app_implementation. Legacy
550 # Support legacy values of vcs.scm_app_implementation. Legacy
551 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http', or
551 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http', or
552 # disabled since 4.13 'vcsserver.scm_app' which is now mapped to 'http'.
552 # disabled since 4.13 'vcsserver.scm_app' which is now mapped to 'http'.
553 scm_app_impl = settings['vcs.scm_app_implementation']
553 scm_app_impl = settings['vcs.scm_app_implementation']
554 if scm_app_impl in ['rhodecode.lib.middleware.utils.scm_app_http', 'vcsserver.scm_app']:
554 if scm_app_impl in ['rhodecode.lib.middleware.utils.scm_app_http', 'vcsserver.scm_app']:
555 settings['vcs.scm_app_implementation'] = 'http'
555 settings['vcs.scm_app_implementation'] = 'http'
556
556
557 settings_maker.make_setting('appenlight', False, parser='bool')
557 settings_maker.make_setting('appenlight', False, parser='bool')
558
558
559 temp_store = tempfile.gettempdir()
559 temp_store = tempfile.gettempdir()
560 default_cache_dir = os.path.join(temp_store, 'rc_cache')
560 default_cache_dir = os.path.join(temp_store, 'rc_cache')
561
561
562 # save default, cache dir, and use it for all backends later.
562 # save default, cache dir, and use it for all backends later.
563 default_cache_dir = settings_maker.make_setting(
563 default_cache_dir = settings_maker.make_setting(
564 'cache_dir',
564 'cache_dir',
565 default=default_cache_dir, default_when_empty=True,
565 default=default_cache_dir, default_when_empty=True,
566 parser='dir:ensured')
566 parser='dir:ensured')
567
567
568 # exception store cache
568 # exception store cache
569 settings_maker.make_setting(
569 settings_maker.make_setting(
570 'exception_tracker.store_path',
570 'exception_tracker.store_path',
571 default=os.path.join(default_cache_dir, 'exc_store'), default_when_empty=True,
571 default=os.path.join(default_cache_dir, 'exc_store'), default_when_empty=True,
572 parser='dir:ensured'
572 parser='dir:ensured'
573 )
573 )
574
574
575 settings_maker.make_setting(
575 settings_maker.make_setting(
576 'celerybeat-schedule.path',
576 'celerybeat-schedule.path',
577 default=os.path.join(default_cache_dir, 'celerybeat_schedule', 'celerybeat-schedule.db'), default_when_empty=True,
577 default=os.path.join(default_cache_dir, 'celerybeat_schedule', 'celerybeat-schedule.db'), default_when_empty=True,
578 parser='file:ensured'
578 parser='file:ensured'
579 )
579 )
580
580
581 settings_maker.make_setting('exception_tracker.send_email', False, parser='bool')
581 settings_maker.make_setting('exception_tracker.send_email', False, parser='bool')
582 settings_maker.make_setting('exception_tracker.email_prefix', '[RHODECODE ERROR]', default_when_empty=True)
582 settings_maker.make_setting('exception_tracker.email_prefix', '[RHODECODE ERROR]', default_when_empty=True)
583
583
584 # cache_general
585 settings_maker.make_setting('rc_cache.cache_general.backend', 'dogpile.cache.rc.file_namespace')
586 settings_maker.make_setting('rc_cache.cache_general.expiration_time', 60 * 60 * 12, parser='int')
587 settings_maker.make_setting('rc_cache.cache_general.arguments.filename', os.path.join(default_cache_dir, 'rhodecode_cache_general.db'))
588
584 # cache_perms
589 # cache_perms
585 settings_maker.make_setting('rc_cache.cache_perms.backend', 'dogpile.cache.rc.file_namespace')
590 settings_maker.make_setting('rc_cache.cache_perms.backend', 'dogpile.cache.rc.file_namespace')
586 settings_maker.make_setting('rc_cache.cache_perms.expiration_time', 60, parser='int')
591 settings_maker.make_setting('rc_cache.cache_perms.expiration_time', 60 * 60, parser='int')
587 settings_maker.make_setting('rc_cache.cache_perms.arguments.filename', os.path.join(default_cache_dir, 'rhodecode_cache_perms.db'))
592 settings_maker.make_setting('rc_cache.cache_perms.arguments.filename', os.path.join(default_cache_dir, 'rhodecode_cache_perms.db'))
588
593
589 # cache_repo
594 # cache_repo
590 settings_maker.make_setting('rc_cache.cache_repo.backend', 'dogpile.cache.rc.file_namespace')
595 settings_maker.make_setting('rc_cache.cache_repo.backend', 'dogpile.cache.rc.file_namespace')
591 settings_maker.make_setting('rc_cache.cache_repo.expiration_time', 60, parser='int')
596 settings_maker.make_setting('rc_cache.cache_repo.expiration_time', 60 * 60 * 24 * 30, parser='int')
592 settings_maker.make_setting('rc_cache.cache_repo.arguments.filename', os.path.join(default_cache_dir, 'rhodecode_cache_repo.db'))
597 settings_maker.make_setting('rc_cache.cache_repo.arguments.filename', os.path.join(default_cache_dir, 'rhodecode_cache_repo.db'))
593
598
594 # cache_license
599 # cache_license
595 settings_maker.make_setting('rc_cache.cache_license.backend', 'dogpile.cache.rc.file_namespace')
600 settings_maker.make_setting('rc_cache.cache_license.backend', 'dogpile.cache.rc.file_namespace')
596 settings_maker.make_setting('rc_cache.cache_license.expiration_time', 5*60, parser='int')
601 settings_maker.make_setting('rc_cache.cache_license.expiration_time', 60 * 5, parser='int')
597 settings_maker.make_setting('rc_cache.cache_license.arguments.filename', os.path.join(default_cache_dir, 'rhodecode_cache_license.db'))
602 settings_maker.make_setting('rc_cache.cache_license.arguments.filename', os.path.join(default_cache_dir, 'rhodecode_cache_license.db'))
598
603
599 # cache_repo_longterm memory, 96H
604 # cache_repo_longterm memory, 96H
600 settings_maker.make_setting('rc_cache.cache_repo_longterm.backend', 'dogpile.cache.rc.memory_lru')
605 settings_maker.make_setting('rc_cache.cache_repo_longterm.backend', 'dogpile.cache.rc.memory_lru')
601 settings_maker.make_setting('rc_cache.cache_repo_longterm.expiration_time', 345600, parser='int')
606 settings_maker.make_setting('rc_cache.cache_repo_longterm.expiration_time', 345600, parser='int')
602 settings_maker.make_setting('rc_cache.cache_repo_longterm.max_size', 10000, parser='int')
607 settings_maker.make_setting('rc_cache.cache_repo_longterm.max_size', 10000, parser='int')
603
608
604 # sql_cache_short
609 # sql_cache_short
605 settings_maker.make_setting('rc_cache.sql_cache_short.backend', 'dogpile.cache.rc.memory_lru')
610 settings_maker.make_setting('rc_cache.sql_cache_short.backend', 'dogpile.cache.rc.memory_lru')
606 settings_maker.make_setting('rc_cache.sql_cache_short.expiration_time', 30, parser='int')
611 settings_maker.make_setting('rc_cache.sql_cache_short.expiration_time', 30, parser='int')
607 settings_maker.make_setting('rc_cache.sql_cache_short.max_size', 10000, parser='int')
612 settings_maker.make_setting('rc_cache.sql_cache_short.max_size', 10000, parser='int')
608
613
609 settings_maker.env_expand()
614 settings_maker.env_expand()
610
615
611 # configure instance id
616 # configure instance id
612 config_utils.set_instance_id(settings)
617 config_utils.set_instance_id(settings)
613
618
614 return settings
619 return settings
@@ -1,920 +1,918 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import os
21 import os
22 import hashlib
22 import hashlib
23 import logging
23 import logging
24 import re
24 import re
25 from collections import namedtuple
25 from collections import namedtuple
26 from functools import wraps
26 from functools import wraps
27 import bleach
27 import bleach
28 from pyramid.threadlocal import get_current_request, get_current_registry
28 from pyramid.threadlocal import get_current_request, get_current_registry
29
29
30 from rhodecode.lib import rc_cache
30 from rhodecode.lib import rc_cache
31 from rhodecode.lib.utils2 import (
31 from rhodecode.lib.utils2 import (
32 Optional, AttributeDict, safe_str, remove_prefix, str2bool)
32 Optional, AttributeDict, safe_str, remove_prefix, str2bool)
33 from rhodecode.lib.vcs.backends import base
33 from rhodecode.lib.vcs.backends import base
34 from rhodecode.lib.statsd_client import StatsdClient
34 from rhodecode.model import BaseModel
35 from rhodecode.model import BaseModel
35 from rhodecode.model.db import (
36 from rhodecode.model.db import (
36 RepoRhodeCodeUi, RepoRhodeCodeSetting, RhodeCodeUi, RhodeCodeSetting, CacheKey)
37 RepoRhodeCodeUi, RepoRhodeCodeSetting, RhodeCodeUi, RhodeCodeSetting, CacheKey)
37 from rhodecode.model.meta import Session
38 from rhodecode.model.meta import Session
38
39
39
40
40 log = logging.getLogger(__name__)
41 log = logging.getLogger(__name__)
41
42
42
43
43 UiSetting = namedtuple(
44 UiSetting = namedtuple(
44 'UiSetting', ['section', 'key', 'value', 'active'])
45 'UiSetting', ['section', 'key', 'value', 'active'])
45
46
46 SOCIAL_PLUGINS_LIST = ['github', 'bitbucket', 'twitter', 'google']
47 SOCIAL_PLUGINS_LIST = ['github', 'bitbucket', 'twitter', 'google']
47
48
48
49
49 class SettingNotFound(Exception):
50 class SettingNotFound(Exception):
50 def __init__(self, setting_id):
51 def __init__(self, setting_id):
51 msg = 'Setting `{}` is not found'.format(setting_id)
52 msg = 'Setting `{}` is not found'.format(setting_id)
52 super(SettingNotFound, self).__init__(msg)
53 super(SettingNotFound, self).__init__(msg)
53
54
54
55
55 class SettingsModel(BaseModel):
56 class SettingsModel(BaseModel):
56 BUILTIN_HOOKS = (
57 BUILTIN_HOOKS = (
57 RhodeCodeUi.HOOK_REPO_SIZE, RhodeCodeUi.HOOK_PUSH,
58 RhodeCodeUi.HOOK_REPO_SIZE, RhodeCodeUi.HOOK_PUSH,
58 RhodeCodeUi.HOOK_PRE_PUSH, RhodeCodeUi.HOOK_PRETX_PUSH,
59 RhodeCodeUi.HOOK_PRE_PUSH, RhodeCodeUi.HOOK_PRETX_PUSH,
59 RhodeCodeUi.HOOK_PULL, RhodeCodeUi.HOOK_PRE_PULL,
60 RhodeCodeUi.HOOK_PULL, RhodeCodeUi.HOOK_PRE_PULL,
60 RhodeCodeUi.HOOK_PUSH_KEY,)
61 RhodeCodeUi.HOOK_PUSH_KEY,)
61 HOOKS_SECTION = 'hooks'
62 HOOKS_SECTION = 'hooks'
62
63
63 def __init__(self, sa=None, repo=None):
64 def __init__(self, sa=None, repo=None):
64 self.repo = repo
65 self.repo = repo
65 self.UiDbModel = RepoRhodeCodeUi if repo else RhodeCodeUi
66 self.UiDbModel = RepoRhodeCodeUi if repo else RhodeCodeUi
66 self.SettingsDbModel = (
67 self.SettingsDbModel = (
67 RepoRhodeCodeSetting if repo else RhodeCodeSetting)
68 RepoRhodeCodeSetting if repo else RhodeCodeSetting)
68 super(SettingsModel, self).__init__(sa)
69 super(SettingsModel, self).__init__(sa)
69
70
70 def get_ui_by_key(self, key):
71 def get_ui_by_key(self, key):
71 q = self.UiDbModel.query()
72 q = self.UiDbModel.query()
72 q = q.filter(self.UiDbModel.ui_key == key)
73 q = q.filter(self.UiDbModel.ui_key == key)
73 q = self._filter_by_repo(RepoRhodeCodeUi, q)
74 q = self._filter_by_repo(RepoRhodeCodeUi, q)
74 return q.scalar()
75 return q.scalar()
75
76
76 def get_ui_by_section(self, section):
77 def get_ui_by_section(self, section):
77 q = self.UiDbModel.query()
78 q = self.UiDbModel.query()
78 q = q.filter(self.UiDbModel.ui_section == section)
79 q = q.filter(self.UiDbModel.ui_section == section)
79 q = self._filter_by_repo(RepoRhodeCodeUi, q)
80 q = self._filter_by_repo(RepoRhodeCodeUi, q)
80 return q.all()
81 return q.all()
81
82
82 def get_ui_by_section_and_key(self, section, key):
83 def get_ui_by_section_and_key(self, section, key):
83 q = self.UiDbModel.query()
84 q = self.UiDbModel.query()
84 q = q.filter(self.UiDbModel.ui_section == section)
85 q = q.filter(self.UiDbModel.ui_section == section)
85 q = q.filter(self.UiDbModel.ui_key == key)
86 q = q.filter(self.UiDbModel.ui_key == key)
86 q = self._filter_by_repo(RepoRhodeCodeUi, q)
87 q = self._filter_by_repo(RepoRhodeCodeUi, q)
87 return q.scalar()
88 return q.scalar()
88
89
89 def get_ui(self, section=None, key=None):
90 def get_ui(self, section=None, key=None):
90 q = self.UiDbModel.query()
91 q = self.UiDbModel.query()
91 q = self._filter_by_repo(RepoRhodeCodeUi, q)
92 q = self._filter_by_repo(RepoRhodeCodeUi, q)
92
93
93 if section:
94 if section:
94 q = q.filter(self.UiDbModel.ui_section == section)
95 q = q.filter(self.UiDbModel.ui_section == section)
95 if key:
96 if key:
96 q = q.filter(self.UiDbModel.ui_key == key)
97 q = q.filter(self.UiDbModel.ui_key == key)
97
98
98 # TODO: mikhail: add caching
99 # TODO: mikhail: add caching
99 result = [
100 result = [
100 UiSetting(
101 UiSetting(
101 section=safe_str(r.ui_section), key=safe_str(r.ui_key),
102 section=safe_str(r.ui_section), key=safe_str(r.ui_key),
102 value=safe_str(r.ui_value), active=r.ui_active
103 value=safe_str(r.ui_value), active=r.ui_active
103 )
104 )
104 for r in q.all()
105 for r in q.all()
105 ]
106 ]
106 return result
107 return result
107
108
108 def get_builtin_hooks(self):
109 def get_builtin_hooks(self):
109 q = self.UiDbModel.query()
110 q = self.UiDbModel.query()
110 q = q.filter(self.UiDbModel.ui_key.in_(self.BUILTIN_HOOKS))
111 q = q.filter(self.UiDbModel.ui_key.in_(self.BUILTIN_HOOKS))
111 return self._get_hooks(q)
112 return self._get_hooks(q)
112
113
113 def get_custom_hooks(self):
114 def get_custom_hooks(self):
114 q = self.UiDbModel.query()
115 q = self.UiDbModel.query()
115 q = q.filter(~self.UiDbModel.ui_key.in_(self.BUILTIN_HOOKS))
116 q = q.filter(~self.UiDbModel.ui_key.in_(self.BUILTIN_HOOKS))
116 return self._get_hooks(q)
117 return self._get_hooks(q)
117
118
118 def create_ui_section_value(self, section, val, key=None, active=True):
119 def create_ui_section_value(self, section, val, key=None, active=True):
119 new_ui = self.UiDbModel()
120 new_ui = self.UiDbModel()
120 new_ui.ui_section = section
121 new_ui.ui_section = section
121 new_ui.ui_value = val
122 new_ui.ui_value = val
122 new_ui.ui_active = active
123 new_ui.ui_active = active
123
124
124 repository_id = ''
125 repository_id = ''
125 if self.repo:
126 if self.repo:
126 repo = self._get_repo(self.repo)
127 repo = self._get_repo(self.repo)
127 repository_id = repo.repo_id
128 repository_id = repo.repo_id
128 new_ui.repository_id = repository_id
129 new_ui.repository_id = repository_id
129
130
130 if not key:
131 if not key:
131 # keys are unique so they need appended info
132 # keys are unique so they need appended info
132 if self.repo:
133 if self.repo:
133 key = hashlib.sha1(
134 key = hashlib.sha1(
134 '{}{}{}'.format(section, val, repository_id)).hexdigest()
135 '{}{}{}'.format(section, val, repository_id)).hexdigest()
135 else:
136 else:
136 key = hashlib.sha1('{}{}'.format(section, val)).hexdigest()
137 key = hashlib.sha1('{}{}'.format(section, val)).hexdigest()
137
138
138 new_ui.ui_key = key
139 new_ui.ui_key = key
139
140
140 Session().add(new_ui)
141 Session().add(new_ui)
141 return new_ui
142 return new_ui
142
143
143 def create_or_update_hook(self, key, value):
144 def create_or_update_hook(self, key, value):
144 ui = (
145 ui = (
145 self.get_ui_by_section_and_key(self.HOOKS_SECTION, key) or
146 self.get_ui_by_section_and_key(self.HOOKS_SECTION, key) or
146 self.UiDbModel())
147 self.UiDbModel())
147 ui.ui_section = self.HOOKS_SECTION
148 ui.ui_section = self.HOOKS_SECTION
148 ui.ui_active = True
149 ui.ui_active = True
149 ui.ui_key = key
150 ui.ui_key = key
150 ui.ui_value = value
151 ui.ui_value = value
151
152
152 if self.repo:
153 if self.repo:
153 repo = self._get_repo(self.repo)
154 repo = self._get_repo(self.repo)
154 repository_id = repo.repo_id
155 repository_id = repo.repo_id
155 ui.repository_id = repository_id
156 ui.repository_id = repository_id
156
157
157 Session().add(ui)
158 Session().add(ui)
158 return ui
159 return ui
159
160
160 def delete_ui(self, id_):
161 def delete_ui(self, id_):
161 ui = self.UiDbModel.get(id_)
162 ui = self.UiDbModel.get(id_)
162 if not ui:
163 if not ui:
163 raise SettingNotFound(id_)
164 raise SettingNotFound(id_)
164 Session().delete(ui)
165 Session().delete(ui)
165
166
166 def get_setting_by_name(self, name):
167 def get_setting_by_name(self, name):
167 q = self._get_settings_query()
168 q = self._get_settings_query()
168 q = q.filter(self.SettingsDbModel.app_settings_name == name)
169 q = q.filter(self.SettingsDbModel.app_settings_name == name)
169 return q.scalar()
170 return q.scalar()
170
171
171 def create_or_update_setting(
172 def create_or_update_setting(
172 self, name, val=Optional(''), type_=Optional('unicode')):
173 self, name, val=Optional(''), type_=Optional('unicode')):
173 """
174 """
174 Creates or updates RhodeCode setting. If updates is triggered it will
175 Creates or updates RhodeCode setting. If updates is triggered it will
175 only update parameters that are explicityl set Optional instance will
176 only update parameters that are explicityl set Optional instance will
176 be skipped
177 be skipped
177
178
178 :param name:
179 :param name:
179 :param val:
180 :param val:
180 :param type_:
181 :param type_:
181 :return:
182 :return:
182 """
183 """
183
184
184 res = self.get_setting_by_name(name)
185 res = self.get_setting_by_name(name)
185 repo = self._get_repo(self.repo) if self.repo else None
186 repo = self._get_repo(self.repo) if self.repo else None
186
187
187 if not res:
188 if not res:
188 val = Optional.extract(val)
189 val = Optional.extract(val)
189 type_ = Optional.extract(type_)
190 type_ = Optional.extract(type_)
190
191
191 args = (
192 args = (
192 (repo.repo_id, name, val, type_)
193 (repo.repo_id, name, val, type_)
193 if repo else (name, val, type_))
194 if repo else (name, val, type_))
194 res = self.SettingsDbModel(*args)
195 res = self.SettingsDbModel(*args)
195
196
196 else:
197 else:
197 if self.repo:
198 if self.repo:
198 res.repository_id = repo.repo_id
199 res.repository_id = repo.repo_id
199
200
200 res.app_settings_name = name
201 res.app_settings_name = name
201 if not isinstance(type_, Optional):
202 if not isinstance(type_, Optional):
202 # update if set
203 # update if set
203 res.app_settings_type = type_
204 res.app_settings_type = type_
204 if not isinstance(val, Optional):
205 if not isinstance(val, Optional):
205 # update if set
206 # update if set
206 res.app_settings_value = val
207 res.app_settings_value = val
207
208
208 Session().add(res)
209 Session().add(res)
209 return res
210 return res
210
211
212 def get_cache_region(self):
213 repo = self._get_repo(self.repo) if self.repo else None
214 cache_key = "repo.{}".format(repo.repo_id) if repo else "general_settings"
215 cache_namespace_uid = 'cache_settings.{}'.format(cache_key)
216 region = rc_cache.get_or_create_region('cache_general', cache_namespace_uid)
217 return region, cache_key
218
211 def invalidate_settings_cache(self):
219 def invalidate_settings_cache(self):
212 invalidation_namespace = CacheKey.SETTINGS_INVALIDATION_NAMESPACE
220 from rhodecode.authentication.base import get_authn_registry
213 CacheKey.set_invalidate(invalidation_namespace)
221
222 region, cache_key = self.get_cache_region()
223 log.debug('Invalidation cache region %s for cache_key: %s', region, cache_key)
224 region.invalidate()
225 registry = get_current_registry()
226 if registry:
227 authn_registry = get_authn_registry(registry)
228 if authn_registry:
229 authn_registry.invalidate_plugins_for_auth()
214
230
215 def get_all_settings(self, cache=False, from_request=True):
231 def get_all_settings(self, cache=False, from_request=True):
216 from rhodecode.authentication.base import get_authn_registry
217
218 # defines if we use GLOBAL, or PER_REPO
232 # defines if we use GLOBAL, or PER_REPO
219 repo = self._get_repo(self.repo) if self.repo else None
233 repo = self._get_repo(self.repo) if self.repo else None
220 key = "settings_repo.{}".format(repo.repo_id) if repo else "settings_app"
221
234
222 # initially try the requests context, this is the fastest
235 # initially try the requests context, this is the fastest
223 # we only fetch global config
236 # we only fetch global config
224 if from_request:
237 if from_request:
225 request = get_current_request()
238 request = get_current_request()
226
239
227 if request and not repo and hasattr(request, 'call_context') and hasattr(request.call_context, 'rc_config'):
240 if request and not repo and hasattr(request, 'call_context') and hasattr(request.call_context, 'rc_config'):
228 rc_config = request.call_context.rc_config
241 rc_config = request.call_context.rc_config
229 if rc_config:
242 if rc_config:
230 return rc_config
243 return rc_config
231
244
232 region = rc_cache.get_or_create_region('sql_cache_short')
245 region, cache_key = self.get_cache_region()
233 invalidation_namespace = CacheKey.SETTINGS_INVALIDATION_NAMESPACE
234
246
235 @region.conditional_cache_on_arguments(condition=cache)
247 @region.conditional_cache_on_arguments(condition=cache)
236 def _get_all_settings(name, key):
248 def _get_all_settings(name, key):
237 q = self._get_settings_query()
249 q = self._get_settings_query()
238 if not q:
250 if not q:
239 raise Exception('Could not get application settings !')
251 raise Exception('Could not get application settings !')
240
252
241 settings = {
253 settings = {
242 'rhodecode_' + result.app_settings_name: result.app_settings_value
254 'rhodecode_' + res.app_settings_name: res.app_settings_value
243 for result in q
255 for res in q
244 }
256 }
245 return settings
257 return settings
246
258
247 inv_context_manager = rc_cache.InvalidationContext(
259 statsd = StatsdClient.statsd
248 uid='cache_settings', invalidation_namespace=invalidation_namespace)
260 with statsd.timer('rhodecode_settings_timing.histogram', auto_send=True) as tmr:
249 with inv_context_manager as invalidation_context:
261 result = _get_all_settings('rhodecode_settings', cache_key)
250 # check for stored invalidation signal, and maybe purge the cache
251 # before computing it again
252 if invalidation_context.should_invalidate():
253 # NOTE:(marcink) we flush the whole sql_cache_short region, because it
254 # reads different settings etc. It's little too much but those caches
255 # are anyway very short lived and it's a safest way.
256 region = rc_cache.get_or_create_region('sql_cache_short')
257 region.invalidate()
258 registry = get_current_registry()
259 if registry:
260 authn_registry = get_authn_registry(registry)
261 if authn_registry:
262 authn_registry.invalidate_plugins_for_auth()
263
262
264 result = _get_all_settings('rhodecode_settings', key)
263 compute_time = tmr.ms / 1000.
265 log.debug('Fetching app settings for key: %s took: %.4fs', key,
264 log.debug('Fetching app settings for key: %s took: %.4fs', cache_key, compute_time)
266 inv_context_manager.compute_time)
267
265
268 return result
266 return result
269
267
270 def get_auth_settings(self):
268 def get_auth_settings(self):
271 q = self._get_settings_query()
269 q = self._get_settings_query()
272 q = q.filter(
270 q = q.filter(
273 self.SettingsDbModel.app_settings_name.startswith('auth_'))
271 self.SettingsDbModel.app_settings_name.startswith('auth_'))
274 rows = q.all()
272 rows = q.all()
275 auth_settings = {
273 auth_settings = {
276 row.app_settings_name: row.app_settings_value for row in rows}
274 row.app_settings_name: row.app_settings_value for row in rows}
277 return auth_settings
275 return auth_settings
278
276
279 def get_auth_plugins(self):
277 def get_auth_plugins(self):
280 auth_plugins = self.get_setting_by_name("auth_plugins")
278 auth_plugins = self.get_setting_by_name("auth_plugins")
281 return auth_plugins.app_settings_value
279 return auth_plugins.app_settings_value
282
280
283 def get_default_repo_settings(self, strip_prefix=False):
281 def get_default_repo_settings(self, strip_prefix=False):
284 q = self._get_settings_query()
282 q = self._get_settings_query()
285 q = q.filter(
283 q = q.filter(
286 self.SettingsDbModel.app_settings_name.startswith('default_'))
284 self.SettingsDbModel.app_settings_name.startswith('default_'))
287 rows = q.all()
285 rows = q.all()
288
286
289 result = {}
287 result = {}
290 for row in rows:
288 for row in rows:
291 key = row.app_settings_name
289 key = row.app_settings_name
292 if strip_prefix:
290 if strip_prefix:
293 key = remove_prefix(key, prefix='default_')
291 key = remove_prefix(key, prefix='default_')
294 result.update({key: row.app_settings_value})
292 result.update({key: row.app_settings_value})
295 return result
293 return result
296
294
297 def get_repo(self):
295 def get_repo(self):
298 repo = self._get_repo(self.repo)
296 repo = self._get_repo(self.repo)
299 if not repo:
297 if not repo:
300 raise Exception(
298 raise Exception(
301 'Repository `{}` cannot be found inside the database'.format(
299 'Repository `{}` cannot be found inside the database'.format(
302 self.repo))
300 self.repo))
303 return repo
301 return repo
304
302
305 def _filter_by_repo(self, model, query):
303 def _filter_by_repo(self, model, query):
306 if self.repo:
304 if self.repo:
307 repo = self.get_repo()
305 repo = self.get_repo()
308 query = query.filter(model.repository_id == repo.repo_id)
306 query = query.filter(model.repository_id == repo.repo_id)
309 return query
307 return query
310
308
311 def _get_hooks(self, query):
309 def _get_hooks(self, query):
312 query = query.filter(self.UiDbModel.ui_section == self.HOOKS_SECTION)
310 query = query.filter(self.UiDbModel.ui_section == self.HOOKS_SECTION)
313 query = self._filter_by_repo(RepoRhodeCodeUi, query)
311 query = self._filter_by_repo(RepoRhodeCodeUi, query)
314 return query.all()
312 return query.all()
315
313
316 def _get_settings_query(self):
314 def _get_settings_query(self):
317 q = self.SettingsDbModel.query()
315 q = self.SettingsDbModel.query()
318 return self._filter_by_repo(RepoRhodeCodeSetting, q)
316 return self._filter_by_repo(RepoRhodeCodeSetting, q)
319
317
320 def list_enabled_social_plugins(self, settings):
318 def list_enabled_social_plugins(self, settings):
321 enabled = []
319 enabled = []
322 for plug in SOCIAL_PLUGINS_LIST:
320 for plug in SOCIAL_PLUGINS_LIST:
323 if str2bool(settings.get('rhodecode_auth_{}_enabled'.format(plug)
321 if str2bool(settings.get('rhodecode_auth_{}_enabled'.format(plug)
324 )):
322 )):
325 enabled.append(plug)
323 enabled.append(plug)
326 return enabled
324 return enabled
327
325
328
326
329 def assert_repo_settings(func):
327 def assert_repo_settings(func):
330 @wraps(func)
328 @wraps(func)
331 def _wrapper(self, *args, **kwargs):
329 def _wrapper(self, *args, **kwargs):
332 if not self.repo_settings:
330 if not self.repo_settings:
333 raise Exception('Repository is not specified')
331 raise Exception('Repository is not specified')
334 return func(self, *args, **kwargs)
332 return func(self, *args, **kwargs)
335 return _wrapper
333 return _wrapper
336
334
337
335
338 class IssueTrackerSettingsModel(object):
336 class IssueTrackerSettingsModel(object):
339 INHERIT_SETTINGS = 'inherit_issue_tracker_settings'
337 INHERIT_SETTINGS = 'inherit_issue_tracker_settings'
340 SETTINGS_PREFIX = 'issuetracker_'
338 SETTINGS_PREFIX = 'issuetracker_'
341
339
342 def __init__(self, sa=None, repo=None):
340 def __init__(self, sa=None, repo=None):
343 self.global_settings = SettingsModel(sa=sa)
341 self.global_settings = SettingsModel(sa=sa)
344 self.repo_settings = SettingsModel(sa=sa, repo=repo) if repo else None
342 self.repo_settings = SettingsModel(sa=sa, repo=repo) if repo else None
345
343
346 @property
344 @property
347 def inherit_global_settings(self):
345 def inherit_global_settings(self):
348 if not self.repo_settings:
346 if not self.repo_settings:
349 return True
347 return True
350 setting = self.repo_settings.get_setting_by_name(self.INHERIT_SETTINGS)
348 setting = self.repo_settings.get_setting_by_name(self.INHERIT_SETTINGS)
351 return setting.app_settings_value if setting else True
349 return setting.app_settings_value if setting else True
352
350
353 @inherit_global_settings.setter
351 @inherit_global_settings.setter
354 def inherit_global_settings(self, value):
352 def inherit_global_settings(self, value):
355 if self.repo_settings:
353 if self.repo_settings:
356 settings = self.repo_settings.create_or_update_setting(
354 settings = self.repo_settings.create_or_update_setting(
357 self.INHERIT_SETTINGS, value, type_='bool')
355 self.INHERIT_SETTINGS, value, type_='bool')
358 Session().add(settings)
356 Session().add(settings)
359
357
360 def _get_keyname(self, key, uid, prefix=''):
358 def _get_keyname(self, key, uid, prefix=''):
361 return '{0}{1}{2}_{3}'.format(
359 return '{0}{1}{2}_{3}'.format(
362 prefix, self.SETTINGS_PREFIX, key, uid)
360 prefix, self.SETTINGS_PREFIX, key, uid)
363
361
364 def _make_dict_for_settings(self, qs):
362 def _make_dict_for_settings(self, qs):
365 prefix_match = self._get_keyname('pat', '', 'rhodecode_')
363 prefix_match = self._get_keyname('pat', '', 'rhodecode_')
366
364
367 issuetracker_entries = {}
365 issuetracker_entries = {}
368 # create keys
366 # create keys
369 for k, v in qs.items():
367 for k, v in qs.items():
370 if k.startswith(prefix_match):
368 if k.startswith(prefix_match):
371 uid = k[len(prefix_match):]
369 uid = k[len(prefix_match):]
372 issuetracker_entries[uid] = None
370 issuetracker_entries[uid] = None
373
371
374 def url_cleaner(input_str):
372 def url_cleaner(input_str):
375 input_str = input_str.replace('"', '').replace("'", '')
373 input_str = input_str.replace('"', '').replace("'", '')
376 input_str = bleach.clean(input_str, strip=True)
374 input_str = bleach.clean(input_str, strip=True)
377 return input_str
375 return input_str
378
376
379 # populate
377 # populate
380 for uid in issuetracker_entries:
378 for uid in issuetracker_entries:
381 url_data = qs.get(self._get_keyname('url', uid, 'rhodecode_'))
379 url_data = qs.get(self._get_keyname('url', uid, 'rhodecode_'))
382
380
383 pat = qs.get(self._get_keyname('pat', uid, 'rhodecode_'))
381 pat = qs.get(self._get_keyname('pat', uid, 'rhodecode_'))
384 try:
382 try:
385 pat_compiled = re.compile(r'%s' % pat)
383 pat_compiled = re.compile(r'%s' % pat)
386 except re.error:
384 except re.error:
387 pat_compiled = None
385 pat_compiled = None
388
386
389 issuetracker_entries[uid] = AttributeDict({
387 issuetracker_entries[uid] = AttributeDict({
390 'pat': pat,
388 'pat': pat,
391 'pat_compiled': pat_compiled,
389 'pat_compiled': pat_compiled,
392 'url': url_cleaner(
390 'url': url_cleaner(
393 qs.get(self._get_keyname('url', uid, 'rhodecode_')) or ''),
391 qs.get(self._get_keyname('url', uid, 'rhodecode_')) or ''),
394 'pref': bleach.clean(
392 'pref': bleach.clean(
395 qs.get(self._get_keyname('pref', uid, 'rhodecode_')) or ''),
393 qs.get(self._get_keyname('pref', uid, 'rhodecode_')) or ''),
396 'desc': qs.get(
394 'desc': qs.get(
397 self._get_keyname('desc', uid, 'rhodecode_')),
395 self._get_keyname('desc', uid, 'rhodecode_')),
398 })
396 })
399
397
400 return issuetracker_entries
398 return issuetracker_entries
401
399
402 def get_global_settings(self, cache=False):
400 def get_global_settings(self, cache=False):
403 """
401 """
404 Returns list of global issue tracker settings
402 Returns list of global issue tracker settings
405 """
403 """
406 defaults = self.global_settings.get_all_settings(cache=cache)
404 defaults = self.global_settings.get_all_settings(cache=cache)
407 settings = self._make_dict_for_settings(defaults)
405 settings = self._make_dict_for_settings(defaults)
408 return settings
406 return settings
409
407
410 def get_repo_settings(self, cache=False):
408 def get_repo_settings(self, cache=False):
411 """
409 """
412 Returns list of issue tracker settings per repository
410 Returns list of issue tracker settings per repository
413 """
411 """
414 if not self.repo_settings:
412 if not self.repo_settings:
415 raise Exception('Repository is not specified')
413 raise Exception('Repository is not specified')
416 all_settings = self.repo_settings.get_all_settings(cache=cache)
414 all_settings = self.repo_settings.get_all_settings(cache=cache)
417 settings = self._make_dict_for_settings(all_settings)
415 settings = self._make_dict_for_settings(all_settings)
418 return settings
416 return settings
419
417
420 def get_settings(self, cache=False):
418 def get_settings(self, cache=False):
421 if self.inherit_global_settings:
419 if self.inherit_global_settings:
422 return self.get_global_settings(cache=cache)
420 return self.get_global_settings(cache=cache)
423 else:
421 else:
424 return self.get_repo_settings(cache=cache)
422 return self.get_repo_settings(cache=cache)
425
423
426 def delete_entries(self, uid):
424 def delete_entries(self, uid):
427 if self.repo_settings:
425 if self.repo_settings:
428 all_patterns = self.get_repo_settings()
426 all_patterns = self.get_repo_settings()
429 settings_model = self.repo_settings
427 settings_model = self.repo_settings
430 else:
428 else:
431 all_patterns = self.get_global_settings()
429 all_patterns = self.get_global_settings()
432 settings_model = self.global_settings
430 settings_model = self.global_settings
433 entries = all_patterns.get(uid, [])
431 entries = all_patterns.get(uid, [])
434
432
435 for del_key in entries:
433 for del_key in entries:
436 setting_name = self._get_keyname(del_key, uid)
434 setting_name = self._get_keyname(del_key, uid)
437 entry = settings_model.get_setting_by_name(setting_name)
435 entry = settings_model.get_setting_by_name(setting_name)
438 if entry:
436 if entry:
439 Session().delete(entry)
437 Session().delete(entry)
440
438
441 Session().commit()
439 Session().commit()
442
440
443 def create_or_update_setting(
441 def create_or_update_setting(
444 self, name, val=Optional(''), type_=Optional('unicode')):
442 self, name, val=Optional(''), type_=Optional('unicode')):
445 if self.repo_settings:
443 if self.repo_settings:
446 setting = self.repo_settings.create_or_update_setting(
444 setting = self.repo_settings.create_or_update_setting(
447 name, val, type_)
445 name, val, type_)
448 else:
446 else:
449 setting = self.global_settings.create_or_update_setting(
447 setting = self.global_settings.create_or_update_setting(
450 name, val, type_)
448 name, val, type_)
451 return setting
449 return setting
452
450
453
451
454 class VcsSettingsModel(object):
452 class VcsSettingsModel(object):
455
453
456 INHERIT_SETTINGS = 'inherit_vcs_settings'
454 INHERIT_SETTINGS = 'inherit_vcs_settings'
457 GENERAL_SETTINGS = (
455 GENERAL_SETTINGS = (
458 'use_outdated_comments',
456 'use_outdated_comments',
459 'pr_merge_enabled',
457 'pr_merge_enabled',
460 'hg_use_rebase_for_merging',
458 'hg_use_rebase_for_merging',
461 'hg_close_branch_before_merging',
459 'hg_close_branch_before_merging',
462 'git_use_rebase_for_merging',
460 'git_use_rebase_for_merging',
463 'git_close_branch_before_merging',
461 'git_close_branch_before_merging',
464 'diff_cache',
462 'diff_cache',
465 )
463 )
466
464
467 HOOKS_SETTINGS = (
465 HOOKS_SETTINGS = (
468 ('hooks', 'changegroup.repo_size'),
466 ('hooks', 'changegroup.repo_size'),
469 ('hooks', 'changegroup.push_logger'),
467 ('hooks', 'changegroup.push_logger'),
470 ('hooks', 'outgoing.pull_logger'),
468 ('hooks', 'outgoing.pull_logger'),
471 )
469 )
472 HG_SETTINGS = (
470 HG_SETTINGS = (
473 ('extensions', 'largefiles'),
471 ('extensions', 'largefiles'),
474 ('phases', 'publish'),
472 ('phases', 'publish'),
475 ('extensions', 'evolve'),
473 ('extensions', 'evolve'),
476 ('extensions', 'topic'),
474 ('extensions', 'topic'),
477 ('experimental', 'evolution'),
475 ('experimental', 'evolution'),
478 ('experimental', 'evolution.exchange'),
476 ('experimental', 'evolution.exchange'),
479 )
477 )
480 GIT_SETTINGS = (
478 GIT_SETTINGS = (
481 ('vcs_git_lfs', 'enabled'),
479 ('vcs_git_lfs', 'enabled'),
482 )
480 )
483 GLOBAL_HG_SETTINGS = (
481 GLOBAL_HG_SETTINGS = (
484 ('extensions', 'largefiles'),
482 ('extensions', 'largefiles'),
485 ('largefiles', 'usercache'),
483 ('largefiles', 'usercache'),
486 ('phases', 'publish'),
484 ('phases', 'publish'),
487 ('extensions', 'hgsubversion'),
485 ('extensions', 'hgsubversion'),
488 ('extensions', 'evolve'),
486 ('extensions', 'evolve'),
489 ('extensions', 'topic'),
487 ('extensions', 'topic'),
490 ('experimental', 'evolution'),
488 ('experimental', 'evolution'),
491 ('experimental', 'evolution.exchange'),
489 ('experimental', 'evolution.exchange'),
492 )
490 )
493
491
494 GLOBAL_GIT_SETTINGS = (
492 GLOBAL_GIT_SETTINGS = (
495 ('vcs_git_lfs', 'enabled'),
493 ('vcs_git_lfs', 'enabled'),
496 ('vcs_git_lfs', 'store_location')
494 ('vcs_git_lfs', 'store_location')
497 )
495 )
498
496
499 GLOBAL_SVN_SETTINGS = (
497 GLOBAL_SVN_SETTINGS = (
500 ('vcs_svn_proxy', 'http_requests_enabled'),
498 ('vcs_svn_proxy', 'http_requests_enabled'),
501 ('vcs_svn_proxy', 'http_server_url')
499 ('vcs_svn_proxy', 'http_server_url')
502 )
500 )
503
501
504 SVN_BRANCH_SECTION = 'vcs_svn_branch'
502 SVN_BRANCH_SECTION = 'vcs_svn_branch'
505 SVN_TAG_SECTION = 'vcs_svn_tag'
503 SVN_TAG_SECTION = 'vcs_svn_tag'
506 SSL_SETTING = ('web', 'push_ssl')
504 SSL_SETTING = ('web', 'push_ssl')
507 PATH_SETTING = ('paths', '/')
505 PATH_SETTING = ('paths', '/')
508
506
509 def __init__(self, sa=None, repo=None):
507 def __init__(self, sa=None, repo=None):
510 self.global_settings = SettingsModel(sa=sa)
508 self.global_settings = SettingsModel(sa=sa)
511 self.repo_settings = SettingsModel(sa=sa, repo=repo) if repo else None
509 self.repo_settings = SettingsModel(sa=sa, repo=repo) if repo else None
512 self._ui_settings = (
510 self._ui_settings = (
513 self.HG_SETTINGS + self.GIT_SETTINGS + self.HOOKS_SETTINGS)
511 self.HG_SETTINGS + self.GIT_SETTINGS + self.HOOKS_SETTINGS)
514 self._svn_sections = (self.SVN_BRANCH_SECTION, self.SVN_TAG_SECTION)
512 self._svn_sections = (self.SVN_BRANCH_SECTION, self.SVN_TAG_SECTION)
515
513
516 @property
514 @property
517 @assert_repo_settings
515 @assert_repo_settings
518 def inherit_global_settings(self):
516 def inherit_global_settings(self):
519 setting = self.repo_settings.get_setting_by_name(self.INHERIT_SETTINGS)
517 setting = self.repo_settings.get_setting_by_name(self.INHERIT_SETTINGS)
520 return setting.app_settings_value if setting else True
518 return setting.app_settings_value if setting else True
521
519
522 @inherit_global_settings.setter
520 @inherit_global_settings.setter
523 @assert_repo_settings
521 @assert_repo_settings
524 def inherit_global_settings(self, value):
522 def inherit_global_settings(self, value):
525 self.repo_settings.create_or_update_setting(
523 self.repo_settings.create_or_update_setting(
526 self.INHERIT_SETTINGS, value, type_='bool')
524 self.INHERIT_SETTINGS, value, type_='bool')
527
525
528 def get_global_svn_branch_patterns(self):
526 def get_global_svn_branch_patterns(self):
529 return self.global_settings.get_ui_by_section(self.SVN_BRANCH_SECTION)
527 return self.global_settings.get_ui_by_section(self.SVN_BRANCH_SECTION)
530
528
531 @assert_repo_settings
529 @assert_repo_settings
532 def get_repo_svn_branch_patterns(self):
530 def get_repo_svn_branch_patterns(self):
533 return self.repo_settings.get_ui_by_section(self.SVN_BRANCH_SECTION)
531 return self.repo_settings.get_ui_by_section(self.SVN_BRANCH_SECTION)
534
532
535 def get_global_svn_tag_patterns(self):
533 def get_global_svn_tag_patterns(self):
536 return self.global_settings.get_ui_by_section(self.SVN_TAG_SECTION)
534 return self.global_settings.get_ui_by_section(self.SVN_TAG_SECTION)
537
535
538 @assert_repo_settings
536 @assert_repo_settings
539 def get_repo_svn_tag_patterns(self):
537 def get_repo_svn_tag_patterns(self):
540 return self.repo_settings.get_ui_by_section(self.SVN_TAG_SECTION)
538 return self.repo_settings.get_ui_by_section(self.SVN_TAG_SECTION)
541
539
542 def get_global_settings(self):
540 def get_global_settings(self):
543 return self._collect_all_settings(global_=True)
541 return self._collect_all_settings(global_=True)
544
542
545 @assert_repo_settings
543 @assert_repo_settings
546 def get_repo_settings(self):
544 def get_repo_settings(self):
547 return self._collect_all_settings(global_=False)
545 return self._collect_all_settings(global_=False)
548
546
549 @assert_repo_settings
547 @assert_repo_settings
550 def get_repo_settings_inherited(self):
548 def get_repo_settings_inherited(self):
551 global_settings = self.get_global_settings()
549 global_settings = self.get_global_settings()
552 global_settings.update(self.get_repo_settings())
550 global_settings.update(self.get_repo_settings())
553 return global_settings
551 return global_settings
554
552
555 @assert_repo_settings
553 @assert_repo_settings
556 def create_or_update_repo_settings(
554 def create_or_update_repo_settings(
557 self, data, inherit_global_settings=False):
555 self, data, inherit_global_settings=False):
558 from rhodecode.model.scm import ScmModel
556 from rhodecode.model.scm import ScmModel
559
557
560 self.inherit_global_settings = inherit_global_settings
558 self.inherit_global_settings = inherit_global_settings
561
559
562 repo = self.repo_settings.get_repo()
560 repo = self.repo_settings.get_repo()
563 if not inherit_global_settings:
561 if not inherit_global_settings:
564 if repo.repo_type == 'svn':
562 if repo.repo_type == 'svn':
565 self.create_repo_svn_settings(data)
563 self.create_repo_svn_settings(data)
566 else:
564 else:
567 self.create_or_update_repo_hook_settings(data)
565 self.create_or_update_repo_hook_settings(data)
568 self.create_or_update_repo_pr_settings(data)
566 self.create_or_update_repo_pr_settings(data)
569
567
570 if repo.repo_type == 'hg':
568 if repo.repo_type == 'hg':
571 self.create_or_update_repo_hg_settings(data)
569 self.create_or_update_repo_hg_settings(data)
572
570
573 if repo.repo_type == 'git':
571 if repo.repo_type == 'git':
574 self.create_or_update_repo_git_settings(data)
572 self.create_or_update_repo_git_settings(data)
575
573
576 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
574 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
577
575
578 @assert_repo_settings
576 @assert_repo_settings
579 def create_or_update_repo_hook_settings(self, data):
577 def create_or_update_repo_hook_settings(self, data):
580 for section, key in self.HOOKS_SETTINGS:
578 for section, key in self.HOOKS_SETTINGS:
581 data_key = self._get_form_ui_key(section, key)
579 data_key = self._get_form_ui_key(section, key)
582 if data_key not in data:
580 if data_key not in data:
583 raise ValueError(
581 raise ValueError(
584 'The given data does not contain {} key'.format(data_key))
582 'The given data does not contain {} key'.format(data_key))
585
583
586 active = data.get(data_key)
584 active = data.get(data_key)
587 repo_setting = self.repo_settings.get_ui_by_section_and_key(
585 repo_setting = self.repo_settings.get_ui_by_section_and_key(
588 section, key)
586 section, key)
589 if not repo_setting:
587 if not repo_setting:
590 global_setting = self.global_settings.\
588 global_setting = self.global_settings.\
591 get_ui_by_section_and_key(section, key)
589 get_ui_by_section_and_key(section, key)
592 self.repo_settings.create_ui_section_value(
590 self.repo_settings.create_ui_section_value(
593 section, global_setting.ui_value, key=key, active=active)
591 section, global_setting.ui_value, key=key, active=active)
594 else:
592 else:
595 repo_setting.ui_active = active
593 repo_setting.ui_active = active
596 Session().add(repo_setting)
594 Session().add(repo_setting)
597
595
598 def update_global_hook_settings(self, data):
596 def update_global_hook_settings(self, data):
599 for section, key in self.HOOKS_SETTINGS:
597 for section, key in self.HOOKS_SETTINGS:
600 data_key = self._get_form_ui_key(section, key)
598 data_key = self._get_form_ui_key(section, key)
601 if data_key not in data:
599 if data_key not in data:
602 raise ValueError(
600 raise ValueError(
603 'The given data does not contain {} key'.format(data_key))
601 'The given data does not contain {} key'.format(data_key))
604 active = data.get(data_key)
602 active = data.get(data_key)
605 repo_setting = self.global_settings.get_ui_by_section_and_key(
603 repo_setting = self.global_settings.get_ui_by_section_and_key(
606 section, key)
604 section, key)
607 repo_setting.ui_active = active
605 repo_setting.ui_active = active
608 Session().add(repo_setting)
606 Session().add(repo_setting)
609
607
610 @assert_repo_settings
608 @assert_repo_settings
611 def create_or_update_repo_pr_settings(self, data):
609 def create_or_update_repo_pr_settings(self, data):
612 return self._create_or_update_general_settings(
610 return self._create_or_update_general_settings(
613 self.repo_settings, data)
611 self.repo_settings, data)
614
612
615 def create_or_update_global_pr_settings(self, data):
613 def create_or_update_global_pr_settings(self, data):
616 return self._create_or_update_general_settings(
614 return self._create_or_update_general_settings(
617 self.global_settings, data)
615 self.global_settings, data)
618
616
619 @assert_repo_settings
617 @assert_repo_settings
620 def create_repo_svn_settings(self, data):
618 def create_repo_svn_settings(self, data):
621 return self._create_svn_settings(self.repo_settings, data)
619 return self._create_svn_settings(self.repo_settings, data)
622
620
623 def _set_evolution(self, settings, is_enabled):
621 def _set_evolution(self, settings, is_enabled):
624 if is_enabled:
622 if is_enabled:
625 # if evolve is active set evolution=all
623 # if evolve is active set evolution=all
626
624
627 self._create_or_update_ui(
625 self._create_or_update_ui(
628 settings, *('experimental', 'evolution'), value='all',
626 settings, *('experimental', 'evolution'), value='all',
629 active=True)
627 active=True)
630 self._create_or_update_ui(
628 self._create_or_update_ui(
631 settings, *('experimental', 'evolution.exchange'), value='yes',
629 settings, *('experimental', 'evolution.exchange'), value='yes',
632 active=True)
630 active=True)
633 # if evolve is active set topics server support
631 # if evolve is active set topics server support
634 self._create_or_update_ui(
632 self._create_or_update_ui(
635 settings, *('extensions', 'topic'), value='',
633 settings, *('extensions', 'topic'), value='',
636 active=True)
634 active=True)
637
635
638 else:
636 else:
639 self._create_or_update_ui(
637 self._create_or_update_ui(
640 settings, *('experimental', 'evolution'), value='',
638 settings, *('experimental', 'evolution'), value='',
641 active=False)
639 active=False)
642 self._create_or_update_ui(
640 self._create_or_update_ui(
643 settings, *('experimental', 'evolution.exchange'), value='no',
641 settings, *('experimental', 'evolution.exchange'), value='no',
644 active=False)
642 active=False)
645 self._create_or_update_ui(
643 self._create_or_update_ui(
646 settings, *('extensions', 'topic'), value='',
644 settings, *('extensions', 'topic'), value='',
647 active=False)
645 active=False)
648
646
649 @assert_repo_settings
647 @assert_repo_settings
650 def create_or_update_repo_hg_settings(self, data):
648 def create_or_update_repo_hg_settings(self, data):
651 largefiles, phases, evolve = \
649 largefiles, phases, evolve = \
652 self.HG_SETTINGS[:3]
650 self.HG_SETTINGS[:3]
653 largefiles_key, phases_key, evolve_key = \
651 largefiles_key, phases_key, evolve_key = \
654 self._get_settings_keys(self.HG_SETTINGS[:3], data)
652 self._get_settings_keys(self.HG_SETTINGS[:3], data)
655
653
656 self._create_or_update_ui(
654 self._create_or_update_ui(
657 self.repo_settings, *largefiles, value='',
655 self.repo_settings, *largefiles, value='',
658 active=data[largefiles_key])
656 active=data[largefiles_key])
659 self._create_or_update_ui(
657 self._create_or_update_ui(
660 self.repo_settings, *evolve, value='',
658 self.repo_settings, *evolve, value='',
661 active=data[evolve_key])
659 active=data[evolve_key])
662 self._set_evolution(self.repo_settings, is_enabled=data[evolve_key])
660 self._set_evolution(self.repo_settings, is_enabled=data[evolve_key])
663
661
664 self._create_or_update_ui(
662 self._create_or_update_ui(
665 self.repo_settings, *phases, value=safe_str(data[phases_key]))
663 self.repo_settings, *phases, value=safe_str(data[phases_key]))
666
664
667 def create_or_update_global_hg_settings(self, data):
665 def create_or_update_global_hg_settings(self, data):
668 largefiles, largefiles_store, phases, hgsubversion, evolve \
666 largefiles, largefiles_store, phases, hgsubversion, evolve \
669 = self.GLOBAL_HG_SETTINGS[:5]
667 = self.GLOBAL_HG_SETTINGS[:5]
670 largefiles_key, largefiles_store_key, phases_key, subversion_key, evolve_key \
668 largefiles_key, largefiles_store_key, phases_key, subversion_key, evolve_key \
671 = self._get_settings_keys(self.GLOBAL_HG_SETTINGS[:5], data)
669 = self._get_settings_keys(self.GLOBAL_HG_SETTINGS[:5], data)
672
670
673 self._create_or_update_ui(
671 self._create_or_update_ui(
674 self.global_settings, *largefiles, value='',
672 self.global_settings, *largefiles, value='',
675 active=data[largefiles_key])
673 active=data[largefiles_key])
676 self._create_or_update_ui(
674 self._create_or_update_ui(
677 self.global_settings, *largefiles_store, value=data[largefiles_store_key])
675 self.global_settings, *largefiles_store, value=data[largefiles_store_key])
678 self._create_or_update_ui(
676 self._create_or_update_ui(
679 self.global_settings, *phases, value=safe_str(data[phases_key]))
677 self.global_settings, *phases, value=safe_str(data[phases_key]))
680 self._create_or_update_ui(
678 self._create_or_update_ui(
681 self.global_settings, *hgsubversion, active=data[subversion_key])
679 self.global_settings, *hgsubversion, active=data[subversion_key])
682 self._create_or_update_ui(
680 self._create_or_update_ui(
683 self.global_settings, *evolve, value='',
681 self.global_settings, *evolve, value='',
684 active=data[evolve_key])
682 active=data[evolve_key])
685 self._set_evolution(self.global_settings, is_enabled=data[evolve_key])
683 self._set_evolution(self.global_settings, is_enabled=data[evolve_key])
686
684
687 def create_or_update_repo_git_settings(self, data):
685 def create_or_update_repo_git_settings(self, data):
688 # NOTE(marcink): # comma makes unpack work properly
686 # NOTE(marcink): # comma makes unpack work properly
689 lfs_enabled, \
687 lfs_enabled, \
690 = self.GIT_SETTINGS
688 = self.GIT_SETTINGS
691
689
692 lfs_enabled_key, \
690 lfs_enabled_key, \
693 = self._get_settings_keys(self.GIT_SETTINGS, data)
691 = self._get_settings_keys(self.GIT_SETTINGS, data)
694
692
695 self._create_or_update_ui(
693 self._create_or_update_ui(
696 self.repo_settings, *lfs_enabled, value=data[lfs_enabled_key],
694 self.repo_settings, *lfs_enabled, value=data[lfs_enabled_key],
697 active=data[lfs_enabled_key])
695 active=data[lfs_enabled_key])
698
696
699 def create_or_update_global_git_settings(self, data):
697 def create_or_update_global_git_settings(self, data):
700 lfs_enabled, lfs_store_location \
698 lfs_enabled, lfs_store_location \
701 = self.GLOBAL_GIT_SETTINGS
699 = self.GLOBAL_GIT_SETTINGS
702 lfs_enabled_key, lfs_store_location_key \
700 lfs_enabled_key, lfs_store_location_key \
703 = self._get_settings_keys(self.GLOBAL_GIT_SETTINGS, data)
701 = self._get_settings_keys(self.GLOBAL_GIT_SETTINGS, data)
704
702
705 self._create_or_update_ui(
703 self._create_or_update_ui(
706 self.global_settings, *lfs_enabled, value=data[lfs_enabled_key],
704 self.global_settings, *lfs_enabled, value=data[lfs_enabled_key],
707 active=data[lfs_enabled_key])
705 active=data[lfs_enabled_key])
708 self._create_or_update_ui(
706 self._create_or_update_ui(
709 self.global_settings, *lfs_store_location,
707 self.global_settings, *lfs_store_location,
710 value=data[lfs_store_location_key])
708 value=data[lfs_store_location_key])
711
709
712 def create_or_update_global_svn_settings(self, data):
710 def create_or_update_global_svn_settings(self, data):
713 # branch/tags patterns
711 # branch/tags patterns
714 self._create_svn_settings(self.global_settings, data)
712 self._create_svn_settings(self.global_settings, data)
715
713
716 http_requests_enabled, http_server_url = self.GLOBAL_SVN_SETTINGS
714 http_requests_enabled, http_server_url = self.GLOBAL_SVN_SETTINGS
717 http_requests_enabled_key, http_server_url_key = self._get_settings_keys(
715 http_requests_enabled_key, http_server_url_key = self._get_settings_keys(
718 self.GLOBAL_SVN_SETTINGS, data)
716 self.GLOBAL_SVN_SETTINGS, data)
719
717
720 self._create_or_update_ui(
718 self._create_or_update_ui(
721 self.global_settings, *http_requests_enabled,
719 self.global_settings, *http_requests_enabled,
722 value=safe_str(data[http_requests_enabled_key]))
720 value=safe_str(data[http_requests_enabled_key]))
723 self._create_or_update_ui(
721 self._create_or_update_ui(
724 self.global_settings, *http_server_url,
722 self.global_settings, *http_server_url,
725 value=data[http_server_url_key])
723 value=data[http_server_url_key])
726
724
727 def update_global_ssl_setting(self, value):
725 def update_global_ssl_setting(self, value):
728 self._create_or_update_ui(
726 self._create_or_update_ui(
729 self.global_settings, *self.SSL_SETTING, value=value)
727 self.global_settings, *self.SSL_SETTING, value=value)
730
728
731 def update_global_path_setting(self, value):
729 def update_global_path_setting(self, value):
732 self._create_or_update_ui(
730 self._create_or_update_ui(
733 self.global_settings, *self.PATH_SETTING, value=value)
731 self.global_settings, *self.PATH_SETTING, value=value)
734
732
735 @assert_repo_settings
733 @assert_repo_settings
736 def delete_repo_svn_pattern(self, id_):
734 def delete_repo_svn_pattern(self, id_):
737 ui = self.repo_settings.UiDbModel.get(id_)
735 ui = self.repo_settings.UiDbModel.get(id_)
738 if ui and ui.repository.repo_name == self.repo_settings.repo:
736 if ui and ui.repository.repo_name == self.repo_settings.repo:
739 # only delete if it's the same repo as initialized settings
737 # only delete if it's the same repo as initialized settings
740 self.repo_settings.delete_ui(id_)
738 self.repo_settings.delete_ui(id_)
741 else:
739 else:
742 # raise error as if we wouldn't find this option
740 # raise error as if we wouldn't find this option
743 self.repo_settings.delete_ui(-1)
741 self.repo_settings.delete_ui(-1)
744
742
745 def delete_global_svn_pattern(self, id_):
743 def delete_global_svn_pattern(self, id_):
746 self.global_settings.delete_ui(id_)
744 self.global_settings.delete_ui(id_)
747
745
748 @assert_repo_settings
746 @assert_repo_settings
749 def get_repo_ui_settings(self, section=None, key=None):
747 def get_repo_ui_settings(self, section=None, key=None):
750 global_uis = self.global_settings.get_ui(section, key)
748 global_uis = self.global_settings.get_ui(section, key)
751 repo_uis = self.repo_settings.get_ui(section, key)
749 repo_uis = self.repo_settings.get_ui(section, key)
752
750
753 filtered_repo_uis = self._filter_ui_settings(repo_uis)
751 filtered_repo_uis = self._filter_ui_settings(repo_uis)
754 filtered_repo_uis_keys = [
752 filtered_repo_uis_keys = [
755 (s.section, s.key) for s in filtered_repo_uis]
753 (s.section, s.key) for s in filtered_repo_uis]
756
754
757 def _is_global_ui_filtered(ui):
755 def _is_global_ui_filtered(ui):
758 return (
756 return (
759 (ui.section, ui.key) in filtered_repo_uis_keys
757 (ui.section, ui.key) in filtered_repo_uis_keys
760 or ui.section in self._svn_sections)
758 or ui.section in self._svn_sections)
761
759
762 filtered_global_uis = [
760 filtered_global_uis = [
763 ui for ui in global_uis if not _is_global_ui_filtered(ui)]
761 ui for ui in global_uis if not _is_global_ui_filtered(ui)]
764
762
765 return filtered_global_uis + filtered_repo_uis
763 return filtered_global_uis + filtered_repo_uis
766
764
767 def get_global_ui_settings(self, section=None, key=None):
765 def get_global_ui_settings(self, section=None, key=None):
768 return self.global_settings.get_ui(section, key)
766 return self.global_settings.get_ui(section, key)
769
767
770 def get_ui_settings_as_config_obj(self, section=None, key=None):
768 def get_ui_settings_as_config_obj(self, section=None, key=None):
771 config = base.Config()
769 config = base.Config()
772
770
773 ui_settings = self.get_ui_settings(section=section, key=key)
771 ui_settings = self.get_ui_settings(section=section, key=key)
774
772
775 for entry in ui_settings:
773 for entry in ui_settings:
776 config.set(entry.section, entry.key, entry.value)
774 config.set(entry.section, entry.key, entry.value)
777
775
778 return config
776 return config
779
777
780 def get_ui_settings(self, section=None, key=None):
778 def get_ui_settings(self, section=None, key=None):
781 if not self.repo_settings or self.inherit_global_settings:
779 if not self.repo_settings or self.inherit_global_settings:
782 return self.get_global_ui_settings(section, key)
780 return self.get_global_ui_settings(section, key)
783 else:
781 else:
784 return self.get_repo_ui_settings(section, key)
782 return self.get_repo_ui_settings(section, key)
785
783
786 def get_svn_patterns(self, section=None):
784 def get_svn_patterns(self, section=None):
787 if not self.repo_settings:
785 if not self.repo_settings:
788 return self.get_global_ui_settings(section)
786 return self.get_global_ui_settings(section)
789 else:
787 else:
790 return self.get_repo_ui_settings(section)
788 return self.get_repo_ui_settings(section)
791
789
792 @assert_repo_settings
790 @assert_repo_settings
793 def get_repo_general_settings(self):
791 def get_repo_general_settings(self):
794 global_settings = self.global_settings.get_all_settings()
792 global_settings = self.global_settings.get_all_settings()
795 repo_settings = self.repo_settings.get_all_settings()
793 repo_settings = self.repo_settings.get_all_settings()
796 filtered_repo_settings = self._filter_general_settings(repo_settings)
794 filtered_repo_settings = self._filter_general_settings(repo_settings)
797 global_settings.update(filtered_repo_settings)
795 global_settings.update(filtered_repo_settings)
798 return global_settings
796 return global_settings
799
797
800 def get_global_general_settings(self):
798 def get_global_general_settings(self):
801 return self.global_settings.get_all_settings()
799 return self.global_settings.get_all_settings()
802
800
803 def get_general_settings(self):
801 def get_general_settings(self):
804 if not self.repo_settings or self.inherit_global_settings:
802 if not self.repo_settings or self.inherit_global_settings:
805 return self.get_global_general_settings()
803 return self.get_global_general_settings()
806 else:
804 else:
807 return self.get_repo_general_settings()
805 return self.get_repo_general_settings()
808
806
809 def get_repos_location(self):
807 def get_repos_location(self):
810 return self.global_settings.get_ui_by_key('/').ui_value
808 return self.global_settings.get_ui_by_key('/').ui_value
811
809
812 def _filter_ui_settings(self, settings):
810 def _filter_ui_settings(self, settings):
813 filtered_settings = [
811 filtered_settings = [
814 s for s in settings if self._should_keep_setting(s)]
812 s for s in settings if self._should_keep_setting(s)]
815 return filtered_settings
813 return filtered_settings
816
814
817 def _should_keep_setting(self, setting):
815 def _should_keep_setting(self, setting):
818 keep = (
816 keep = (
819 (setting.section, setting.key) in self._ui_settings or
817 (setting.section, setting.key) in self._ui_settings or
820 setting.section in self._svn_sections)
818 setting.section in self._svn_sections)
821 return keep
819 return keep
822
820
823 def _filter_general_settings(self, settings):
821 def _filter_general_settings(self, settings):
824 keys = ['rhodecode_{}'.format(key) for key in self.GENERAL_SETTINGS]
822 keys = ['rhodecode_{}'.format(key) for key in self.GENERAL_SETTINGS]
825 return {
823 return {
826 k: settings[k]
824 k: settings[k]
827 for k in settings if k in keys}
825 for k in settings if k in keys}
828
826
829 def _collect_all_settings(self, global_=False):
827 def _collect_all_settings(self, global_=False):
830 settings = self.global_settings if global_ else self.repo_settings
828 settings = self.global_settings if global_ else self.repo_settings
831 result = {}
829 result = {}
832
830
833 for section, key in self._ui_settings:
831 for section, key in self._ui_settings:
834 ui = settings.get_ui_by_section_and_key(section, key)
832 ui = settings.get_ui_by_section_and_key(section, key)
835 result_key = self._get_form_ui_key(section, key)
833 result_key = self._get_form_ui_key(section, key)
836
834
837 if ui:
835 if ui:
838 if section in ('hooks', 'extensions'):
836 if section in ('hooks', 'extensions'):
839 result[result_key] = ui.ui_active
837 result[result_key] = ui.ui_active
840 elif result_key in ['vcs_git_lfs_enabled']:
838 elif result_key in ['vcs_git_lfs_enabled']:
841 result[result_key] = ui.ui_active
839 result[result_key] = ui.ui_active
842 else:
840 else:
843 result[result_key] = ui.ui_value
841 result[result_key] = ui.ui_value
844
842
845 for name in self.GENERAL_SETTINGS:
843 for name in self.GENERAL_SETTINGS:
846 setting = settings.get_setting_by_name(name)
844 setting = settings.get_setting_by_name(name)
847 if setting:
845 if setting:
848 result_key = 'rhodecode_{}'.format(name)
846 result_key = 'rhodecode_{}'.format(name)
849 result[result_key] = setting.app_settings_value
847 result[result_key] = setting.app_settings_value
850
848
851 return result
849 return result
852
850
853 def _get_form_ui_key(self, section, key):
851 def _get_form_ui_key(self, section, key):
854 return '{section}_{key}'.format(
852 return '{section}_{key}'.format(
855 section=section, key=key.replace('.', '_'))
853 section=section, key=key.replace('.', '_'))
856
854
857 def _create_or_update_ui(
855 def _create_or_update_ui(
858 self, settings, section, key, value=None, active=None):
856 self, settings, section, key, value=None, active=None):
859 ui = settings.get_ui_by_section_and_key(section, key)
857 ui = settings.get_ui_by_section_and_key(section, key)
860 if not ui:
858 if not ui:
861 active = True if active is None else active
859 active = True if active is None else active
862 settings.create_ui_section_value(
860 settings.create_ui_section_value(
863 section, value, key=key, active=active)
861 section, value, key=key, active=active)
864 else:
862 else:
865 if active is not None:
863 if active is not None:
866 ui.ui_active = active
864 ui.ui_active = active
867 if value is not None:
865 if value is not None:
868 ui.ui_value = value
866 ui.ui_value = value
869 Session().add(ui)
867 Session().add(ui)
870
868
871 def _create_svn_settings(self, settings, data):
869 def _create_svn_settings(self, settings, data):
872 svn_settings = {
870 svn_settings = {
873 'new_svn_branch': self.SVN_BRANCH_SECTION,
871 'new_svn_branch': self.SVN_BRANCH_SECTION,
874 'new_svn_tag': self.SVN_TAG_SECTION
872 'new_svn_tag': self.SVN_TAG_SECTION
875 }
873 }
876 for key in svn_settings:
874 for key in svn_settings:
877 if data.get(key):
875 if data.get(key):
878 settings.create_ui_section_value(svn_settings[key], data[key])
876 settings.create_ui_section_value(svn_settings[key], data[key])
879
877
880 def _create_or_update_general_settings(self, settings, data):
878 def _create_or_update_general_settings(self, settings, data):
881 for name in self.GENERAL_SETTINGS:
879 for name in self.GENERAL_SETTINGS:
882 data_key = 'rhodecode_{}'.format(name)
880 data_key = 'rhodecode_{}'.format(name)
883 if data_key not in data:
881 if data_key not in data:
884 raise ValueError(
882 raise ValueError(
885 'The given data does not contain {} key'.format(data_key))
883 'The given data does not contain {} key'.format(data_key))
886 setting = settings.create_or_update_setting(
884 setting = settings.create_or_update_setting(
887 name, data[data_key], 'bool')
885 name, data[data_key], 'bool')
888 Session().add(setting)
886 Session().add(setting)
889
887
890 def _get_settings_keys(self, settings, data):
888 def _get_settings_keys(self, settings, data):
891 data_keys = [self._get_form_ui_key(*s) for s in settings]
889 data_keys = [self._get_form_ui_key(*s) for s in settings]
892 for data_key in data_keys:
890 for data_key in data_keys:
893 if data_key not in data:
891 if data_key not in data:
894 raise ValueError(
892 raise ValueError(
895 'The given data does not contain {} key'.format(data_key))
893 'The given data does not contain {} key'.format(data_key))
896 return data_keys
894 return data_keys
897
895
898 def create_largeobjects_dirs_if_needed(self, repo_store_path):
896 def create_largeobjects_dirs_if_needed(self, repo_store_path):
899 """
897 """
900 This is subscribed to the `pyramid.events.ApplicationCreated` event. It
898 This is subscribed to the `pyramid.events.ApplicationCreated` event. It
901 does a repository scan if enabled in the settings.
899 does a repository scan if enabled in the settings.
902 """
900 """
903
901
904 from rhodecode.lib.vcs.backends.hg import largefiles_store
902 from rhodecode.lib.vcs.backends.hg import largefiles_store
905 from rhodecode.lib.vcs.backends.git import lfs_store
903 from rhodecode.lib.vcs.backends.git import lfs_store
906
904
907 paths = [
905 paths = [
908 largefiles_store(repo_store_path),
906 largefiles_store(repo_store_path),
909 lfs_store(repo_store_path)]
907 lfs_store(repo_store_path)]
910
908
911 for path in paths:
909 for path in paths:
912 if os.path.isdir(path):
910 if os.path.isdir(path):
913 continue
911 continue
914 if os.path.isfile(path):
912 if os.path.isfile(path):
915 continue
913 continue
916 # not a file nor dir, we try to create it
914 # not a file nor dir, we try to create it
917 try:
915 try:
918 os.makedirs(path)
916 os.makedirs(path)
919 except Exception:
917 except Exception:
920 log.warning('Failed to create largefiles dir:%s', path)
918 log.warning('Failed to create largefiles dir:%s', path)
General Comments 0
You need to be logged in to leave comments. Login now