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