##// END OF EJS Templates
svn-support: move into apps module.
marcink -
r1531:982f71c6 default
parent child Browse files
Show More
@@ -1,59 +1,59 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 import logging
22 22
23 23 from pyramid.view import view_config
24 24
25 25 from rhodecode.apps._base import BaseAppView
26 from rhodecode.svn_support.utils import generate_mod_dav_svn_config
26 from rhodecode.apps.svn_support.utils import generate_mod_dav_svn_config
27 27 from rhodecode.lib.auth import (
28 28 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
29 29
30 30 log = logging.getLogger(__name__)
31 31
32 32
33 33 class SvnConfigAdminSettingsView(BaseAppView):
34 34
35 35 @LoginRequired()
36 36 @CSRFRequired()
37 37 @HasPermissionAllDecorator('hg.admin')
38 38 @view_config(
39 39 route_name='admin_settings_vcs_svn_generate_cfg',
40 40 request_method='POST', renderer='json')
41 41 def vcs_svn_generate_config(self):
42 42 _ = self.request.translate
43 43 try:
44 44 generate_mod_dav_svn_config(self.request.registry)
45 45 msg = {
46 46 'message': _('Apache configuration for Subversion generated.'),
47 47 'level': 'success',
48 48 }
49 49 except Exception:
50 50 log.exception(
51 51 'Exception while generating the Apache '
52 52 'configuration for Subversion.')
53 53 msg = {
54 54 'message': _('Failed to generate the Apache configuration for Subversion.'),
55 55 'level': 'error',
56 56 }
57 57
58 58 data = {'message': msg}
59 59 return data
1 NO CONTENT: file renamed from rhodecode/svn_support/__init__.py to rhodecode/apps/svn_support/__init__.py
1 NO CONTENT: file renamed from rhodecode/svn_support/config_keys.py to rhodecode/apps/svn_support/config_keys.py
1 NO CONTENT: file renamed from rhodecode/svn_support/events.py to rhodecode/apps/svn_support/events.py
1 NO CONTENT: file renamed from rhodecode/svn_support/subscribers.py to rhodecode/apps/svn_support/subscribers.py
1 NO CONTENT: file renamed from rhodecode/svn_support/templates/mod-dav-svn.conf.mako to rhodecode/apps/svn_support/templates/mod-dav-svn.conf.mako
@@ -1,107 +1,107 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 mock
23 23 import pytest
24 24 import re
25 25
26 26 from pyramid import testing
27 27
28 from rhodecode.svn_support import utils
28 from rhodecode.apps.svn_support import utils
29 29
30 30
31 31 class TestModDavSvnConfig(object):
32 32
33 33 @classmethod
34 34 def setup_class(cls):
35 35 # Make mako renderer available in tests.
36 36 config = testing.setUp()
37 37 config.include('pyramid_mako')
38 38
39 39 cls.location_root = u'/location/root/ç¡Àâ'
40 40 cls.parent_path_root = u'/parent/path/ç¡Àâ'
41 41 cls.realm = u'Dummy Realm (Àâüç¡)'
42 42
43 43 @classmethod
44 44 def get_repo_group_mocks(cls, count=1):
45 45 repo_groups = []
46 46 for num in range(0, count):
47 47 full_path = u'/path/to/RepâGrâúp-°¡ {}'.format(num)
48 48 repo_group_mock = mock.MagicMock()
49 49 repo_group_mock.full_path = full_path
50 50 repo_group_mock.full_path_splitted = full_path.split('/')
51 51 repo_groups.append(repo_group_mock)
52 52 return repo_groups
53 53
54 54 def assert_root_location_directive(self, config):
55 55 pattern = u'<Location "{location}">'.format(
56 56 location=self.location_root)
57 57 assert len(re.findall(pattern, config)) == 1
58 58
59 59 def assert_group_location_directive(self, config, group_path):
60 60 pattern = u'<Location "{location}{group_path}">'.format(
61 61 location=self.location_root, group_path=group_path)
62 62 assert len(re.findall(pattern, config)) == 1
63 63
64 64 def test_render_mod_dav_svn_config(self):
65 65 repo_groups = self.get_repo_group_mocks(count=10)
66 66 generated_config = utils._render_mod_dav_svn_config(
67 67 parent_path_root=self.parent_path_root,
68 68 list_parent_path=True,
69 69 location_root=self.location_root,
70 70 repo_groups=repo_groups,
71 71 realm=self.realm,
72 72 use_ssl=True
73 73 )
74 74 # Assert that one location directive exists for each repository group.
75 75 for group in repo_groups:
76 76 self.assert_group_location_directive(
77 77 generated_config, group.full_path)
78 78
79 79 # Assert that the root location directive exists.
80 80 self.assert_root_location_directive(generated_config)
81 81
82 82 @pytest.mark.parametrize('list_parent_path', [True, False])
83 83 @pytest.mark.parametrize('use_ssl', [True, False])
84 84 def test_list_parent_path(self, list_parent_path, use_ssl):
85 85 generated_config = utils._render_mod_dav_svn_config(
86 86 parent_path_root=self.parent_path_root,
87 87 list_parent_path=list_parent_path,
88 88 location_root=self.location_root,
89 89 repo_groups=self.get_repo_group_mocks(count=10),
90 90 realm=self.realm,
91 91 use_ssl=use_ssl
92 92 )
93 93
94 94 # Assert that correct configuration directive is present.
95 95 if list_parent_path:
96 96 assert not re.search('SVNListParentPath\s+Off', generated_config)
97 97 assert re.search('SVNListParentPath\s+On', generated_config)
98 98 else:
99 99 assert re.search('SVNListParentPath\s+Off', generated_config)
100 100 assert not re.search('SVNListParentPath\s+On', generated_config)
101 101
102 102 if use_ssl:
103 103 assert 'RequestHeader edit Destination ^https: http: early' \
104 104 in generated_config
105 105 else:
106 106 assert '#RequestHeader edit Destination ^https: http: early' \
107 107 in generated_config
@@ -1,93 +1,93 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 import codecs
22 22 import logging
23 23 import os
24 24 from pyramid.renderers import render
25 25
26 26 from rhodecode.events import trigger
27 27 from rhodecode.lib.utils import get_rhodecode_realm, get_rhodecode_base_path
28 28 from rhodecode.lib.utils2 import str2bool
29 29 from rhodecode.model.db import RepoGroup
30 30
31 31 from . import config_keys
32 32 from .events import ModDavSvnConfigChange
33 33
34 34
35 35 log = logging.getLogger(__name__)
36 36
37 37
38 38 def generate_mod_dav_svn_config(registry):
39 39 """
40 40 Generate the configuration file for use with subversion's mod_dav_svn
41 41 module. The configuration has to contain a <Location> block for each
42 42 available repository group because the mod_dav_svn module does not support
43 43 repositories organized in sub folders.
44 44 """
45 45 settings = registry.settings
46 46 use_ssl = str2bool(registry.settings['force_https'])
47 47
48 48 config = _render_mod_dav_svn_config(
49 49 use_ssl=use_ssl,
50 50 parent_path_root=get_rhodecode_base_path(),
51 51 list_parent_path=settings[config_keys.list_parent_path],
52 52 location_root=settings[config_keys.location_root],
53 53 repo_groups=RepoGroup.get_all_repo_groups(),
54 54 realm=get_rhodecode_realm())
55 55 _write_mod_dav_svn_config(config, settings[config_keys.config_file_path])
56 56
57 57 # Trigger an event on mod dav svn configuration change.
58 58 trigger(ModDavSvnConfigChange(), registry)
59 59
60 60
61 61 def _render_mod_dav_svn_config(
62 62 parent_path_root, list_parent_path, location_root, repo_groups, realm,
63 63 use_ssl):
64 64 """
65 65 Render mod_dav_svn configuration to string.
66 66 """
67 67 repo_group_paths = []
68 68 for repo_group in repo_groups:
69 69 group_path = repo_group.full_path_splitted
70 70 location = os.path.join(location_root, *group_path)
71 71 parent_path = os.path.join(parent_path_root, *group_path)
72 72 repo_group_paths.append((location, parent_path))
73 73
74 74 context = {
75 75 'location_root': location_root,
76 76 'parent_path_root': parent_path_root,
77 77 'repo_group_paths': repo_group_paths,
78 78 'svn_list_parent_path': list_parent_path,
79 79 'rhodecode_realm': realm,
80 80 'use_https': use_ssl
81 81 }
82 82
83 83 # Render the configuration template to string.
84 template = 'rhodecode:svn_support/templates/mod-dav-svn.conf.mako'
84 template = 'rhodecode:apps/svn_support/templates/mod-dav-svn.conf.mako'
85 85 return render(template, context)
86 86
87 87
88 88 def _write_mod_dav_svn_config(config, filepath):
89 89 """
90 90 Write mod_dav_svn config to file.
91 91 """
92 92 with codecs.open(filepath, 'w', encoding='utf-8') as f:
93 93 f.write(config)
@@ -1,502 +1,502 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 from collections import OrderedDict
26 26
27 27 from paste.registry import RegistryManager
28 28 from paste.gzipper import make_gzip_middleware
29 29 from pylons.wsgiapp import PylonsApp
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 routes.util
40 40
41 41 import rhodecode
42 42 from rhodecode.model import meta
43 43 from rhodecode.config import patches
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 from rhodecode.lib.middleware import csrf
48 48 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
49 49 from rhodecode.lib.middleware.error_handling import (
50 50 PylonsErrorHandlingMiddleware)
51 51 from rhodecode.lib.middleware.https_fixup import HttpsFixup
52 52 from rhodecode.lib.middleware.vcs import VCSMiddleware
53 53 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
54 54 from rhodecode.lib.utils2 import aslist as rhodecode_aslist
55 55 from rhodecode.subscribers import (
56 56 scan_repositories_if_enabled, write_metadata_if_needed)
57 57
58 58
59 59 log = logging.getLogger(__name__)
60 60
61 61
62 62 # this is used to avoid avoid the route lookup overhead in routesmiddleware
63 63 # for certain routes which won't go to pylons to - eg. static files, debugger
64 64 # it is only needed for the pylons migration and can be removed once complete
65 65 class SkippableRoutesMiddleware(RoutesMiddleware):
66 66 """ Routes middleware that allows you to skip prefixes """
67 67
68 68 def __init__(self, *args, **kw):
69 69 self.skip_prefixes = kw.pop('skip_prefixes', [])
70 70 super(SkippableRoutesMiddleware, self).__init__(*args, **kw)
71 71
72 72 def __call__(self, environ, start_response):
73 73 for prefix in self.skip_prefixes:
74 74 if environ['PATH_INFO'].startswith(prefix):
75 75 # added to avoid the case when a missing /_static route falls
76 76 # through to pylons and causes an exception as pylons is
77 77 # expecting wsgiorg.routingargs to be set in the environ
78 78 # by RoutesMiddleware.
79 79 if 'wsgiorg.routing_args' not in environ:
80 80 environ['wsgiorg.routing_args'] = (None, {})
81 81 return self.app(environ, start_response)
82 82
83 83 return super(SkippableRoutesMiddleware, self).__call__(
84 84 environ, start_response)
85 85
86 86
87 87 def make_app(global_conf, static_files=True, **app_conf):
88 88 """Create a Pylons WSGI application and return it
89 89
90 90 ``global_conf``
91 91 The inherited configuration for this application. Normally from
92 92 the [DEFAULT] section of the Paste ini file.
93 93
94 94 ``app_conf``
95 95 The application's local configuration. Normally specified in
96 96 the [app:<name>] section of the Paste ini file (where <name>
97 97 defaults to main).
98 98
99 99 """
100 100 # Apply compatibility patches
101 101 patches.kombu_1_5_1_python_2_7_11()
102 102 patches.inspect_getargspec()
103 103
104 104 # Configure the Pylons environment
105 105 config = load_environment(global_conf, app_conf)
106 106
107 107 # The Pylons WSGI app
108 108 app = PylonsApp(config=config)
109 109 if rhodecode.is_test:
110 110 app = csrf.CSRFDetector(app)
111 111
112 112 expected_origin = config.get('expected_origin')
113 113 if expected_origin:
114 114 # The API can be accessed from other Origins.
115 115 app = csrf.OriginChecker(app, expected_origin,
116 116 skip_urls=[routes.util.url_for('api')])
117 117
118 118 # Establish the Registry for this application
119 119 app = RegistryManager(app)
120 120
121 121 app.config = config
122 122
123 123 return app
124 124
125 125
126 126 def make_pyramid_app(global_config, **settings):
127 127 """
128 128 Constructs the WSGI application based on Pyramid and wraps the Pylons based
129 129 application.
130 130
131 131 Specials:
132 132
133 133 * We migrate from Pylons to Pyramid. While doing this, we keep both
134 134 frameworks functional. This involves moving some WSGI middlewares around
135 135 and providing access to some data internals, so that the old code is
136 136 still functional.
137 137
138 138 * The application can also be integrated like a plugin via the call to
139 139 `includeme`. This is accompanied with the other utility functions which
140 140 are called. Changing this should be done with great care to not break
141 141 cases when these fragments are assembled from another place.
142 142
143 143 """
144 144 # The edition string should be available in pylons too, so we add it here
145 145 # before copying the settings.
146 146 settings.setdefault('rhodecode.edition', 'Community Edition')
147 147
148 148 # As long as our Pylons application does expect "unprepared" settings, make
149 149 # sure that we keep an unmodified copy. This avoids unintentional change of
150 150 # behavior in the old application.
151 151 settings_pylons = settings.copy()
152 152
153 153 sanitize_settings_and_apply_defaults(settings)
154 154 config = Configurator(settings=settings)
155 155 add_pylons_compat_data(config.registry, global_config, settings_pylons)
156 156
157 157 load_pyramid_environment(global_config, settings)
158 158
159 159 includeme_first(config)
160 160 includeme(config)
161 161 pyramid_app = config.make_wsgi_app()
162 162 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
163 163 pyramid_app.config = config
164 164
165 165 # creating the app uses a connection - return it after we are done
166 166 meta.Session.remove()
167 167
168 168 return pyramid_app
169 169
170 170
171 171 def make_not_found_view(config):
172 172 """
173 173 This creates the view which should be registered as not-found-view to
174 174 pyramid. Basically it contains of the old pylons app, converted to a view.
175 175 Additionally it is wrapped by some other middlewares.
176 176 """
177 177 settings = config.registry.settings
178 178 vcs_server_enabled = settings['vcs.server.enable']
179 179
180 180 # Make pylons app from unprepared settings.
181 181 pylons_app = make_app(
182 182 config.registry._pylons_compat_global_config,
183 183 **config.registry._pylons_compat_settings)
184 184 config.registry._pylons_compat_config = pylons_app.config
185 185
186 186 # Appenlight monitoring.
187 187 pylons_app, appenlight_client = wrap_in_appenlight_if_enabled(
188 188 pylons_app, settings)
189 189
190 190 # The pylons app is executed inside of the pyramid 404 exception handler.
191 191 # Exceptions which are raised inside of it are not handled by pyramid
192 192 # again. Therefore we add a middleware that invokes the error handler in
193 193 # case of an exception or error response. This way we return proper error
194 194 # HTML pages in case of an error.
195 195 reraise = (settings.get('debugtoolbar.enabled', False) or
196 196 rhodecode.disable_error_handler)
197 197 pylons_app = PylonsErrorHandlingMiddleware(
198 198 pylons_app, error_handler, reraise)
199 199
200 200 # The VCSMiddleware shall operate like a fallback if pyramid doesn't find a
201 201 # view to handle the request. Therefore it is wrapped around the pylons
202 202 # app. It has to be outside of the error handling otherwise error responses
203 203 # from the vcsserver are converted to HTML error pages. This confuses the
204 204 # command line tools and the user won't get a meaningful error message.
205 205 if vcs_server_enabled:
206 206 pylons_app = VCSMiddleware(
207 207 pylons_app, settings, appenlight_client, registry=config.registry)
208 208
209 209 # Convert WSGI app to pyramid view and return it.
210 210 return wsgiapp(pylons_app)
211 211
212 212
213 213 def add_pylons_compat_data(registry, global_config, settings):
214 214 """
215 215 Attach data to the registry to support the Pylons integration.
216 216 """
217 217 registry._pylons_compat_global_config = global_config
218 218 registry._pylons_compat_settings = settings
219 219
220 220
221 221 def error_handler(exception, request):
222 222 import rhodecode
223 223 from rhodecode.lib.utils2 import AttributeDict
224 224
225 225 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
226 226
227 227 base_response = HTTPInternalServerError()
228 228 # prefer original exception for the response since it may have headers set
229 229 if isinstance(exception, HTTPException):
230 230 base_response = exception
231 231
232 232 def is_http_error(response):
233 233 # error which should have traceback
234 234 return response.status_code > 499
235 235
236 236 if is_http_error(base_response):
237 237 log.exception(
238 238 'error occurred handling this request for path: %s', request.path)
239 239
240 240 c = AttributeDict()
241 241 c.error_message = base_response.status
242 242 c.error_explanation = base_response.explanation or str(base_response)
243 243 c.visual = AttributeDict()
244 244
245 245 c.visual.rhodecode_support_url = (
246 246 request.registry.settings.get('rhodecode_support_url') or
247 247 request.route_url('rhodecode_support')
248 248 )
249 249 c.redirect_time = 0
250 250 c.rhodecode_name = rhodecode_title
251 251 if not c.rhodecode_name:
252 252 c.rhodecode_name = 'Rhodecode'
253 253
254 254 c.causes = []
255 255 if hasattr(base_response, 'causes'):
256 256 c.causes = base_response.causes
257 257
258 258 response = render_to_response(
259 259 '/errors/error_document.mako', {'c': c}, request=request,
260 260 response=base_response)
261 261
262 262 return response
263 263
264 264
265 265 def includeme(config):
266 266 settings = config.registry.settings
267 267
268 268 # plugin information
269 269 config.registry.rhodecode_plugins = OrderedDict()
270 270
271 271 config.add_directive(
272 272 'register_rhodecode_plugin', register_rhodecode_plugin)
273 273
274 274 if asbool(settings.get('appenlight', 'false')):
275 275 config.include('appenlight_client.ext.pyramid_tween')
276 276
277 277 # Includes which are required. The application would fail without them.
278 278 config.include('pyramid_mako')
279 279 config.include('pyramid_beaker')
280 280
281 281 config.include('rhodecode.authentication')
282 282 config.include('rhodecode.integrations')
283 283
284 284 # apps
285 285 config.include('rhodecode.apps.admin')
286 286 config.include('rhodecode.apps.channelstream')
287 287 config.include('rhodecode.apps.login')
288 288 config.include('rhodecode.apps.user_profile')
289 289 config.include('rhodecode.apps.my_account')
290 config.include('rhodecode.apps.svn_support')
290 291
291 292 config.include('rhodecode.tweens')
292 293 config.include('rhodecode.api')
293 config.include('rhodecode.svn_support')
294 294
295 295 config.add_route(
296 296 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
297 297
298 298 config.add_translation_dirs('rhodecode:i18n/')
299 299 settings['default_locale_name'] = settings.get('lang', 'en')
300 300
301 301 # Add subscribers.
302 302 config.add_subscriber(scan_repositories_if_enabled, ApplicationCreated)
303 303 config.add_subscriber(write_metadata_if_needed, ApplicationCreated)
304 304
305 305 # Set the authorization policy.
306 306 authz_policy = ACLAuthorizationPolicy()
307 307 config.set_authorization_policy(authz_policy)
308 308
309 309 # Set the default renderer for HTML templates to mako.
310 310 config.add_mako_renderer('.html')
311 311
312 312 # include RhodeCode plugins
313 313 includes = aslist(settings.get('rhodecode.includes', []))
314 314 for inc in includes:
315 315 config.include(inc)
316 316
317 317 # This is the glue which allows us to migrate in chunks. By registering the
318 318 # pylons based application as the "Not Found" view in Pyramid, we will
319 319 # fallback to the old application each time the new one does not yet know
320 320 # how to handle a request.
321 321 config.add_notfound_view(make_not_found_view(config))
322 322
323 323 if not settings.get('debugtoolbar.enabled', False):
324 324 # if no toolbar, then any exception gets caught and rendered
325 325 config.add_view(error_handler, context=Exception)
326 326
327 327 config.add_view(error_handler, context=HTTPError)
328 328
329 329
330 330 def includeme_first(config):
331 331 # redirect automatic browser favicon.ico requests to correct place
332 332 def favicon_redirect(context, request):
333 333 return HTTPFound(
334 334 request.static_path('rhodecode:public/images/favicon.ico'))
335 335
336 336 config.add_view(favicon_redirect, route_name='favicon')
337 337 config.add_route('favicon', '/favicon.ico')
338 338
339 339 def robots_redirect(context, request):
340 340 return HTTPFound(
341 341 request.static_path('rhodecode:public/robots.txt'))
342 342
343 343 config.add_view(robots_redirect, route_name='robots')
344 344 config.add_route('robots', '/robots.txt')
345 345
346 346 config.add_static_view(
347 347 '_static/deform', 'deform:static')
348 348 config.add_static_view(
349 349 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
350 350
351 351
352 352 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
353 353 """
354 354 Apply outer WSGI middlewares around the application.
355 355
356 356 Part of this has been moved up from the Pylons layer, so that the
357 357 data is also available if old Pylons code is hit through an already ported
358 358 view.
359 359 """
360 360 settings = config.registry.settings
361 361
362 362 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
363 363 pyramid_app = HttpsFixup(pyramid_app, settings)
364 364
365 365 # Add RoutesMiddleware to support the pylons compatibility tween during
366 366 # migration to pyramid.
367 367 pyramid_app = SkippableRoutesMiddleware(
368 368 pyramid_app, config.registry._pylons_compat_config['routes.map'],
369 369 skip_prefixes=(STATIC_FILE_PREFIX, '/_debug_toolbar'))
370 370
371 371 pyramid_app, _ = wrap_in_appenlight_if_enabled(pyramid_app, settings)
372 372
373 373 if settings['gzip_responses']:
374 374 pyramid_app = make_gzip_middleware(
375 375 pyramid_app, settings, compress_level=1)
376 376
377 377 # this should be the outer most middleware in the wsgi stack since
378 378 # middleware like Routes make database calls
379 379 def pyramid_app_with_cleanup(environ, start_response):
380 380 try:
381 381 return pyramid_app(environ, start_response)
382 382 finally:
383 383 # Dispose current database session and rollback uncommitted
384 384 # transactions.
385 385 meta.Session.remove()
386 386
387 387 # In a single threaded mode server, on non sqlite db we should have
388 388 # '0 Current Checked out connections' at the end of a request,
389 389 # if not, then something, somewhere is leaving a connection open
390 390 pool = meta.Base.metadata.bind.engine.pool
391 391 log.debug('sa pool status: %s', pool.status())
392 392
393 393
394 394 return pyramid_app_with_cleanup
395 395
396 396
397 397 def sanitize_settings_and_apply_defaults(settings):
398 398 """
399 399 Applies settings defaults and does all type conversion.
400 400
401 401 We would move all settings parsing and preparation into this place, so that
402 402 we have only one place left which deals with this part. The remaining parts
403 403 of the application would start to rely fully on well prepared settings.
404 404
405 405 This piece would later be split up per topic to avoid a big fat monster
406 406 function.
407 407 """
408 408
409 409 # Pyramid's mako renderer has to search in the templates folder so that the
410 410 # old templates still work. Ported and new templates are expected to use
411 411 # real asset specifications for the includes.
412 412 mako_directories = settings.setdefault('mako.directories', [
413 413 # Base templates of the original Pylons application
414 414 'rhodecode:templates',
415 415 ])
416 416 log.debug(
417 417 "Using the following Mako template directories: %s",
418 418 mako_directories)
419 419
420 420 # Default includes, possible to change as a user
421 421 pyramid_includes = settings.setdefault('pyramid.includes', [
422 422 'rhodecode.lib.middleware.request_wrapper',
423 423 ])
424 424 log.debug(
425 425 "Using the following pyramid.includes: %s",
426 426 pyramid_includes)
427 427
428 428 # TODO: johbo: Re-think this, usually the call to config.include
429 429 # should allow to pass in a prefix.
430 430 settings.setdefault('rhodecode.api.url', '/_admin/api')
431 431
432 432 # Sanitize generic settings.
433 433 _list_setting(settings, 'default_encoding', 'UTF-8')
434 434 _bool_setting(settings, 'is_test', 'false')
435 435 _bool_setting(settings, 'gzip_responses', 'false')
436 436
437 437 # Call split out functions that sanitize settings for each topic.
438 438 _sanitize_appenlight_settings(settings)
439 439 _sanitize_vcs_settings(settings)
440 440
441 441 return settings
442 442
443 443
444 444 def _sanitize_appenlight_settings(settings):
445 445 _bool_setting(settings, 'appenlight', 'false')
446 446
447 447
448 448 def _sanitize_vcs_settings(settings):
449 449 """
450 450 Applies settings defaults and does type conversion for all VCS related
451 451 settings.
452 452 """
453 453 _string_setting(settings, 'vcs.svn.compatible_version', '')
454 454 _string_setting(settings, 'git_rev_filter', '--all')
455 455 _string_setting(settings, 'vcs.hooks.protocol', 'http')
456 456 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
457 457 _string_setting(settings, 'vcs.server', '')
458 458 _string_setting(settings, 'vcs.server.log_level', 'debug')
459 459 _string_setting(settings, 'vcs.server.protocol', 'http')
460 460 _bool_setting(settings, 'startup.import_repos', 'false')
461 461 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
462 462 _bool_setting(settings, 'vcs.server.enable', 'true')
463 463 _bool_setting(settings, 'vcs.start_server', 'false')
464 464 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
465 465 _int_setting(settings, 'vcs.connection_timeout', 3600)
466 466
467 467 # Support legacy values of vcs.scm_app_implementation. Legacy
468 468 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http'
469 469 # which is now mapped to 'http'.
470 470 scm_app_impl = settings['vcs.scm_app_implementation']
471 471 if scm_app_impl == 'rhodecode.lib.middleware.utils.scm_app_http':
472 472 settings['vcs.scm_app_implementation'] = 'http'
473 473
474 474
475 475 def _int_setting(settings, name, default):
476 476 settings[name] = int(settings.get(name, default))
477 477
478 478
479 479 def _bool_setting(settings, name, default):
480 480 input = settings.get(name, default)
481 481 if isinstance(input, unicode):
482 482 input = input.encode('utf8')
483 483 settings[name] = asbool(input)
484 484
485 485
486 486 def _list_setting(settings, name, default):
487 487 raw_value = settings.get(name, default)
488 488
489 489 old_separator = ','
490 490 if old_separator in raw_value:
491 491 # If we get a comma separated list, pass it to our own function.
492 492 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
493 493 else:
494 494 # Otherwise we assume it uses pyramids space/newline separation.
495 495 settings[name] = aslist(raw_value)
496 496
497 497
498 498 def _string_setting(settings, name, default, lower=True):
499 499 value = settings.get(name, default)
500 500 if lower:
501 501 value = value.lower()
502 502 settings[name] = value
@@ -1,692 +1,692 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 pyramid.threadlocal import get_current_registry
36 36 from webob.exc import HTTPBadRequest
37 37
38 38 import rhodecode
39 39 from rhodecode.apps.admin.navigation import navigation_list
40 from rhodecode.apps.svn_support.config_keys import generate_config
40 41 from rhodecode.lib import auth
41 42 from rhodecode.lib import helpers as h
42 43 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
43 44 from rhodecode.lib.base import BaseController, render
44 45 from rhodecode.lib.celerylib import tasks, run_task
45 46 from rhodecode.lib.utils import repo2db_mapper
46 47 from rhodecode.lib.utils2 import (
47 48 str2bool, safe_unicode, AttributeDict, safe_int)
48 49 from rhodecode.lib.compat import OrderedDict
49 50 from rhodecode.lib.utils import jsonify
50 51
51 52 from rhodecode.model.db import RhodeCodeUi, Repository
52 53 from rhodecode.model.forms import ApplicationSettingsForm, \
53 54 ApplicationUiSettingsForm, ApplicationVisualisationForm, \
54 55 LabsSettingsForm, IssueTrackerPatternsForm
55 56 from rhodecode.model.repo_group import RepoGroupModel
56 57
57 58 from rhodecode.model.scm import ScmModel
58 59 from rhodecode.model.notification import EmailNotificationModel
59 60 from rhodecode.model.meta import Session
60 61 from rhodecode.model.settings import (
61 62 IssueTrackerSettingsModel, VcsSettingsModel, SettingNotFound,
62 63 SettingsModel)
63 64
64 65 from rhodecode.model.supervisor import SupervisorModel, SUPERVISOR_MASTER
65 from rhodecode.svn_support.config_keys import generate_config
66 66
67 67
68 68 log = logging.getLogger(__name__)
69 69
70 70
71 71 class SettingsController(BaseController):
72 72 """REST Controller styled on the Atom Publishing Protocol"""
73 73 # To properly map this controller, ensure your config/routing.py
74 74 # file has a resource setup:
75 75 # map.resource('setting', 'settings', controller='admin/settings',
76 76 # path_prefix='/admin', name_prefix='admin_')
77 77
78 78 @LoginRequired()
79 79 def __before__(self):
80 80 super(SettingsController, self).__before__()
81 81 c.labs_active = str2bool(
82 82 rhodecode.CONFIG.get('labs_settings_active', 'true'))
83 83 c.navlist = navigation_list(request)
84 84
85 85 def _get_hg_ui_settings(self):
86 86 ret = RhodeCodeUi.query().all()
87 87
88 88 if not ret:
89 89 raise Exception('Could not get application ui settings !')
90 90 settings = {}
91 91 for each in ret:
92 92 k = each.ui_key
93 93 v = each.ui_value
94 94 if k == '/':
95 95 k = 'root_path'
96 96
97 97 if k in ['push_ssl', 'publish']:
98 98 v = str2bool(v)
99 99
100 100 if k.find('.') != -1:
101 101 k = k.replace('.', '_')
102 102
103 103 if each.ui_section in ['hooks', 'extensions']:
104 104 v = each.ui_active
105 105
106 106 settings[each.ui_section + '_' + k] = v
107 107 return settings
108 108
109 109 @HasPermissionAllDecorator('hg.admin')
110 110 @auth.CSRFRequired()
111 111 @jsonify
112 112 def delete_svn_pattern(self):
113 113 if not request.is_xhr:
114 114 raise HTTPBadRequest()
115 115
116 116 delete_pattern_id = request.POST.get('delete_svn_pattern')
117 117 model = VcsSettingsModel()
118 118 try:
119 119 model.delete_global_svn_pattern(delete_pattern_id)
120 120 except SettingNotFound:
121 121 raise HTTPBadRequest()
122 122
123 123 Session().commit()
124 124 return True
125 125
126 126 @HasPermissionAllDecorator('hg.admin')
127 127 @auth.CSRFRequired()
128 128 def settings_vcs_update(self):
129 129 """POST /admin/settings: All items in the collection"""
130 130 # url('admin_settings_vcs')
131 131 c.active = 'vcs'
132 132
133 133 model = VcsSettingsModel()
134 134 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
135 135 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
136 136
137 137 # TODO: Replace with request.registry after migrating to pyramid.
138 138 pyramid_settings = get_current_registry().settings
139 139 c.svn_proxy_generate_config = pyramid_settings[generate_config]
140 140
141 141 application_form = ApplicationUiSettingsForm()()
142 142
143 143 try:
144 144 form_result = application_form.to_python(dict(request.POST))
145 145 except formencode.Invalid as errors:
146 146 h.flash(
147 147 _("Some form inputs contain invalid data."),
148 148 category='error')
149 149 return htmlfill.render(
150 150 render('admin/settings/settings.mako'),
151 151 defaults=errors.value,
152 152 errors=errors.error_dict or {},
153 153 prefix_error=False,
154 154 encoding="UTF-8",
155 155 force_defaults=False
156 156 )
157 157
158 158 try:
159 159 if c.visual.allow_repo_location_change:
160 160 model.update_global_path_setting(
161 161 form_result['paths_root_path'])
162 162
163 163 model.update_global_ssl_setting(form_result['web_push_ssl'])
164 164 model.update_global_hook_settings(form_result)
165 165
166 166 model.create_or_update_global_svn_settings(form_result)
167 167 model.create_or_update_global_hg_settings(form_result)
168 168 model.create_or_update_global_pr_settings(form_result)
169 169 except Exception:
170 170 log.exception("Exception while updating settings")
171 171 h.flash(_('Error occurred during updating '
172 172 'application settings'), category='error')
173 173 else:
174 174 Session().commit()
175 175 h.flash(_('Updated VCS settings'), category='success')
176 176 return redirect(url('admin_settings_vcs'))
177 177
178 178 return htmlfill.render(
179 179 render('admin/settings/settings.mako'),
180 180 defaults=self._form_defaults(),
181 181 encoding="UTF-8",
182 182 force_defaults=False)
183 183
184 184 @HasPermissionAllDecorator('hg.admin')
185 185 def settings_vcs(self):
186 186 """GET /admin/settings: All items in the collection"""
187 187 # url('admin_settings_vcs')
188 188 c.active = 'vcs'
189 189 model = VcsSettingsModel()
190 190 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
191 191 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
192 192
193 193 # TODO: Replace with request.registry after migrating to pyramid.
194 194 pyramid_settings = get_current_registry().settings
195 195 c.svn_proxy_generate_config = pyramid_settings[generate_config]
196 196
197 197 return htmlfill.render(
198 198 render('admin/settings/settings.mako'),
199 199 defaults=self._form_defaults(),
200 200 encoding="UTF-8",
201 201 force_defaults=False)
202 202
203 203 @HasPermissionAllDecorator('hg.admin')
204 204 @auth.CSRFRequired()
205 205 def settings_mapping_update(self):
206 206 """POST /admin/settings/mapping: All items in the collection"""
207 207 # url('admin_settings_mapping')
208 208 c.active = 'mapping'
209 209 rm_obsolete = request.POST.get('destroy', False)
210 210 invalidate_cache = request.POST.get('invalidate', False)
211 211 log.debug(
212 212 'rescanning repo location with destroy obsolete=%s', rm_obsolete)
213 213
214 214 if invalidate_cache:
215 215 log.debug('invalidating all repositories cache')
216 216 for repo in Repository.get_all():
217 217 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
218 218
219 219 filesystem_repos = ScmModel().repo_scan()
220 220 added, removed = repo2db_mapper(filesystem_repos, rm_obsolete)
221 221 _repr = lambda l: ', '.join(map(safe_unicode, l)) or '-'
222 222 h.flash(_('Repositories successfully '
223 223 'rescanned added: %s ; removed: %s') %
224 224 (_repr(added), _repr(removed)),
225 225 category='success')
226 226 return redirect(url('admin_settings_mapping'))
227 227
228 228 @HasPermissionAllDecorator('hg.admin')
229 229 def settings_mapping(self):
230 230 """GET /admin/settings/mapping: All items in the collection"""
231 231 # url('admin_settings_mapping')
232 232 c.active = 'mapping'
233 233
234 234 return htmlfill.render(
235 235 render('admin/settings/settings.mako'),
236 236 defaults=self._form_defaults(),
237 237 encoding="UTF-8",
238 238 force_defaults=False)
239 239
240 240 @HasPermissionAllDecorator('hg.admin')
241 241 @auth.CSRFRequired()
242 242 def settings_global_update(self):
243 243 """POST /admin/settings/global: All items in the collection"""
244 244 # url('admin_settings_global')
245 245 c.active = 'global'
246 246 c.personal_repo_group_default_pattern = RepoGroupModel()\
247 247 .get_personal_group_name_pattern()
248 248 application_form = ApplicationSettingsForm()()
249 249 try:
250 250 form_result = application_form.to_python(dict(request.POST))
251 251 except formencode.Invalid as errors:
252 252 return htmlfill.render(
253 253 render('admin/settings/settings.mako'),
254 254 defaults=errors.value,
255 255 errors=errors.error_dict or {},
256 256 prefix_error=False,
257 257 encoding="UTF-8",
258 258 force_defaults=False)
259 259
260 260 try:
261 261 settings = [
262 262 ('title', 'rhodecode_title', 'unicode'),
263 263 ('realm', 'rhodecode_realm', 'unicode'),
264 264 ('pre_code', 'rhodecode_pre_code', 'unicode'),
265 265 ('post_code', 'rhodecode_post_code', 'unicode'),
266 266 ('captcha_public_key', 'rhodecode_captcha_public_key', 'unicode'),
267 267 ('captcha_private_key', 'rhodecode_captcha_private_key', 'unicode'),
268 268 ('create_personal_repo_group', 'rhodecode_create_personal_repo_group', 'bool'),
269 269 ('personal_repo_group_pattern', 'rhodecode_personal_repo_group_pattern', 'unicode'),
270 270 ]
271 271 for setting, form_key, type_ in settings:
272 272 sett = SettingsModel().create_or_update_setting(
273 273 setting, form_result[form_key], type_)
274 274 Session().add(sett)
275 275
276 276 Session().commit()
277 277 SettingsModel().invalidate_settings_cache()
278 278 h.flash(_('Updated application settings'), category='success')
279 279 except Exception:
280 280 log.exception("Exception while updating application settings")
281 281 h.flash(
282 282 _('Error occurred during updating application settings'),
283 283 category='error')
284 284
285 285 return redirect(url('admin_settings_global'))
286 286
287 287 @HasPermissionAllDecorator('hg.admin')
288 288 def settings_global(self):
289 289 """GET /admin/settings/global: All items in the collection"""
290 290 # url('admin_settings_global')
291 291 c.active = 'global'
292 292 c.personal_repo_group_default_pattern = RepoGroupModel()\
293 293 .get_personal_group_name_pattern()
294 294
295 295 return htmlfill.render(
296 296 render('admin/settings/settings.mako'),
297 297 defaults=self._form_defaults(),
298 298 encoding="UTF-8",
299 299 force_defaults=False)
300 300
301 301 @HasPermissionAllDecorator('hg.admin')
302 302 @auth.CSRFRequired()
303 303 def settings_visual_update(self):
304 304 """POST /admin/settings/visual: All items in the collection"""
305 305 # url('admin_settings_visual')
306 306 c.active = 'visual'
307 307 application_form = ApplicationVisualisationForm()()
308 308 try:
309 309 form_result = application_form.to_python(dict(request.POST))
310 310 except formencode.Invalid as errors:
311 311 return htmlfill.render(
312 312 render('admin/settings/settings.mako'),
313 313 defaults=errors.value,
314 314 errors=errors.error_dict or {},
315 315 prefix_error=False,
316 316 encoding="UTF-8",
317 317 force_defaults=False
318 318 )
319 319
320 320 try:
321 321 settings = [
322 322 ('show_public_icon', 'rhodecode_show_public_icon', 'bool'),
323 323 ('show_private_icon', 'rhodecode_show_private_icon', 'bool'),
324 324 ('stylify_metatags', 'rhodecode_stylify_metatags', 'bool'),
325 325 ('repository_fields', 'rhodecode_repository_fields', 'bool'),
326 326 ('dashboard_items', 'rhodecode_dashboard_items', 'int'),
327 327 ('admin_grid_items', 'rhodecode_admin_grid_items', 'int'),
328 328 ('show_version', 'rhodecode_show_version', 'bool'),
329 329 ('use_gravatar', 'rhodecode_use_gravatar', 'bool'),
330 330 ('markup_renderer', 'rhodecode_markup_renderer', 'unicode'),
331 331 ('gravatar_url', 'rhodecode_gravatar_url', 'unicode'),
332 332 ('clone_uri_tmpl', 'rhodecode_clone_uri_tmpl', 'unicode'),
333 333 ('support_url', 'rhodecode_support_url', 'unicode'),
334 334 ('show_revision_number', 'rhodecode_show_revision_number', 'bool'),
335 335 ('show_sha_length', 'rhodecode_show_sha_length', 'int'),
336 336 ]
337 337 for setting, form_key, type_ in settings:
338 338 sett = SettingsModel().create_or_update_setting(
339 339 setting, form_result[form_key], type_)
340 340 Session().add(sett)
341 341
342 342 Session().commit()
343 343 SettingsModel().invalidate_settings_cache()
344 344 h.flash(_('Updated visualisation settings'), category='success')
345 345 except Exception:
346 346 log.exception("Exception updating visualization settings")
347 347 h.flash(_('Error occurred during updating '
348 348 'visualisation settings'),
349 349 category='error')
350 350
351 351 return redirect(url('admin_settings_visual'))
352 352
353 353 @HasPermissionAllDecorator('hg.admin')
354 354 def settings_visual(self):
355 355 """GET /admin/settings/visual: All items in the collection"""
356 356 # url('admin_settings_visual')
357 357 c.active = 'visual'
358 358
359 359 return htmlfill.render(
360 360 render('admin/settings/settings.mako'),
361 361 defaults=self._form_defaults(),
362 362 encoding="UTF-8",
363 363 force_defaults=False)
364 364
365 365 @HasPermissionAllDecorator('hg.admin')
366 366 @auth.CSRFRequired()
367 367 def settings_issuetracker_test(self):
368 368 if request.is_xhr:
369 369 return h.urlify_commit_message(
370 370 request.POST.get('test_text', ''),
371 371 'repo_group/test_repo1')
372 372 else:
373 373 raise HTTPBadRequest()
374 374
375 375 @HasPermissionAllDecorator('hg.admin')
376 376 @auth.CSRFRequired()
377 377 def settings_issuetracker_delete(self):
378 378 uid = request.POST.get('uid')
379 379 IssueTrackerSettingsModel().delete_entries(uid)
380 380 h.flash(_('Removed issue tracker entry'), category='success')
381 381 return redirect(url('admin_settings_issuetracker'))
382 382
383 383 @HasPermissionAllDecorator('hg.admin')
384 384 def settings_issuetracker(self):
385 385 """GET /admin/settings/issue-tracker: All items in the collection"""
386 386 # url('admin_settings_issuetracker')
387 387 c.active = 'issuetracker'
388 388 defaults = SettingsModel().get_all_settings()
389 389
390 390 entry_key = 'rhodecode_issuetracker_pat_'
391 391
392 392 c.issuetracker_entries = {}
393 393 for k, v in defaults.items():
394 394 if k.startswith(entry_key):
395 395 uid = k[len(entry_key):]
396 396 c.issuetracker_entries[uid] = None
397 397
398 398 for uid in c.issuetracker_entries:
399 399 c.issuetracker_entries[uid] = AttributeDict({
400 400 'pat': defaults.get('rhodecode_issuetracker_pat_' + uid),
401 401 'url': defaults.get('rhodecode_issuetracker_url_' + uid),
402 402 'pref': defaults.get('rhodecode_issuetracker_pref_' + uid),
403 403 'desc': defaults.get('rhodecode_issuetracker_desc_' + uid),
404 404 })
405 405
406 406 return render('admin/settings/settings.mako')
407 407
408 408 @HasPermissionAllDecorator('hg.admin')
409 409 @auth.CSRFRequired()
410 410 def settings_issuetracker_save(self):
411 411 settings_model = IssueTrackerSettingsModel()
412 412
413 413 form = IssueTrackerPatternsForm()().to_python(request.POST)
414 414 if form:
415 415 for uid in form.get('delete_patterns', []):
416 416 settings_model.delete_entries(uid)
417 417
418 418 for pattern in form.get('patterns', []):
419 419 for setting, value, type_ in pattern:
420 420 sett = settings_model.create_or_update_setting(
421 421 setting, value, type_)
422 422 Session().add(sett)
423 423
424 424 Session().commit()
425 425
426 426 SettingsModel().invalidate_settings_cache()
427 427 h.flash(_('Updated issue tracker entries'), category='success')
428 428 return redirect(url('admin_settings_issuetracker'))
429 429
430 430 @HasPermissionAllDecorator('hg.admin')
431 431 @auth.CSRFRequired()
432 432 def settings_email_update(self):
433 433 """POST /admin/settings/email: All items in the collection"""
434 434 # url('admin_settings_email')
435 435 c.active = 'email'
436 436
437 437 test_email = request.POST.get('test_email')
438 438
439 439 if not test_email:
440 440 h.flash(_('Please enter email address'), category='error')
441 441 return redirect(url('admin_settings_email'))
442 442
443 443 email_kwargs = {
444 444 'date': datetime.datetime.now(),
445 445 'user': c.rhodecode_user,
446 446 'rhodecode_version': c.rhodecode_version
447 447 }
448 448
449 449 (subject, headers, email_body,
450 450 email_body_plaintext) = EmailNotificationModel().render_email(
451 451 EmailNotificationModel.TYPE_EMAIL_TEST, **email_kwargs)
452 452
453 453 recipients = [test_email] if test_email else None
454 454
455 455 run_task(tasks.send_email, recipients, subject,
456 456 email_body_plaintext, email_body)
457 457
458 458 h.flash(_('Send email task created'), category='success')
459 459 return redirect(url('admin_settings_email'))
460 460
461 461 @HasPermissionAllDecorator('hg.admin')
462 462 def settings_email(self):
463 463 """GET /admin/settings/email: All items in the collection"""
464 464 # url('admin_settings_email')
465 465 c.active = 'email'
466 466 c.rhodecode_ini = rhodecode.CONFIG
467 467
468 468 return htmlfill.render(
469 469 render('admin/settings/settings.mako'),
470 470 defaults=self._form_defaults(),
471 471 encoding="UTF-8",
472 472 force_defaults=False)
473 473
474 474 @HasPermissionAllDecorator('hg.admin')
475 475 @auth.CSRFRequired()
476 476 def settings_hooks_update(self):
477 477 """POST or DELETE /admin/settings/hooks: All items in the collection"""
478 478 # url('admin_settings_hooks')
479 479 c.active = 'hooks'
480 480 if c.visual.allow_custom_hooks_settings:
481 481 ui_key = request.POST.get('new_hook_ui_key')
482 482 ui_value = request.POST.get('new_hook_ui_value')
483 483
484 484 hook_id = request.POST.get('hook_id')
485 485 new_hook = False
486 486
487 487 model = SettingsModel()
488 488 try:
489 489 if ui_value and ui_key:
490 490 model.create_or_update_hook(ui_key, ui_value)
491 491 h.flash(_('Added new hook'), category='success')
492 492 new_hook = True
493 493 elif hook_id:
494 494 RhodeCodeUi.delete(hook_id)
495 495 Session().commit()
496 496
497 497 # check for edits
498 498 update = False
499 499 _d = request.POST.dict_of_lists()
500 500 for k, v in zip(_d.get('hook_ui_key', []),
501 501 _d.get('hook_ui_value_new', [])):
502 502 model.create_or_update_hook(k, v)
503 503 update = True
504 504
505 505 if update and not new_hook:
506 506 h.flash(_('Updated hooks'), category='success')
507 507 Session().commit()
508 508 except Exception:
509 509 log.exception("Exception during hook creation")
510 510 h.flash(_('Error occurred during hook creation'),
511 511 category='error')
512 512
513 513 return redirect(url('admin_settings_hooks'))
514 514
515 515 @HasPermissionAllDecorator('hg.admin')
516 516 def settings_hooks(self):
517 517 """GET /admin/settings/hooks: All items in the collection"""
518 518 # url('admin_settings_hooks')
519 519 c.active = 'hooks'
520 520
521 521 model = SettingsModel()
522 522 c.hooks = model.get_builtin_hooks()
523 523 c.custom_hooks = model.get_custom_hooks()
524 524
525 525 return htmlfill.render(
526 526 render('admin/settings/settings.mako'),
527 527 defaults=self._form_defaults(),
528 528 encoding="UTF-8",
529 529 force_defaults=False)
530 530
531 531 @HasPermissionAllDecorator('hg.admin')
532 532 def settings_search(self):
533 533 """GET /admin/settings/search: All items in the collection"""
534 534 # url('admin_settings_search')
535 535 c.active = 'search'
536 536
537 537 from rhodecode.lib.index import searcher_from_config
538 538 searcher = searcher_from_config(config)
539 539 c.statistics = searcher.statistics()
540 540
541 541 return render('admin/settings/settings.mako')
542 542
543 543 @HasPermissionAllDecorator('hg.admin')
544 544 def settings_supervisor(self):
545 545 c.rhodecode_ini = rhodecode.CONFIG
546 546 c.active = 'supervisor'
547 547
548 548 c.supervisor_procs = OrderedDict([
549 549 (SUPERVISOR_MASTER, {}),
550 550 ])
551 551
552 552 c.log_size = 10240
553 553 supervisor = SupervisorModel()
554 554
555 555 _connection = supervisor.get_connection(
556 556 c.rhodecode_ini.get('supervisor.uri'))
557 557 c.connection_error = None
558 558 try:
559 559 _connection.supervisor.getAllProcessInfo()
560 560 except Exception as e:
561 561 c.connection_error = str(e)
562 562 log.exception("Exception reading supervisor data")
563 563 return render('admin/settings/settings.mako')
564 564
565 565 groupid = c.rhodecode_ini.get('supervisor.group_id')
566 566
567 567 # feed our group processes to the main
568 568 for proc in supervisor.get_group_processes(_connection, groupid):
569 569 c.supervisor_procs[proc['name']] = {}
570 570
571 571 for k in c.supervisor_procs.keys():
572 572 try:
573 573 # master process info
574 574 if k == SUPERVISOR_MASTER:
575 575 _data = supervisor.get_master_state(_connection)
576 576 _data['name'] = 'supervisor master'
577 577 _data['description'] = 'pid %s, id: %s, ver: %s' % (
578 578 _data['pid'], _data['id'], _data['ver'])
579 579 c.supervisor_procs[k] = _data
580 580 else:
581 581 procid = groupid + ":" + k
582 582 c.supervisor_procs[k] = supervisor.get_process_info(_connection, procid)
583 583 except Exception as e:
584 584 log.exception("Exception reading supervisor data")
585 585 c.supervisor_procs[k] = {'_rhodecode_error': str(e)}
586 586
587 587 return render('admin/settings/settings.mako')
588 588
589 589 @HasPermissionAllDecorator('hg.admin')
590 590 def settings_supervisor_log(self, procid):
591 591 import rhodecode
592 592 c.rhodecode_ini = rhodecode.CONFIG
593 593 c.active = 'supervisor_tail'
594 594
595 595 supervisor = SupervisorModel()
596 596 _connection = supervisor.get_connection(c.rhodecode_ini.get('supervisor.uri'))
597 597 groupid = c.rhodecode_ini.get('supervisor.group_id')
598 598 procid = groupid + ":" + procid if procid != SUPERVISOR_MASTER else procid
599 599
600 600 c.log_size = 10240
601 601 offset = abs(safe_int(request.GET.get('offset', c.log_size))) * -1
602 602 c.log = supervisor.read_process_log(_connection, procid, offset, 0)
603 603
604 604 return render('admin/settings/settings.mako')
605 605
606 606 @HasPermissionAllDecorator('hg.admin')
607 607 @auth.CSRFRequired()
608 608 def settings_labs_update(self):
609 609 """POST /admin/settings/labs: All items in the collection"""
610 610 # url('admin_settings/labs', method={'POST'})
611 611 c.active = 'labs'
612 612
613 613 application_form = LabsSettingsForm()()
614 614 try:
615 615 form_result = application_form.to_python(dict(request.POST))
616 616 except formencode.Invalid as errors:
617 617 h.flash(
618 618 _('Some form inputs contain invalid data.'),
619 619 category='error')
620 620 return htmlfill.render(
621 621 render('admin/settings/settings.mako'),
622 622 defaults=errors.value,
623 623 errors=errors.error_dict or {},
624 624 prefix_error=False,
625 625 encoding='UTF-8',
626 626 force_defaults=False
627 627 )
628 628
629 629 try:
630 630 session = Session()
631 631 for setting in _LAB_SETTINGS:
632 632 setting_name = setting.key[len('rhodecode_'):]
633 633 sett = SettingsModel().create_or_update_setting(
634 634 setting_name, form_result[setting.key], setting.type)
635 635 session.add(sett)
636 636
637 637 except Exception:
638 638 log.exception('Exception while updating lab settings')
639 639 h.flash(_('Error occurred during updating labs settings'),
640 640 category='error')
641 641 else:
642 642 Session().commit()
643 643 SettingsModel().invalidate_settings_cache()
644 644 h.flash(_('Updated Labs settings'), category='success')
645 645 return redirect(url('admin_settings_labs'))
646 646
647 647 return htmlfill.render(
648 648 render('admin/settings/settings.mako'),
649 649 defaults=self._form_defaults(),
650 650 encoding='UTF-8',
651 651 force_defaults=False)
652 652
653 653 @HasPermissionAllDecorator('hg.admin')
654 654 def settings_labs(self):
655 655 """GET /admin/settings/labs: All items in the collection"""
656 656 # url('admin_settings_labs')
657 657 if not c.labs_active:
658 658 redirect(url('admin_settings'))
659 659
660 660 c.active = 'labs'
661 661 c.lab_settings = _LAB_SETTINGS
662 662
663 663 return htmlfill.render(
664 664 render('admin/settings/settings.mako'),
665 665 defaults=self._form_defaults(),
666 666 encoding='UTF-8',
667 667 force_defaults=False)
668 668
669 669 def _form_defaults(self):
670 670 defaults = SettingsModel().get_all_settings()
671 671 defaults.update(self._get_hg_ui_settings())
672 672 defaults.update({
673 673 'new_svn_branch': '',
674 674 'new_svn_tag': '',
675 675 })
676 676 return defaults
677 677
678 678
679 679 # :param key: name of the setting including the 'rhodecode_' prefix
680 680 # :param type: the RhodeCodeSetting type to use.
681 681 # :param group: the i18ned group in which we should dispaly this setting
682 682 # :param label: the i18ned label we should display for this setting
683 683 # :param help: the i18ned help we should dispaly for this setting
684 684 LabSetting = collections.namedtuple(
685 685 'LabSetting', ('key', 'type', 'group', 'label', 'help'))
686 686
687 687
688 688 # This list has to be kept in sync with the form
689 689 # rhodecode.model.forms.LabsSettingsForm.
690 690 _LAB_SETTINGS = [
691 691
692 692 ]
General Comments 0
You need to be logged in to leave comments. Login now