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