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