##// END OF EJS Templates
application: re-organize imports for pyramid to prepare code for speedup optimization.
marcink -
r3238:426d07af default
parent child Browse files
Show More
1 NO CONTENT: file renamed from rhodecode/apps/admin/interfaces.py to rhodecode/apps/_base/interfaces.py
NO CONTENT: file renamed from rhodecode/apps/admin/interfaces.py to rhodecode/apps/_base/interfaces.py
@@ -1,147 +1,147 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2018 RhodeCode GmbH
3 # Copyright (C) 2016-2018 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 logging
22 import logging
23 import collections
23 import collections
24
24
25 from zope.interface import implementer
25 from zope.interface import implementer
26
26
27 from rhodecode.apps.admin.interfaces import IAdminNavigationRegistry
27 from rhodecode.apps._base.interfaces import IAdminNavigationRegistry
28 from rhodecode.lib.utils2 import str2bool
28 from rhodecode.lib.utils2 import str2bool
29 from rhodecode.translation import _
29 from rhodecode.translation import _
30
30
31
31
32 log = logging.getLogger(__name__)
32 log = logging.getLogger(__name__)
33
33
34 NavListEntry = collections.namedtuple(
34 NavListEntry = collections.namedtuple(
35 'NavListEntry', ['key', 'name', 'url', 'active_list'])
35 'NavListEntry', ['key', 'name', 'url', 'active_list'])
36
36
37
37
38 class NavEntry(object):
38 class NavEntry(object):
39 """
39 """
40 Represents an entry in the admin navigation.
40 Represents an entry in the admin navigation.
41
41
42 :param key: Unique identifier used to store reference in an OrderedDict.
42 :param key: Unique identifier used to store reference in an OrderedDict.
43 :param name: Display name, usually a translation string.
43 :param name: Display name, usually a translation string.
44 :param view_name: Name of the view, used generate the URL.
44 :param view_name: Name of the view, used generate the URL.
45 :param active_list: list of urls that we select active for this element
45 :param active_list: list of urls that we select active for this element
46 """
46 """
47
47
48 def __init__(self, key, name, view_name, active_list=None):
48 def __init__(self, key, name, view_name, active_list=None):
49 self.key = key
49 self.key = key
50 self.name = name
50 self.name = name
51 self.view_name = view_name
51 self.view_name = view_name
52 self._active_list = active_list or []
52 self._active_list = active_list or []
53
53
54 def generate_url(self, request):
54 def generate_url(self, request):
55 return request.route_path(self.view_name)
55 return request.route_path(self.view_name)
56
56
57 def get_localized_name(self, request):
57 def get_localized_name(self, request):
58 return request.translate(self.name)
58 return request.translate(self.name)
59
59
60 @property
60 @property
61 def active_list(self):
61 def active_list(self):
62 active_list = [self.key]
62 active_list = [self.key]
63 if self._active_list:
63 if self._active_list:
64 active_list = self._active_list
64 active_list = self._active_list
65 return active_list
65 return active_list
66
66
67
67
68 @implementer(IAdminNavigationRegistry)
68 @implementer(IAdminNavigationRegistry)
69 class NavigationRegistry(object):
69 class NavigationRegistry(object):
70
70
71 _base_entries = [
71 _base_entries = [
72 NavEntry('global', _('Global'),
72 NavEntry('global', _('Global'),
73 'admin_settings_global'),
73 'admin_settings_global'),
74 NavEntry('vcs', _('VCS'),
74 NavEntry('vcs', _('VCS'),
75 'admin_settings_vcs'),
75 'admin_settings_vcs'),
76 NavEntry('visual', _('Visual'),
76 NavEntry('visual', _('Visual'),
77 'admin_settings_visual'),
77 'admin_settings_visual'),
78 NavEntry('mapping', _('Remap and Rescan'),
78 NavEntry('mapping', _('Remap and Rescan'),
79 'admin_settings_mapping'),
79 'admin_settings_mapping'),
80 NavEntry('issuetracker', _('Issue Tracker'),
80 NavEntry('issuetracker', _('Issue Tracker'),
81 'admin_settings_issuetracker'),
81 'admin_settings_issuetracker'),
82 NavEntry('email', _('Email'),
82 NavEntry('email', _('Email'),
83 'admin_settings_email'),
83 'admin_settings_email'),
84 NavEntry('hooks', _('Hooks'),
84 NavEntry('hooks', _('Hooks'),
85 'admin_settings_hooks'),
85 'admin_settings_hooks'),
86 NavEntry('search', _('Full Text Search'),
86 NavEntry('search', _('Full Text Search'),
87 'admin_settings_search'),
87 'admin_settings_search'),
88 NavEntry('integrations', _('Integrations'),
88 NavEntry('integrations', _('Integrations'),
89 'global_integrations_home'),
89 'global_integrations_home'),
90 NavEntry('system', _('System Info'),
90 NavEntry('system', _('System Info'),
91 'admin_settings_system'),
91 'admin_settings_system'),
92 NavEntry('exceptions', _('Exceptions Tracker'),
92 NavEntry('exceptions', _('Exceptions Tracker'),
93 'admin_settings_exception_tracker',
93 'admin_settings_exception_tracker',
94 active_list=['exceptions', 'exceptions_browse']),
94 active_list=['exceptions', 'exceptions_browse']),
95 NavEntry('process_management', _('Processes'),
95 NavEntry('process_management', _('Processes'),
96 'admin_settings_process_management'),
96 'admin_settings_process_management'),
97 NavEntry('sessions', _('User Sessions'),
97 NavEntry('sessions', _('User Sessions'),
98 'admin_settings_sessions'),
98 'admin_settings_sessions'),
99 NavEntry('open_source', _('Open Source Licenses'),
99 NavEntry('open_source', _('Open Source Licenses'),
100 'admin_settings_open_source'),
100 'admin_settings_open_source'),
101 NavEntry('automation', _('Automation'),
101 NavEntry('automation', _('Automation'),
102 'admin_settings_automation')
102 'admin_settings_automation')
103 ]
103 ]
104
104
105 _labs_entry = NavEntry('labs', _('Labs'),
105 _labs_entry = NavEntry('labs', _('Labs'),
106 'admin_settings_labs')
106 'admin_settings_labs')
107
107
108 def __init__(self, labs_active=False):
108 def __init__(self, labs_active=False):
109 self._registered_entries = collections.OrderedDict()
109 self._registered_entries = collections.OrderedDict()
110 for item in self.__class__._base_entries:
110 for item in self.__class__._base_entries:
111 self._registered_entries[item.key] = item
111 self._registered_entries[item.key] = item
112
112
113 if labs_active:
113 if labs_active:
114 self.add_entry(self._labs_entry)
114 self.add_entry(self._labs_entry)
115
115
116 def add_entry(self, entry):
116 def add_entry(self, entry):
117 self._registered_entries[entry.key] = entry
117 self._registered_entries[entry.key] = entry
118
118
119 def get_navlist(self, request):
119 def get_navlist(self, request):
120 navlist = [NavListEntry(i.key, i.get_localized_name(request),
120 navlist = [NavListEntry(i.key, i.get_localized_name(request),
121 i.generate_url(request), i.active_list)
121 i.generate_url(request), i.active_list)
122 for i in self._registered_entries.values()]
122 for i in self._registered_entries.values()]
123 return navlist
123 return navlist
124
124
125
125
126 def navigation_registry(request, registry=None):
126 def navigation_registry(request, registry=None):
127 """
127 """
128 Helper that returns the admin navigation registry.
128 Helper that returns the admin navigation registry.
129 """
129 """
130 pyramid_registry = registry or request.registry
130 pyramid_registry = registry or request.registry
131 nav_registry = pyramid_registry.queryUtility(IAdminNavigationRegistry)
131 nav_registry = pyramid_registry.queryUtility(IAdminNavigationRegistry)
132 return nav_registry
132 return nav_registry
133
133
134
134
135 def navigation_list(request):
135 def navigation_list(request):
136 """
136 """
137 Helper that returns the admin navigation as list of NavListEntry objects.
137 Helper that returns the admin navigation as list of NavListEntry objects.
138 """
138 """
139 return navigation_registry(request).get_navlist(request)
139 return navigation_registry(request).get_navlist(request)
140
140
141
141
142 def includeme(config):
142 def includeme(config):
143 # Create admin navigation registry and add it to the pyramid registry.
143 # Create admin navigation registry and add it to the pyramid registry.
144 settings = config.get_settings()
144 settings = config.get_settings()
145 labs_active = str2bool(settings.get('labs_settings_active', False))
145 labs_active = str2bool(settings.get('labs_settings_active', False))
146 navigation_registry = NavigationRegistry(labs_active=labs_active)
146 navigation_registry = NavigationRegistry(labs_active=labs_active)
147 config.registry.registerUtility(navigation_registry) No newline at end of file
147 config.registry.registerUtility(navigation_registry)
1 NO CONTENT: file renamed from rhodecode/apps/admin/subscribers.py to rhodecode/apps/_base/subscribers.py
NO CONTENT: file renamed from rhodecode/apps/admin/subscribers.py to rhodecode/apps/_base/subscribers.py
@@ -1,444 +1,442 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2018 RhodeCode GmbH
3 # Copyright (C) 2016-2018 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 from rhodecode.apps._base import ADMIN_PREFIX
22 from rhodecode.apps._base import ADMIN_PREFIX
23
23
24
24
25 def admin_routes(config):
25 def admin_routes(config):
26 """
26 """
27 Admin prefixed routes
27 Admin prefixed routes
28 """
28 """
29
29
30 config.add_route(
30 config.add_route(
31 name='admin_audit_logs',
31 name='admin_audit_logs',
32 pattern='/audit_logs')
32 pattern='/audit_logs')
33
33
34 config.add_route(
34 config.add_route(
35 name='admin_audit_log_entry',
35 name='admin_audit_log_entry',
36 pattern='/audit_logs/{audit_log_id}')
36 pattern='/audit_logs/{audit_log_id}')
37
37
38 config.add_route(
38 config.add_route(
39 name='pull_requests_global_0', # backward compat
39 name='pull_requests_global_0', # backward compat
40 pattern='/pull_requests/{pull_request_id:\d+}')
40 pattern='/pull_requests/{pull_request_id:\d+}')
41 config.add_route(
41 config.add_route(
42 name='pull_requests_global_1', # backward compat
42 name='pull_requests_global_1', # backward compat
43 pattern='/pull-requests/{pull_request_id:\d+}')
43 pattern='/pull-requests/{pull_request_id:\d+}')
44 config.add_route(
44 config.add_route(
45 name='pull_requests_global',
45 name='pull_requests_global',
46 pattern='/pull-request/{pull_request_id:\d+}')
46 pattern='/pull-request/{pull_request_id:\d+}')
47
47
48 config.add_route(
48 config.add_route(
49 name='admin_settings_open_source',
49 name='admin_settings_open_source',
50 pattern='/settings/open_source')
50 pattern='/settings/open_source')
51 config.add_route(
51 config.add_route(
52 name='admin_settings_vcs_svn_generate_cfg',
52 name='admin_settings_vcs_svn_generate_cfg',
53 pattern='/settings/vcs/svn_generate_cfg')
53 pattern='/settings/vcs/svn_generate_cfg')
54
54
55 config.add_route(
55 config.add_route(
56 name='admin_settings_system',
56 name='admin_settings_system',
57 pattern='/settings/system')
57 pattern='/settings/system')
58 config.add_route(
58 config.add_route(
59 name='admin_settings_system_update',
59 name='admin_settings_system_update',
60 pattern='/settings/system/updates')
60 pattern='/settings/system/updates')
61
61
62 config.add_route(
62 config.add_route(
63 name='admin_settings_exception_tracker',
63 name='admin_settings_exception_tracker',
64 pattern='/settings/exceptions')
64 pattern='/settings/exceptions')
65 config.add_route(
65 config.add_route(
66 name='admin_settings_exception_tracker_delete_all',
66 name='admin_settings_exception_tracker_delete_all',
67 pattern='/settings/exceptions/delete')
67 pattern='/settings/exceptions/delete')
68 config.add_route(
68 config.add_route(
69 name='admin_settings_exception_tracker_show',
69 name='admin_settings_exception_tracker_show',
70 pattern='/settings/exceptions/{exception_id}')
70 pattern='/settings/exceptions/{exception_id}')
71 config.add_route(
71 config.add_route(
72 name='admin_settings_exception_tracker_delete',
72 name='admin_settings_exception_tracker_delete',
73 pattern='/settings/exceptions/{exception_id}/delete')
73 pattern='/settings/exceptions/{exception_id}/delete')
74
74
75 config.add_route(
75 config.add_route(
76 name='admin_settings_sessions',
76 name='admin_settings_sessions',
77 pattern='/settings/sessions')
77 pattern='/settings/sessions')
78 config.add_route(
78 config.add_route(
79 name='admin_settings_sessions_cleanup',
79 name='admin_settings_sessions_cleanup',
80 pattern='/settings/sessions/cleanup')
80 pattern='/settings/sessions/cleanup')
81
81
82 config.add_route(
82 config.add_route(
83 name='admin_settings_process_management',
83 name='admin_settings_process_management',
84 pattern='/settings/process_management')
84 pattern='/settings/process_management')
85 config.add_route(
85 config.add_route(
86 name='admin_settings_process_management_data',
86 name='admin_settings_process_management_data',
87 pattern='/settings/process_management/data')
87 pattern='/settings/process_management/data')
88 config.add_route(
88 config.add_route(
89 name='admin_settings_process_management_signal',
89 name='admin_settings_process_management_signal',
90 pattern='/settings/process_management/signal')
90 pattern='/settings/process_management/signal')
91 config.add_route(
91 config.add_route(
92 name='admin_settings_process_management_master_signal',
92 name='admin_settings_process_management_master_signal',
93 pattern='/settings/process_management/master_signal')
93 pattern='/settings/process_management/master_signal')
94
94
95 # default settings
95 # default settings
96 config.add_route(
96 config.add_route(
97 name='admin_defaults_repositories',
97 name='admin_defaults_repositories',
98 pattern='/defaults/repositories')
98 pattern='/defaults/repositories')
99 config.add_route(
99 config.add_route(
100 name='admin_defaults_repositories_update',
100 name='admin_defaults_repositories_update',
101 pattern='/defaults/repositories/update')
101 pattern='/defaults/repositories/update')
102
102
103 # admin settings
103 # admin settings
104
104
105 config.add_route(
105 config.add_route(
106 name='admin_settings',
106 name='admin_settings',
107 pattern='/settings')
107 pattern='/settings')
108 config.add_route(
108 config.add_route(
109 name='admin_settings_update',
109 name='admin_settings_update',
110 pattern='/settings/update')
110 pattern='/settings/update')
111
111
112 config.add_route(
112 config.add_route(
113 name='admin_settings_global',
113 name='admin_settings_global',
114 pattern='/settings/global')
114 pattern='/settings/global')
115 config.add_route(
115 config.add_route(
116 name='admin_settings_global_update',
116 name='admin_settings_global_update',
117 pattern='/settings/global/update')
117 pattern='/settings/global/update')
118
118
119 config.add_route(
119 config.add_route(
120 name='admin_settings_vcs',
120 name='admin_settings_vcs',
121 pattern='/settings/vcs')
121 pattern='/settings/vcs')
122 config.add_route(
122 config.add_route(
123 name='admin_settings_vcs_update',
123 name='admin_settings_vcs_update',
124 pattern='/settings/vcs/update')
124 pattern='/settings/vcs/update')
125 config.add_route(
125 config.add_route(
126 name='admin_settings_vcs_svn_pattern_delete',
126 name='admin_settings_vcs_svn_pattern_delete',
127 pattern='/settings/vcs/svn_pattern_delete')
127 pattern='/settings/vcs/svn_pattern_delete')
128
128
129 config.add_route(
129 config.add_route(
130 name='admin_settings_mapping',
130 name='admin_settings_mapping',
131 pattern='/settings/mapping')
131 pattern='/settings/mapping')
132 config.add_route(
132 config.add_route(
133 name='admin_settings_mapping_update',
133 name='admin_settings_mapping_update',
134 pattern='/settings/mapping/update')
134 pattern='/settings/mapping/update')
135
135
136 config.add_route(
136 config.add_route(
137 name='admin_settings_visual',
137 name='admin_settings_visual',
138 pattern='/settings/visual')
138 pattern='/settings/visual')
139 config.add_route(
139 config.add_route(
140 name='admin_settings_visual_update',
140 name='admin_settings_visual_update',
141 pattern='/settings/visual/update')
141 pattern='/settings/visual/update')
142
142
143
143
144 config.add_route(
144 config.add_route(
145 name='admin_settings_issuetracker',
145 name='admin_settings_issuetracker',
146 pattern='/settings/issue-tracker')
146 pattern='/settings/issue-tracker')
147 config.add_route(
147 config.add_route(
148 name='admin_settings_issuetracker_update',
148 name='admin_settings_issuetracker_update',
149 pattern='/settings/issue-tracker/update')
149 pattern='/settings/issue-tracker/update')
150 config.add_route(
150 config.add_route(
151 name='admin_settings_issuetracker_test',
151 name='admin_settings_issuetracker_test',
152 pattern='/settings/issue-tracker/test')
152 pattern='/settings/issue-tracker/test')
153 config.add_route(
153 config.add_route(
154 name='admin_settings_issuetracker_delete',
154 name='admin_settings_issuetracker_delete',
155 pattern='/settings/issue-tracker/delete')
155 pattern='/settings/issue-tracker/delete')
156
156
157 config.add_route(
157 config.add_route(
158 name='admin_settings_email',
158 name='admin_settings_email',
159 pattern='/settings/email')
159 pattern='/settings/email')
160 config.add_route(
160 config.add_route(
161 name='admin_settings_email_update',
161 name='admin_settings_email_update',
162 pattern='/settings/email/update')
162 pattern='/settings/email/update')
163
163
164 config.add_route(
164 config.add_route(
165 name='admin_settings_hooks',
165 name='admin_settings_hooks',
166 pattern='/settings/hooks')
166 pattern='/settings/hooks')
167 config.add_route(
167 config.add_route(
168 name='admin_settings_hooks_update',
168 name='admin_settings_hooks_update',
169 pattern='/settings/hooks/update')
169 pattern='/settings/hooks/update')
170 config.add_route(
170 config.add_route(
171 name='admin_settings_hooks_delete',
171 name='admin_settings_hooks_delete',
172 pattern='/settings/hooks/delete')
172 pattern='/settings/hooks/delete')
173
173
174 config.add_route(
174 config.add_route(
175 name='admin_settings_search',
175 name='admin_settings_search',
176 pattern='/settings/search')
176 pattern='/settings/search')
177
177
178 config.add_route(
178 config.add_route(
179 name='admin_settings_labs',
179 name='admin_settings_labs',
180 pattern='/settings/labs')
180 pattern='/settings/labs')
181 config.add_route(
181 config.add_route(
182 name='admin_settings_labs_update',
182 name='admin_settings_labs_update',
183 pattern='/settings/labs/update')
183 pattern='/settings/labs/update')
184
184
185 # Automation EE feature
185 # Automation EE feature
186 config.add_route(
186 config.add_route(
187 'admin_settings_automation',
187 'admin_settings_automation',
188 pattern=ADMIN_PREFIX + '/settings/automation')
188 pattern=ADMIN_PREFIX + '/settings/automation')
189
189
190 # global permissions
190 # global permissions
191
191
192 config.add_route(
192 config.add_route(
193 name='admin_permissions_application',
193 name='admin_permissions_application',
194 pattern='/permissions/application')
194 pattern='/permissions/application')
195 config.add_route(
195 config.add_route(
196 name='admin_permissions_application_update',
196 name='admin_permissions_application_update',
197 pattern='/permissions/application/update')
197 pattern='/permissions/application/update')
198
198
199 config.add_route(
199 config.add_route(
200 name='admin_permissions_global',
200 name='admin_permissions_global',
201 pattern='/permissions/global')
201 pattern='/permissions/global')
202 config.add_route(
202 config.add_route(
203 name='admin_permissions_global_update',
203 name='admin_permissions_global_update',
204 pattern='/permissions/global/update')
204 pattern='/permissions/global/update')
205
205
206 config.add_route(
206 config.add_route(
207 name='admin_permissions_object',
207 name='admin_permissions_object',
208 pattern='/permissions/object')
208 pattern='/permissions/object')
209 config.add_route(
209 config.add_route(
210 name='admin_permissions_object_update',
210 name='admin_permissions_object_update',
211 pattern='/permissions/object/update')
211 pattern='/permissions/object/update')
212
212
213 # Branch perms EE feature
213 # Branch perms EE feature
214 config.add_route(
214 config.add_route(
215 name='admin_permissions_branch',
215 name='admin_permissions_branch',
216 pattern='/permissions/branch')
216 pattern='/permissions/branch')
217
217
218 config.add_route(
218 config.add_route(
219 name='admin_permissions_ips',
219 name='admin_permissions_ips',
220 pattern='/permissions/ips')
220 pattern='/permissions/ips')
221
221
222 config.add_route(
222 config.add_route(
223 name='admin_permissions_overview',
223 name='admin_permissions_overview',
224 pattern='/permissions/overview')
224 pattern='/permissions/overview')
225
225
226 config.add_route(
226 config.add_route(
227 name='admin_permissions_auth_token_access',
227 name='admin_permissions_auth_token_access',
228 pattern='/permissions/auth_token_access')
228 pattern='/permissions/auth_token_access')
229
229
230 config.add_route(
230 config.add_route(
231 name='admin_permissions_ssh_keys',
231 name='admin_permissions_ssh_keys',
232 pattern='/permissions/ssh_keys')
232 pattern='/permissions/ssh_keys')
233 config.add_route(
233 config.add_route(
234 name='admin_permissions_ssh_keys_data',
234 name='admin_permissions_ssh_keys_data',
235 pattern='/permissions/ssh_keys/data')
235 pattern='/permissions/ssh_keys/data')
236 config.add_route(
236 config.add_route(
237 name='admin_permissions_ssh_keys_update',
237 name='admin_permissions_ssh_keys_update',
238 pattern='/permissions/ssh_keys/update')
238 pattern='/permissions/ssh_keys/update')
239
239
240 # users admin
240 # users admin
241 config.add_route(
241 config.add_route(
242 name='users',
242 name='users',
243 pattern='/users')
243 pattern='/users')
244
244
245 config.add_route(
245 config.add_route(
246 name='users_data',
246 name='users_data',
247 pattern='/users_data')
247 pattern='/users_data')
248
248
249 config.add_route(
249 config.add_route(
250 name='users_create',
250 name='users_create',
251 pattern='/users/create')
251 pattern='/users/create')
252
252
253 config.add_route(
253 config.add_route(
254 name='users_new',
254 name='users_new',
255 pattern='/users/new')
255 pattern='/users/new')
256
256
257 # user management
257 # user management
258 config.add_route(
258 config.add_route(
259 name='user_edit',
259 name='user_edit',
260 pattern='/users/{user_id:\d+}/edit',
260 pattern='/users/{user_id:\d+}/edit',
261 user_route=True)
261 user_route=True)
262 config.add_route(
262 config.add_route(
263 name='user_edit_advanced',
263 name='user_edit_advanced',
264 pattern='/users/{user_id:\d+}/edit/advanced',
264 pattern='/users/{user_id:\d+}/edit/advanced',
265 user_route=True)
265 user_route=True)
266 config.add_route(
266 config.add_route(
267 name='user_edit_global_perms',
267 name='user_edit_global_perms',
268 pattern='/users/{user_id:\d+}/edit/global_permissions',
268 pattern='/users/{user_id:\d+}/edit/global_permissions',
269 user_route=True)
269 user_route=True)
270 config.add_route(
270 config.add_route(
271 name='user_edit_global_perms_update',
271 name='user_edit_global_perms_update',
272 pattern='/users/{user_id:\d+}/edit/global_permissions/update',
272 pattern='/users/{user_id:\d+}/edit/global_permissions/update',
273 user_route=True)
273 user_route=True)
274 config.add_route(
274 config.add_route(
275 name='user_update',
275 name='user_update',
276 pattern='/users/{user_id:\d+}/update',
276 pattern='/users/{user_id:\d+}/update',
277 user_route=True)
277 user_route=True)
278 config.add_route(
278 config.add_route(
279 name='user_delete',
279 name='user_delete',
280 pattern='/users/{user_id:\d+}/delete',
280 pattern='/users/{user_id:\d+}/delete',
281 user_route=True)
281 user_route=True)
282 config.add_route(
282 config.add_route(
283 name='user_force_password_reset',
283 name='user_force_password_reset',
284 pattern='/users/{user_id:\d+}/password_reset',
284 pattern='/users/{user_id:\d+}/password_reset',
285 user_route=True)
285 user_route=True)
286 config.add_route(
286 config.add_route(
287 name='user_create_personal_repo_group',
287 name='user_create_personal_repo_group',
288 pattern='/users/{user_id:\d+}/create_repo_group',
288 pattern='/users/{user_id:\d+}/create_repo_group',
289 user_route=True)
289 user_route=True)
290
290
291 # user auth tokens
291 # user auth tokens
292 config.add_route(
292 config.add_route(
293 name='edit_user_auth_tokens',
293 name='edit_user_auth_tokens',
294 pattern='/users/{user_id:\d+}/edit/auth_tokens',
294 pattern='/users/{user_id:\d+}/edit/auth_tokens',
295 user_route=True)
295 user_route=True)
296 config.add_route(
296 config.add_route(
297 name='edit_user_auth_tokens_add',
297 name='edit_user_auth_tokens_add',
298 pattern='/users/{user_id:\d+}/edit/auth_tokens/new',
298 pattern='/users/{user_id:\d+}/edit/auth_tokens/new',
299 user_route=True)
299 user_route=True)
300 config.add_route(
300 config.add_route(
301 name='edit_user_auth_tokens_delete',
301 name='edit_user_auth_tokens_delete',
302 pattern='/users/{user_id:\d+}/edit/auth_tokens/delete',
302 pattern='/users/{user_id:\d+}/edit/auth_tokens/delete',
303 user_route=True)
303 user_route=True)
304
304
305 # user ssh keys
305 # user ssh keys
306 config.add_route(
306 config.add_route(
307 name='edit_user_ssh_keys',
307 name='edit_user_ssh_keys',
308 pattern='/users/{user_id:\d+}/edit/ssh_keys',
308 pattern='/users/{user_id:\d+}/edit/ssh_keys',
309 user_route=True)
309 user_route=True)
310 config.add_route(
310 config.add_route(
311 name='edit_user_ssh_keys_generate_keypair',
311 name='edit_user_ssh_keys_generate_keypair',
312 pattern='/users/{user_id:\d+}/edit/ssh_keys/generate',
312 pattern='/users/{user_id:\d+}/edit/ssh_keys/generate',
313 user_route=True)
313 user_route=True)
314 config.add_route(
314 config.add_route(
315 name='edit_user_ssh_keys_add',
315 name='edit_user_ssh_keys_add',
316 pattern='/users/{user_id:\d+}/edit/ssh_keys/new',
316 pattern='/users/{user_id:\d+}/edit/ssh_keys/new',
317 user_route=True)
317 user_route=True)
318 config.add_route(
318 config.add_route(
319 name='edit_user_ssh_keys_delete',
319 name='edit_user_ssh_keys_delete',
320 pattern='/users/{user_id:\d+}/edit/ssh_keys/delete',
320 pattern='/users/{user_id:\d+}/edit/ssh_keys/delete',
321 user_route=True)
321 user_route=True)
322
322
323 # user emails
323 # user emails
324 config.add_route(
324 config.add_route(
325 name='edit_user_emails',
325 name='edit_user_emails',
326 pattern='/users/{user_id:\d+}/edit/emails',
326 pattern='/users/{user_id:\d+}/edit/emails',
327 user_route=True)
327 user_route=True)
328 config.add_route(
328 config.add_route(
329 name='edit_user_emails_add',
329 name='edit_user_emails_add',
330 pattern='/users/{user_id:\d+}/edit/emails/new',
330 pattern='/users/{user_id:\d+}/edit/emails/new',
331 user_route=True)
331 user_route=True)
332 config.add_route(
332 config.add_route(
333 name='edit_user_emails_delete',
333 name='edit_user_emails_delete',
334 pattern='/users/{user_id:\d+}/edit/emails/delete',
334 pattern='/users/{user_id:\d+}/edit/emails/delete',
335 user_route=True)
335 user_route=True)
336
336
337 # user IPs
337 # user IPs
338 config.add_route(
338 config.add_route(
339 name='edit_user_ips',
339 name='edit_user_ips',
340 pattern='/users/{user_id:\d+}/edit/ips',
340 pattern='/users/{user_id:\d+}/edit/ips',
341 user_route=True)
341 user_route=True)
342 config.add_route(
342 config.add_route(
343 name='edit_user_ips_add',
343 name='edit_user_ips_add',
344 pattern='/users/{user_id:\d+}/edit/ips/new',
344 pattern='/users/{user_id:\d+}/edit/ips/new',
345 user_route_with_default=True) # enabled for default user too
345 user_route_with_default=True) # enabled for default user too
346 config.add_route(
346 config.add_route(
347 name='edit_user_ips_delete',
347 name='edit_user_ips_delete',
348 pattern='/users/{user_id:\d+}/edit/ips/delete',
348 pattern='/users/{user_id:\d+}/edit/ips/delete',
349 user_route_with_default=True) # enabled for default user too
349 user_route_with_default=True) # enabled for default user too
350
350
351 # user perms
351 # user perms
352 config.add_route(
352 config.add_route(
353 name='edit_user_perms_summary',
353 name='edit_user_perms_summary',
354 pattern='/users/{user_id:\d+}/edit/permissions_summary',
354 pattern='/users/{user_id:\d+}/edit/permissions_summary',
355 user_route=True)
355 user_route=True)
356 config.add_route(
356 config.add_route(
357 name='edit_user_perms_summary_json',
357 name='edit_user_perms_summary_json',
358 pattern='/users/{user_id:\d+}/edit/permissions_summary/json',
358 pattern='/users/{user_id:\d+}/edit/permissions_summary/json',
359 user_route=True)
359 user_route=True)
360
360
361 # user user groups management
361 # user user groups management
362 config.add_route(
362 config.add_route(
363 name='edit_user_groups_management',
363 name='edit_user_groups_management',
364 pattern='/users/{user_id:\d+}/edit/groups_management',
364 pattern='/users/{user_id:\d+}/edit/groups_management',
365 user_route=True)
365 user_route=True)
366
366
367 config.add_route(
367 config.add_route(
368 name='edit_user_groups_management_updates',
368 name='edit_user_groups_management_updates',
369 pattern='/users/{user_id:\d+}/edit/edit_user_groups_management/updates',
369 pattern='/users/{user_id:\d+}/edit/edit_user_groups_management/updates',
370 user_route=True)
370 user_route=True)
371
371
372 # user audit logs
372 # user audit logs
373 config.add_route(
373 config.add_route(
374 name='edit_user_audit_logs',
374 name='edit_user_audit_logs',
375 pattern='/users/{user_id:\d+}/edit/audit', user_route=True)
375 pattern='/users/{user_id:\d+}/edit/audit', user_route=True)
376
376
377 # user caches
377 # user caches
378 config.add_route(
378 config.add_route(
379 name='edit_user_caches',
379 name='edit_user_caches',
380 pattern='/users/{user_id:\d+}/edit/caches',
380 pattern='/users/{user_id:\d+}/edit/caches',
381 user_route=True)
381 user_route=True)
382 config.add_route(
382 config.add_route(
383 name='edit_user_caches_update',
383 name='edit_user_caches_update',
384 pattern='/users/{user_id:\d+}/edit/caches/update',
384 pattern='/users/{user_id:\d+}/edit/caches/update',
385 user_route=True)
385 user_route=True)
386
386
387 # user-groups admin
387 # user-groups admin
388 config.add_route(
388 config.add_route(
389 name='user_groups',
389 name='user_groups',
390 pattern='/user_groups')
390 pattern='/user_groups')
391
391
392 config.add_route(
392 config.add_route(
393 name='user_groups_data',
393 name='user_groups_data',
394 pattern='/user_groups_data')
394 pattern='/user_groups_data')
395
395
396 config.add_route(
396 config.add_route(
397 name='user_groups_new',
397 name='user_groups_new',
398 pattern='/user_groups/new')
398 pattern='/user_groups/new')
399
399
400 config.add_route(
400 config.add_route(
401 name='user_groups_create',
401 name='user_groups_create',
402 pattern='/user_groups/create')
402 pattern='/user_groups/create')
403
403
404 # repos admin
404 # repos admin
405 config.add_route(
405 config.add_route(
406 name='repos',
406 name='repos',
407 pattern='/repos')
407 pattern='/repos')
408
408
409 config.add_route(
409 config.add_route(
410 name='repo_new',
410 name='repo_new',
411 pattern='/repos/new')
411 pattern='/repos/new')
412
412
413 config.add_route(
413 config.add_route(
414 name='repo_create',
414 name='repo_create',
415 pattern='/repos/create')
415 pattern='/repos/create')
416
416
417 # repo groups admin
417 # repo groups admin
418 config.add_route(
418 config.add_route(
419 name='repo_groups',
419 name='repo_groups',
420 pattern='/repo_groups')
420 pattern='/repo_groups')
421
421
422 config.add_route(
422 config.add_route(
423 name='repo_group_new',
423 name='repo_group_new',
424 pattern='/repo_group/new')
424 pattern='/repo_group/new')
425
425
426 config.add_route(
426 config.add_route(
427 name='repo_group_create',
427 name='repo_group_create',
428 pattern='/repo_group/create')
428 pattern='/repo_group/create')
429
429
430
430
431 def includeme(config):
431 def includeme(config):
432 from rhodecode.apps.admin.navigation import includeme as nav_includeme
432 from rhodecode.apps._base.navigation import includeme as nav_includeme
433
433
434 # Create admin navigation registry and add it to the pyramid registry.
434 # Create admin navigation registry and add it to the pyramid registry.
435 nav_includeme(config)
435 nav_includeme(config)
436
436
437 # main admin routes
437 # main admin routes
438 config.add_route(name='admin_home', pattern=ADMIN_PREFIX)
438 config.add_route(name='admin_home', pattern=ADMIN_PREFIX)
439 config.include(admin_routes, route_prefix=ADMIN_PREFIX)
439 config.include(admin_routes, route_prefix=ADMIN_PREFIX)
440
440
441 config.include('.subscribers')
442
443 # Scan module for configuration decorators.
441 # Scan module for configuration decorators.
444 config.scan('.views', ignore='.tests')
442 config.scan('.views', ignore='.tests')
@@ -1,160 +1,160 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2018-2018 RhodeCode GmbH
3 # Copyright (C) 2018-2018 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 import os
20 import os
21 import logging
21 import logging
22
22
23 from pyramid.httpexceptions import HTTPFound
23 from pyramid.httpexceptions import HTTPFound
24 from pyramid.view import view_config
24 from pyramid.view import view_config
25
25
26 from rhodecode.apps._base import BaseAppView
26 from rhodecode.apps._base import BaseAppView
27 from rhodecode.apps.admin.navigation import navigation_list
27 from rhodecode.apps._base.navigation import navigation_list
28 from rhodecode.lib import helpers as h
28 from rhodecode.lib import helpers as h
29 from rhodecode.lib.auth import (
29 from rhodecode.lib.auth import (
30 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
30 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
31 from rhodecode.lib.utils2 import time_to_utcdatetime, safe_int
31 from rhodecode.lib.utils2 import time_to_utcdatetime, safe_int
32 from rhodecode.lib import exc_tracking
32 from rhodecode.lib import exc_tracking
33
33
34 log = logging.getLogger(__name__)
34 log = logging.getLogger(__name__)
35
35
36
36
37 class ExceptionsTrackerView(BaseAppView):
37 class ExceptionsTrackerView(BaseAppView):
38 def load_default_context(self):
38 def load_default_context(self):
39 c = self._get_local_tmpl_context()
39 c = self._get_local_tmpl_context()
40 c.navlist = navigation_list(self.request)
40 c.navlist = navigation_list(self.request)
41 return c
41 return c
42
42
43 def count_all_exceptions(self):
43 def count_all_exceptions(self):
44 exc_store_path = exc_tracking.get_exc_store()
44 exc_store_path = exc_tracking.get_exc_store()
45 count = 0
45 count = 0
46 for fname in os.listdir(exc_store_path):
46 for fname in os.listdir(exc_store_path):
47 parts = fname.split('_', 2)
47 parts = fname.split('_', 2)
48 if not len(parts) == 3:
48 if not len(parts) == 3:
49 continue
49 continue
50 count +=1
50 count +=1
51 return count
51 return count
52
52
53 def get_all_exceptions(self, read_metadata=False, limit=None):
53 def get_all_exceptions(self, read_metadata=False, limit=None):
54 exc_store_path = exc_tracking.get_exc_store()
54 exc_store_path = exc_tracking.get_exc_store()
55 exception_list = []
55 exception_list = []
56
56
57 def key_sorter(val):
57 def key_sorter(val):
58 try:
58 try:
59 return val.split('_')[-1]
59 return val.split('_')[-1]
60 except Exception:
60 except Exception:
61 return 0
61 return 0
62 count = 0
62 count = 0
63 for fname in reversed(sorted(os.listdir(exc_store_path), key=key_sorter)):
63 for fname in reversed(sorted(os.listdir(exc_store_path), key=key_sorter)):
64
64
65 parts = fname.split('_', 2)
65 parts = fname.split('_', 2)
66 if not len(parts) == 3:
66 if not len(parts) == 3:
67 continue
67 continue
68
68
69 exc_id, app_type, exc_timestamp = parts
69 exc_id, app_type, exc_timestamp = parts
70
70
71 exc = {'exc_id': exc_id, 'app_type': app_type, 'exc_type': 'unknown',
71 exc = {'exc_id': exc_id, 'app_type': app_type, 'exc_type': 'unknown',
72 'exc_utc_date': '', 'exc_timestamp': exc_timestamp}
72 'exc_utc_date': '', 'exc_timestamp': exc_timestamp}
73
73
74 if read_metadata:
74 if read_metadata:
75 full_path = os.path.join(exc_store_path, fname)
75 full_path = os.path.join(exc_store_path, fname)
76 if not os.path.isfile(full_path):
76 if not os.path.isfile(full_path):
77 continue
77 continue
78 try:
78 try:
79 # we can read our metadata
79 # we can read our metadata
80 with open(full_path, 'rb') as f:
80 with open(full_path, 'rb') as f:
81 exc_metadata = exc_tracking.exc_unserialize(f.read())
81 exc_metadata = exc_tracking.exc_unserialize(f.read())
82 exc.update(exc_metadata)
82 exc.update(exc_metadata)
83 except Exception:
83 except Exception:
84 log.exception('Failed to read exc data from:{}'.format(full_path))
84 log.exception('Failed to read exc data from:{}'.format(full_path))
85 pass
85 pass
86
86
87 # convert our timestamp to a date obj, for nicer representation
87 # convert our timestamp to a date obj, for nicer representation
88 exc['exc_utc_date'] = time_to_utcdatetime(exc['exc_timestamp'])
88 exc['exc_utc_date'] = time_to_utcdatetime(exc['exc_timestamp'])
89 exception_list.append(exc)
89 exception_list.append(exc)
90
90
91 count += 1
91 count += 1
92 if limit and count >= limit:
92 if limit and count >= limit:
93 break
93 break
94 return exception_list
94 return exception_list
95
95
96 @LoginRequired()
96 @LoginRequired()
97 @HasPermissionAllDecorator('hg.admin')
97 @HasPermissionAllDecorator('hg.admin')
98 @view_config(
98 @view_config(
99 route_name='admin_settings_exception_tracker', request_method='GET',
99 route_name='admin_settings_exception_tracker', request_method='GET',
100 renderer='rhodecode:templates/admin/settings/settings.mako')
100 renderer='rhodecode:templates/admin/settings/settings.mako')
101 def browse_exceptions(self):
101 def browse_exceptions(self):
102 _ = self.request.translate
102 _ = self.request.translate
103 c = self.load_default_context()
103 c = self.load_default_context()
104 c.active = 'exceptions_browse'
104 c.active = 'exceptions_browse'
105 c.limit = safe_int(self.request.GET.get('limit')) or 50
105 c.limit = safe_int(self.request.GET.get('limit')) or 50
106 c.next_limit = c.limit + 50
106 c.next_limit = c.limit + 50
107 c.exception_list = self.get_all_exceptions(read_metadata=True, limit=c.limit)
107 c.exception_list = self.get_all_exceptions(read_metadata=True, limit=c.limit)
108 c.exception_list_count = self.count_all_exceptions()
108 c.exception_list_count = self.count_all_exceptions()
109 c.exception_store_dir = exc_tracking.get_exc_store()
109 c.exception_store_dir = exc_tracking.get_exc_store()
110 return self._get_template_context(c)
110 return self._get_template_context(c)
111
111
112 @LoginRequired()
112 @LoginRequired()
113 @HasPermissionAllDecorator('hg.admin')
113 @HasPermissionAllDecorator('hg.admin')
114 @view_config(
114 @view_config(
115 route_name='admin_settings_exception_tracker_show', request_method='GET',
115 route_name='admin_settings_exception_tracker_show', request_method='GET',
116 renderer='rhodecode:templates/admin/settings/settings.mako')
116 renderer='rhodecode:templates/admin/settings/settings.mako')
117 def exception_show(self):
117 def exception_show(self):
118 _ = self.request.translate
118 _ = self.request.translate
119 c = self.load_default_context()
119 c = self.load_default_context()
120
120
121 c.active = 'exceptions'
121 c.active = 'exceptions'
122 c.exception_id = self.request.matchdict['exception_id']
122 c.exception_id = self.request.matchdict['exception_id']
123 c.traceback = exc_tracking.read_exception(c.exception_id, prefix=None)
123 c.traceback = exc_tracking.read_exception(c.exception_id, prefix=None)
124 return self._get_template_context(c)
124 return self._get_template_context(c)
125
125
126 @LoginRequired()
126 @LoginRequired()
127 @HasPermissionAllDecorator('hg.admin')
127 @HasPermissionAllDecorator('hg.admin')
128 @CSRFRequired()
128 @CSRFRequired()
129 @view_config(
129 @view_config(
130 route_name='admin_settings_exception_tracker_delete_all', request_method='POST',
130 route_name='admin_settings_exception_tracker_delete_all', request_method='POST',
131 renderer='rhodecode:templates/admin/settings/settings.mako')
131 renderer='rhodecode:templates/admin/settings/settings.mako')
132 def exception_delete_all(self):
132 def exception_delete_all(self):
133 _ = self.request.translate
133 _ = self.request.translate
134 c = self.load_default_context()
134 c = self.load_default_context()
135
135
136 c.active = 'exceptions'
136 c.active = 'exceptions'
137 all_exc = self.get_all_exceptions()
137 all_exc = self.get_all_exceptions()
138 exc_count = len(all_exc)
138 exc_count = len(all_exc)
139 for exc in all_exc:
139 for exc in all_exc:
140 exc_tracking.delete_exception(exc['exc_id'], prefix=None)
140 exc_tracking.delete_exception(exc['exc_id'], prefix=None)
141
141
142 h.flash(_('Removed {} Exceptions').format(exc_count), category='success')
142 h.flash(_('Removed {} Exceptions').format(exc_count), category='success')
143 raise HTTPFound(h.route_path('admin_settings_exception_tracker'))
143 raise HTTPFound(h.route_path('admin_settings_exception_tracker'))
144
144
145 @LoginRequired()
145 @LoginRequired()
146 @HasPermissionAllDecorator('hg.admin')
146 @HasPermissionAllDecorator('hg.admin')
147 @CSRFRequired()
147 @CSRFRequired()
148 @view_config(
148 @view_config(
149 route_name='admin_settings_exception_tracker_delete', request_method='POST',
149 route_name='admin_settings_exception_tracker_delete', request_method='POST',
150 renderer='rhodecode:templates/admin/settings/settings.mako')
150 renderer='rhodecode:templates/admin/settings/settings.mako')
151 def exception_delete(self):
151 def exception_delete(self):
152 _ = self.request.translate
152 _ = self.request.translate
153 c = self.load_default_context()
153 c = self.load_default_context()
154
154
155 c.active = 'exceptions'
155 c.active = 'exceptions'
156 c.exception_id = self.request.matchdict['exception_id']
156 c.exception_id = self.request.matchdict['exception_id']
157 exc_tracking.delete_exception(c.exception_id, prefix=None)
157 exc_tracking.delete_exception(c.exception_id, prefix=None)
158
158
159 h.flash(_('Removed Exception {}').format(c.exception_id), category='success')
159 h.flash(_('Removed Exception {}').format(c.exception_id), category='success')
160 raise HTTPFound(h.route_path('admin_settings_exception_tracker'))
160 raise HTTPFound(h.route_path('admin_settings_exception_tracker'))
@@ -1,51 +1,51 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2018 RhodeCode GmbH
3 # Copyright (C) 2016-2018 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 collections
21 import collections
22 import logging
22 import logging
23
23
24 from pyramid.view import view_config
24 from pyramid.view import view_config
25
25
26 from rhodecode.apps._base import BaseAppView
26 from rhodecode.apps._base import BaseAppView
27 from rhodecode.apps.admin.navigation import navigation_list
27 from rhodecode.apps._base.navigation import navigation_list
28 from rhodecode.lib.auth import (LoginRequired, HasPermissionAllDecorator)
28 from rhodecode.lib.auth import (LoginRequired, HasPermissionAllDecorator)
29 from rhodecode.lib.utils import read_opensource_licenses
29 from rhodecode.lib.utils import read_opensource_licenses
30
30
31 log = logging.getLogger(__name__)
31 log = logging.getLogger(__name__)
32
32
33
33
34 class OpenSourceLicensesAdminSettingsView(BaseAppView):
34 class OpenSourceLicensesAdminSettingsView(BaseAppView):
35
35
36 def load_default_context(self):
36 def load_default_context(self):
37 c = self._get_local_tmpl_context()
37 c = self._get_local_tmpl_context()
38 return c
38 return c
39
39
40 @LoginRequired()
40 @LoginRequired()
41 @HasPermissionAllDecorator('hg.admin')
41 @HasPermissionAllDecorator('hg.admin')
42 @view_config(
42 @view_config(
43 route_name='admin_settings_open_source', request_method='GET',
43 route_name='admin_settings_open_source', request_method='GET',
44 renderer='rhodecode:templates/admin/settings/settings.mako')
44 renderer='rhodecode:templates/admin/settings/settings.mako')
45 def open_source_licenses(self):
45 def open_source_licenses(self):
46 c = self.load_default_context()
46 c = self.load_default_context()
47 c.active = 'open_source'
47 c.active = 'open_source'
48 c.navlist = navigation_list(self.request)
48 c.navlist = navigation_list(self.request)
49 c.opensource_licenses = sorted(
49 c.opensource_licenses = sorted(
50 read_opensource_licenses(), key=lambda d: d["name"])
50 read_opensource_licenses(), key=lambda d: d["name"])
51 return self._get_template_context(c)
51 return self._get_template_context(c)
@@ -1,183 +1,183 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2018 RhodeCode GmbH
3 # Copyright (C) 2016-2018 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 import psutil
23 import psutil
24 import signal
24 import signal
25 from pyramid.view import view_config
25 from pyramid.view import view_config
26
26
27 from rhodecode.apps._base import BaseAppView
27 from rhodecode.apps._base import BaseAppView
28 from rhodecode.apps.admin.navigation import navigation_list
28 from rhodecode.apps._base.navigation import navigation_list
29 from rhodecode.lib import system_info
29 from rhodecode.lib import system_info
30 from rhodecode.lib.auth import (
30 from rhodecode.lib.auth import (
31 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
31 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
32 from rhodecode.lib.utils2 import safe_int, StrictAttributeDict
32 from rhodecode.lib.utils2 import safe_int, StrictAttributeDict
33
33
34 log = logging.getLogger(__name__)
34 log = logging.getLogger(__name__)
35
35
36
36
37 class AdminProcessManagementView(BaseAppView):
37 class AdminProcessManagementView(BaseAppView):
38 def load_default_context(self):
38 def load_default_context(self):
39 c = self._get_local_tmpl_context()
39 c = self._get_local_tmpl_context()
40 return c
40 return c
41
41
42 def _format_proc(self, proc, with_children=False):
42 def _format_proc(self, proc, with_children=False):
43 try:
43 try:
44 mem = proc.memory_info()
44 mem = proc.memory_info()
45 proc_formatted = StrictAttributeDict({
45 proc_formatted = StrictAttributeDict({
46 'pid': proc.pid,
46 'pid': proc.pid,
47 'name': proc.name(),
47 'name': proc.name(),
48 'mem_rss': mem.rss,
48 'mem_rss': mem.rss,
49 'mem_vms': mem.vms,
49 'mem_vms': mem.vms,
50 'cpu_percent': proc.cpu_percent(),
50 'cpu_percent': proc.cpu_percent(),
51 'create_time': proc.create_time(),
51 'create_time': proc.create_time(),
52 'cmd': ' '.join(proc.cmdline()),
52 'cmd': ' '.join(proc.cmdline()),
53 })
53 })
54
54
55 if with_children:
55 if with_children:
56 proc_formatted.update({
56 proc_formatted.update({
57 'children': [self._format_proc(x)
57 'children': [self._format_proc(x)
58 for x in proc.children(recursive=True)]
58 for x in proc.children(recursive=True)]
59 })
59 })
60 except Exception:
60 except Exception:
61 log.exception('Failed to load proc')
61 log.exception('Failed to load proc')
62 proc_formatted = None
62 proc_formatted = None
63 return proc_formatted
63 return proc_formatted
64
64
65 def get_processes(self):
65 def get_processes(self):
66 proc_list = []
66 proc_list = []
67 for p in psutil.process_iter():
67 for p in psutil.process_iter():
68 if 'gunicorn' in p.name():
68 if 'gunicorn' in p.name():
69 proc = self._format_proc(p, with_children=True)
69 proc = self._format_proc(p, with_children=True)
70 if proc:
70 if proc:
71 proc_list.append(proc)
71 proc_list.append(proc)
72
72
73 return proc_list
73 return proc_list
74
74
75 def get_workers(self):
75 def get_workers(self):
76 workers = None
76 workers = None
77 try:
77 try:
78 rc_config = system_info.rhodecode_config().value['config']
78 rc_config = system_info.rhodecode_config().value['config']
79 workers = rc_config['server:main'].get('workers')
79 workers = rc_config['server:main'].get('workers')
80 except Exception:
80 except Exception:
81 pass
81 pass
82
82
83 return workers or '?'
83 return workers or '?'
84
84
85 @LoginRequired()
85 @LoginRequired()
86 @HasPermissionAllDecorator('hg.admin')
86 @HasPermissionAllDecorator('hg.admin')
87 @view_config(
87 @view_config(
88 route_name='admin_settings_process_management', request_method='GET',
88 route_name='admin_settings_process_management', request_method='GET',
89 renderer='rhodecode:templates/admin/settings/settings.mako')
89 renderer='rhodecode:templates/admin/settings/settings.mako')
90 def process_management(self):
90 def process_management(self):
91 _ = self.request.translate
91 _ = self.request.translate
92 c = self.load_default_context()
92 c = self.load_default_context()
93
93
94 c.active = 'process_management'
94 c.active = 'process_management'
95 c.navlist = navigation_list(self.request)
95 c.navlist = navigation_list(self.request)
96 c.gunicorn_processes = self.get_processes()
96 c.gunicorn_processes = self.get_processes()
97 c.gunicorn_workers = self.get_workers()
97 c.gunicorn_workers = self.get_workers()
98 return self._get_template_context(c)
98 return self._get_template_context(c)
99
99
100 @LoginRequired()
100 @LoginRequired()
101 @HasPermissionAllDecorator('hg.admin')
101 @HasPermissionAllDecorator('hg.admin')
102 @view_config(
102 @view_config(
103 route_name='admin_settings_process_management_data', request_method='GET',
103 route_name='admin_settings_process_management_data', request_method='GET',
104 renderer='rhodecode:templates/admin/settings/settings_process_management_data.mako')
104 renderer='rhodecode:templates/admin/settings/settings_process_management_data.mako')
105 def process_management_data(self):
105 def process_management_data(self):
106 _ = self.request.translate
106 _ = self.request.translate
107 c = self.load_default_context()
107 c = self.load_default_context()
108 c.gunicorn_processes = self.get_processes()
108 c.gunicorn_processes = self.get_processes()
109 return self._get_template_context(c)
109 return self._get_template_context(c)
110
110
111 @LoginRequired()
111 @LoginRequired()
112 @HasPermissionAllDecorator('hg.admin')
112 @HasPermissionAllDecorator('hg.admin')
113 @CSRFRequired()
113 @CSRFRequired()
114 @view_config(
114 @view_config(
115 route_name='admin_settings_process_management_signal',
115 route_name='admin_settings_process_management_signal',
116 request_method='POST', renderer='json_ext')
116 request_method='POST', renderer='json_ext')
117 def process_management_signal(self):
117 def process_management_signal(self):
118 pids = self.request.json.get('pids', [])
118 pids = self.request.json.get('pids', [])
119 result = []
119 result = []
120
120
121 def on_terminate(proc):
121 def on_terminate(proc):
122 msg = "process `PID:{}` terminated with exit code {}".format(
122 msg = "process `PID:{}` terminated with exit code {}".format(
123 proc.pid, proc.returncode or 0)
123 proc.pid, proc.returncode or 0)
124 result.append(msg)
124 result.append(msg)
125
125
126 procs = []
126 procs = []
127 for pid in pids:
127 for pid in pids:
128 pid = safe_int(pid)
128 pid = safe_int(pid)
129 if pid:
129 if pid:
130 try:
130 try:
131 proc = psutil.Process(pid)
131 proc = psutil.Process(pid)
132 except psutil.NoSuchProcess:
132 except psutil.NoSuchProcess:
133 continue
133 continue
134
134
135 children = proc.children(recursive=True)
135 children = proc.children(recursive=True)
136 if children:
136 if children:
137 log.warning('Wont kill Master Process')
137 log.warning('Wont kill Master Process')
138 else:
138 else:
139 procs.append(proc)
139 procs.append(proc)
140
140
141 for p in procs:
141 for p in procs:
142 try:
142 try:
143 p.terminate()
143 p.terminate()
144 except psutil.AccessDenied as e:
144 except psutil.AccessDenied as e:
145 log.warning('Access denied: {}'.format(e))
145 log.warning('Access denied: {}'.format(e))
146
146
147 gone, alive = psutil.wait_procs(procs, timeout=10, callback=on_terminate)
147 gone, alive = psutil.wait_procs(procs, timeout=10, callback=on_terminate)
148 for p in alive:
148 for p in alive:
149 try:
149 try:
150 p.kill()
150 p.kill()
151 except psutil.AccessDenied as e:
151 except psutil.AccessDenied as e:
152 log.warning('Access denied: {}'.format(e))
152 log.warning('Access denied: {}'.format(e))
153
153
154 return {'result': result}
154 return {'result': result}
155
155
156 @LoginRequired()
156 @LoginRequired()
157 @HasPermissionAllDecorator('hg.admin')
157 @HasPermissionAllDecorator('hg.admin')
158 @CSRFRequired()
158 @CSRFRequired()
159 @view_config(
159 @view_config(
160 route_name='admin_settings_process_management_master_signal',
160 route_name='admin_settings_process_management_master_signal',
161 request_method='POST', renderer='json_ext')
161 request_method='POST', renderer='json_ext')
162 def process_management_master_signal(self):
162 def process_management_master_signal(self):
163 pid_data = self.request.json.get('pid_data', {})
163 pid_data = self.request.json.get('pid_data', {})
164 pid = safe_int(pid_data['pid'])
164 pid = safe_int(pid_data['pid'])
165 action = pid_data['action']
165 action = pid_data['action']
166 if pid:
166 if pid:
167 try:
167 try:
168 proc = psutil.Process(pid)
168 proc = psutil.Process(pid)
169 except psutil.NoSuchProcess:
169 except psutil.NoSuchProcess:
170 return {'result': 'failure_no_such_process'}
170 return {'result': 'failure_no_such_process'}
171
171
172 children = proc.children(recursive=True)
172 children = proc.children(recursive=True)
173 if children:
173 if children:
174 # master process
174 # master process
175 if action == '+' and len(children) <= 20:
175 if action == '+' and len(children) <= 20:
176 proc.send_signal(signal.SIGTTIN)
176 proc.send_signal(signal.SIGTTIN)
177 elif action == '-' and len(children) >= 2:
177 elif action == '-' and len(children) >= 2:
178 proc.send_signal(signal.SIGTTOU)
178 proc.send_signal(signal.SIGTTOU)
179 else:
179 else:
180 return {'result': 'failure_wrong_action'}
180 return {'result': 'failure_wrong_action'}
181 return {'result': 'success'}
181 return {'result': 'success'}
182
182
183 return {'result': 'failure_not_master'}
183 return {'result': 'failure_not_master'}
@@ -1,101 +1,101 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2018 RhodeCode GmbH
3 # Copyright (C) 2016-2018 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 from pyramid.httpexceptions import HTTPFound
24 from pyramid.httpexceptions import HTTPFound
25
25
26 from rhodecode.apps._base import BaseAppView
26 from rhodecode.apps._base import BaseAppView
27 from rhodecode.apps.admin.navigation import navigation_list
27 from rhodecode.apps._base.navigation import navigation_list
28 from rhodecode.lib.auth import (
28 from rhodecode.lib.auth import (
29 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
29 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
30 from rhodecode.lib.utils2 import safe_int
30 from rhodecode.lib.utils2 import safe_int
31 from rhodecode.lib import system_info
31 from rhodecode.lib import system_info
32 from rhodecode.lib import user_sessions
32 from rhodecode.lib import user_sessions
33 from rhodecode.lib import helpers as h
33 from rhodecode.lib import helpers as h
34
34
35
35
36 log = logging.getLogger(__name__)
36 log = logging.getLogger(__name__)
37
37
38
38
39 class AdminSessionSettingsView(BaseAppView):
39 class AdminSessionSettingsView(BaseAppView):
40 def load_default_context(self):
40 def load_default_context(self):
41 c = self._get_local_tmpl_context()
41 c = self._get_local_tmpl_context()
42
42
43
43
44 return c
44 return c
45
45
46 @LoginRequired()
46 @LoginRequired()
47 @HasPermissionAllDecorator('hg.admin')
47 @HasPermissionAllDecorator('hg.admin')
48 @view_config(
48 @view_config(
49 route_name='admin_settings_sessions', request_method='GET',
49 route_name='admin_settings_sessions', request_method='GET',
50 renderer='rhodecode:templates/admin/settings/settings.mako')
50 renderer='rhodecode:templates/admin/settings/settings.mako')
51 def settings_sessions(self):
51 def settings_sessions(self):
52 c = self.load_default_context()
52 c = self.load_default_context()
53
53
54 c.active = 'sessions'
54 c.active = 'sessions'
55 c.navlist = navigation_list(self.request)
55 c.navlist = navigation_list(self.request)
56
56
57 c.cleanup_older_days = 60
57 c.cleanup_older_days = 60
58 older_than_seconds = 60 * 60 * 24 * c.cleanup_older_days
58 older_than_seconds = 60 * 60 * 24 * c.cleanup_older_days
59
59
60 config = system_info.rhodecode_config().get_value()['value']['config']
60 config = system_info.rhodecode_config().get_value()['value']['config']
61 c.session_model = user_sessions.get_session_handler(
61 c.session_model = user_sessions.get_session_handler(
62 config.get('beaker.session.type', 'memory'))(config)
62 config.get('beaker.session.type', 'memory'))(config)
63
63
64 c.session_conf = c.session_model.config
64 c.session_conf = c.session_model.config
65 c.session_count = c.session_model.get_count()
65 c.session_count = c.session_model.get_count()
66 c.session_expired_count = c.session_model.get_expired_count(
66 c.session_expired_count = c.session_model.get_expired_count(
67 older_than_seconds)
67 older_than_seconds)
68
68
69 return self._get_template_context(c)
69 return self._get_template_context(c)
70
70
71 @LoginRequired()
71 @LoginRequired()
72 @HasPermissionAllDecorator('hg.admin')
72 @HasPermissionAllDecorator('hg.admin')
73 @CSRFRequired()
73 @CSRFRequired()
74 @view_config(
74 @view_config(
75 route_name='admin_settings_sessions_cleanup', request_method='POST')
75 route_name='admin_settings_sessions_cleanup', request_method='POST')
76 def settings_sessions_cleanup(self):
76 def settings_sessions_cleanup(self):
77 _ = self.request.translate
77 _ = self.request.translate
78 expire_days = safe_int(self.request.params.get('expire_days'))
78 expire_days = safe_int(self.request.params.get('expire_days'))
79
79
80 if expire_days is None:
80 if expire_days is None:
81 expire_days = 60
81 expire_days = 60
82
82
83 older_than_seconds = 60 * 60 * 24 * expire_days
83 older_than_seconds = 60 * 60 * 24 * expire_days
84
84
85 config = system_info.rhodecode_config().get_value()['value']['config']
85 config = system_info.rhodecode_config().get_value()['value']['config']
86 session_model = user_sessions.get_session_handler(
86 session_model = user_sessions.get_session_handler(
87 config.get('beaker.session.type', 'memory'))(config)
87 config.get('beaker.session.type', 'memory'))(config)
88
88
89 try:
89 try:
90 session_model.clean_sessions(
90 session_model.clean_sessions(
91 older_than_seconds=older_than_seconds)
91 older_than_seconds=older_than_seconds)
92 h.flash(_('Cleaned up old sessions'), category='success')
92 h.flash(_('Cleaned up old sessions'), category='success')
93 except user_sessions.CleanupCommand as msg:
93 except user_sessions.CleanupCommand as msg:
94 h.flash(msg.message, category='warning')
94 h.flash(msg.message, category='warning')
95 except Exception as e:
95 except Exception as e:
96 log.exception('Failed session cleanup')
96 log.exception('Failed session cleanup')
97 h.flash(_('Failed to cleanup up old sessions'), category='error')
97 h.flash(_('Failed to cleanup up old sessions'), category='error')
98
98
99 redirect_to = self.request.resource_path(
99 redirect_to = self.request.resource_path(
100 self.context, route_name='admin_settings_sessions')
100 self.context, route_name='admin_settings_sessions')
101 return HTTPFound(redirect_to)
101 return HTTPFound(redirect_to)
@@ -1,780 +1,780 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2018 RhodeCode GmbH
3 # Copyright (C) 2010-2018 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 logging
22 import logging
23 import collections
23 import collections
24
24
25 import datetime
25 import datetime
26 import formencode
26 import formencode
27 import formencode.htmlfill
27 import formencode.htmlfill
28
28
29 import rhodecode
29 import rhodecode
30 from pyramid.view import view_config
30 from pyramid.view import view_config
31 from pyramid.httpexceptions import HTTPFound, HTTPNotFound
31 from pyramid.httpexceptions import HTTPFound, HTTPNotFound
32 from pyramid.renderers import render
32 from pyramid.renderers import render
33 from pyramid.response import Response
33 from pyramid.response import Response
34
34
35 from rhodecode.apps._base import BaseAppView
35 from rhodecode.apps._base import BaseAppView
36 from rhodecode.apps.admin.navigation import navigation_list
36 from rhodecode.apps._base.navigation import navigation_list
37 from rhodecode.apps.svn_support.config_keys import generate_config
37 from rhodecode.apps.svn_support.config_keys import generate_config
38 from rhodecode.lib import helpers as h
38 from rhodecode.lib import helpers as h
39 from rhodecode.lib.auth import (
39 from rhodecode.lib.auth import (
40 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
40 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
41 from rhodecode.lib.celerylib import tasks, run_task
41 from rhodecode.lib.celerylib import tasks, run_task
42 from rhodecode.lib.utils import repo2db_mapper
42 from rhodecode.lib.utils import repo2db_mapper
43 from rhodecode.lib.utils2 import str2bool, safe_unicode, AttributeDict
43 from rhodecode.lib.utils2 import str2bool, safe_unicode, AttributeDict
44 from rhodecode.lib.index import searcher_from_config
44 from rhodecode.lib.index import searcher_from_config
45
45
46 from rhodecode.model.db import RhodeCodeUi, Repository
46 from rhodecode.model.db import RhodeCodeUi, Repository
47 from rhodecode.model.forms import (ApplicationSettingsForm,
47 from rhodecode.model.forms import (ApplicationSettingsForm,
48 ApplicationUiSettingsForm, ApplicationVisualisationForm,
48 ApplicationUiSettingsForm, ApplicationVisualisationForm,
49 LabsSettingsForm, IssueTrackerPatternsForm)
49 LabsSettingsForm, IssueTrackerPatternsForm)
50 from rhodecode.model.repo_group import RepoGroupModel
50 from rhodecode.model.repo_group import RepoGroupModel
51
51
52 from rhodecode.model.scm import ScmModel
52 from rhodecode.model.scm import ScmModel
53 from rhodecode.model.notification import EmailNotificationModel
53 from rhodecode.model.notification import EmailNotificationModel
54 from rhodecode.model.meta import Session
54 from rhodecode.model.meta import Session
55 from rhodecode.model.settings import (
55 from rhodecode.model.settings import (
56 IssueTrackerSettingsModel, VcsSettingsModel, SettingNotFound,
56 IssueTrackerSettingsModel, VcsSettingsModel, SettingNotFound,
57 SettingsModel)
57 SettingsModel)
58
58
59
59
60 log = logging.getLogger(__name__)
60 log = logging.getLogger(__name__)
61
61
62
62
63 class AdminSettingsView(BaseAppView):
63 class AdminSettingsView(BaseAppView):
64
64
65 def load_default_context(self):
65 def load_default_context(self):
66 c = self._get_local_tmpl_context()
66 c = self._get_local_tmpl_context()
67 c.labs_active = str2bool(
67 c.labs_active = str2bool(
68 rhodecode.CONFIG.get('labs_settings_active', 'true'))
68 rhodecode.CONFIG.get('labs_settings_active', 'true'))
69 c.navlist = navigation_list(self.request)
69 c.navlist = navigation_list(self.request)
70
70
71 return c
71 return c
72
72
73 @classmethod
73 @classmethod
74 def _get_ui_settings(cls):
74 def _get_ui_settings(cls):
75 ret = RhodeCodeUi.query().all()
75 ret = RhodeCodeUi.query().all()
76
76
77 if not ret:
77 if not ret:
78 raise Exception('Could not get application ui settings !')
78 raise Exception('Could not get application ui settings !')
79 settings = {}
79 settings = {}
80 for each in ret:
80 for each in ret:
81 k = each.ui_key
81 k = each.ui_key
82 v = each.ui_value
82 v = each.ui_value
83 if k == '/':
83 if k == '/':
84 k = 'root_path'
84 k = 'root_path'
85
85
86 if k in ['push_ssl', 'publish', 'enabled']:
86 if k in ['push_ssl', 'publish', 'enabled']:
87 v = str2bool(v)
87 v = str2bool(v)
88
88
89 if k.find('.') != -1:
89 if k.find('.') != -1:
90 k = k.replace('.', '_')
90 k = k.replace('.', '_')
91
91
92 if each.ui_section in ['hooks', 'extensions']:
92 if each.ui_section in ['hooks', 'extensions']:
93 v = each.ui_active
93 v = each.ui_active
94
94
95 settings[each.ui_section + '_' + k] = v
95 settings[each.ui_section + '_' + k] = v
96 return settings
96 return settings
97
97
98 @classmethod
98 @classmethod
99 def _form_defaults(cls):
99 def _form_defaults(cls):
100 defaults = SettingsModel().get_all_settings()
100 defaults = SettingsModel().get_all_settings()
101 defaults.update(cls._get_ui_settings())
101 defaults.update(cls._get_ui_settings())
102
102
103 defaults.update({
103 defaults.update({
104 'new_svn_branch': '',
104 'new_svn_branch': '',
105 'new_svn_tag': '',
105 'new_svn_tag': '',
106 })
106 })
107 return defaults
107 return defaults
108
108
109 @LoginRequired()
109 @LoginRequired()
110 @HasPermissionAllDecorator('hg.admin')
110 @HasPermissionAllDecorator('hg.admin')
111 @view_config(
111 @view_config(
112 route_name='admin_settings_vcs', request_method='GET',
112 route_name='admin_settings_vcs', request_method='GET',
113 renderer='rhodecode:templates/admin/settings/settings.mako')
113 renderer='rhodecode:templates/admin/settings/settings.mako')
114 def settings_vcs(self):
114 def settings_vcs(self):
115 c = self.load_default_context()
115 c = self.load_default_context()
116 c.active = 'vcs'
116 c.active = 'vcs'
117 model = VcsSettingsModel()
117 model = VcsSettingsModel()
118 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
118 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
119 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
119 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
120
120
121 settings = self.request.registry.settings
121 settings = self.request.registry.settings
122 c.svn_proxy_generate_config = settings[generate_config]
122 c.svn_proxy_generate_config = settings[generate_config]
123
123
124 defaults = self._form_defaults()
124 defaults = self._form_defaults()
125
125
126 model.create_largeobjects_dirs_if_needed(defaults['paths_root_path'])
126 model.create_largeobjects_dirs_if_needed(defaults['paths_root_path'])
127
127
128 data = render('rhodecode:templates/admin/settings/settings.mako',
128 data = render('rhodecode:templates/admin/settings/settings.mako',
129 self._get_template_context(c), self.request)
129 self._get_template_context(c), self.request)
130 html = formencode.htmlfill.render(
130 html = formencode.htmlfill.render(
131 data,
131 data,
132 defaults=defaults,
132 defaults=defaults,
133 encoding="UTF-8",
133 encoding="UTF-8",
134 force_defaults=False
134 force_defaults=False
135 )
135 )
136 return Response(html)
136 return Response(html)
137
137
138 @LoginRequired()
138 @LoginRequired()
139 @HasPermissionAllDecorator('hg.admin')
139 @HasPermissionAllDecorator('hg.admin')
140 @CSRFRequired()
140 @CSRFRequired()
141 @view_config(
141 @view_config(
142 route_name='admin_settings_vcs_update', request_method='POST',
142 route_name='admin_settings_vcs_update', request_method='POST',
143 renderer='rhodecode:templates/admin/settings/settings.mako')
143 renderer='rhodecode:templates/admin/settings/settings.mako')
144 def settings_vcs_update(self):
144 def settings_vcs_update(self):
145 _ = self.request.translate
145 _ = self.request.translate
146 c = self.load_default_context()
146 c = self.load_default_context()
147 c.active = 'vcs'
147 c.active = 'vcs'
148
148
149 model = VcsSettingsModel()
149 model = VcsSettingsModel()
150 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
150 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
151 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
151 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
152
152
153 settings = self.request.registry.settings
153 settings = self.request.registry.settings
154 c.svn_proxy_generate_config = settings[generate_config]
154 c.svn_proxy_generate_config = settings[generate_config]
155
155
156 application_form = ApplicationUiSettingsForm(self.request.translate)()
156 application_form = ApplicationUiSettingsForm(self.request.translate)()
157
157
158 try:
158 try:
159 form_result = application_form.to_python(dict(self.request.POST))
159 form_result = application_form.to_python(dict(self.request.POST))
160 except formencode.Invalid as errors:
160 except formencode.Invalid as errors:
161 h.flash(
161 h.flash(
162 _("Some form inputs contain invalid data."),
162 _("Some form inputs contain invalid data."),
163 category='error')
163 category='error')
164 data = render('rhodecode:templates/admin/settings/settings.mako',
164 data = render('rhodecode:templates/admin/settings/settings.mako',
165 self._get_template_context(c), self.request)
165 self._get_template_context(c), self.request)
166 html = formencode.htmlfill.render(
166 html = formencode.htmlfill.render(
167 data,
167 data,
168 defaults=errors.value,
168 defaults=errors.value,
169 errors=errors.error_dict or {},
169 errors=errors.error_dict or {},
170 prefix_error=False,
170 prefix_error=False,
171 encoding="UTF-8",
171 encoding="UTF-8",
172 force_defaults=False
172 force_defaults=False
173 )
173 )
174 return Response(html)
174 return Response(html)
175
175
176 try:
176 try:
177 if c.visual.allow_repo_location_change:
177 if c.visual.allow_repo_location_change:
178 model.update_global_path_setting(form_result['paths_root_path'])
178 model.update_global_path_setting(form_result['paths_root_path'])
179
179
180 model.update_global_ssl_setting(form_result['web_push_ssl'])
180 model.update_global_ssl_setting(form_result['web_push_ssl'])
181 model.update_global_hook_settings(form_result)
181 model.update_global_hook_settings(form_result)
182
182
183 model.create_or_update_global_svn_settings(form_result)
183 model.create_or_update_global_svn_settings(form_result)
184 model.create_or_update_global_hg_settings(form_result)
184 model.create_or_update_global_hg_settings(form_result)
185 model.create_or_update_global_git_settings(form_result)
185 model.create_or_update_global_git_settings(form_result)
186 model.create_or_update_global_pr_settings(form_result)
186 model.create_or_update_global_pr_settings(form_result)
187 except Exception:
187 except Exception:
188 log.exception("Exception while updating settings")
188 log.exception("Exception while updating settings")
189 h.flash(_('Error occurred during updating '
189 h.flash(_('Error occurred during updating '
190 'application settings'), category='error')
190 'application settings'), category='error')
191 else:
191 else:
192 Session().commit()
192 Session().commit()
193 h.flash(_('Updated VCS settings'), category='success')
193 h.flash(_('Updated VCS settings'), category='success')
194 raise HTTPFound(h.route_path('admin_settings_vcs'))
194 raise HTTPFound(h.route_path('admin_settings_vcs'))
195
195
196 data = render('rhodecode:templates/admin/settings/settings.mako',
196 data = render('rhodecode:templates/admin/settings/settings.mako',
197 self._get_template_context(c), self.request)
197 self._get_template_context(c), self.request)
198 html = formencode.htmlfill.render(
198 html = formencode.htmlfill.render(
199 data,
199 data,
200 defaults=self._form_defaults(),
200 defaults=self._form_defaults(),
201 encoding="UTF-8",
201 encoding="UTF-8",
202 force_defaults=False
202 force_defaults=False
203 )
203 )
204 return Response(html)
204 return Response(html)
205
205
206 @LoginRequired()
206 @LoginRequired()
207 @HasPermissionAllDecorator('hg.admin')
207 @HasPermissionAllDecorator('hg.admin')
208 @CSRFRequired()
208 @CSRFRequired()
209 @view_config(
209 @view_config(
210 route_name='admin_settings_vcs_svn_pattern_delete', request_method='POST',
210 route_name='admin_settings_vcs_svn_pattern_delete', request_method='POST',
211 renderer='json_ext', xhr=True)
211 renderer='json_ext', xhr=True)
212 def settings_vcs_delete_svn_pattern(self):
212 def settings_vcs_delete_svn_pattern(self):
213 delete_pattern_id = self.request.POST.get('delete_svn_pattern')
213 delete_pattern_id = self.request.POST.get('delete_svn_pattern')
214 model = VcsSettingsModel()
214 model = VcsSettingsModel()
215 try:
215 try:
216 model.delete_global_svn_pattern(delete_pattern_id)
216 model.delete_global_svn_pattern(delete_pattern_id)
217 except SettingNotFound:
217 except SettingNotFound:
218 log.exception(
218 log.exception(
219 'Failed to delete svn_pattern with id %s', delete_pattern_id)
219 'Failed to delete svn_pattern with id %s', delete_pattern_id)
220 raise HTTPNotFound()
220 raise HTTPNotFound()
221
221
222 Session().commit()
222 Session().commit()
223 return True
223 return True
224
224
225 @LoginRequired()
225 @LoginRequired()
226 @HasPermissionAllDecorator('hg.admin')
226 @HasPermissionAllDecorator('hg.admin')
227 @view_config(
227 @view_config(
228 route_name='admin_settings_mapping', request_method='GET',
228 route_name='admin_settings_mapping', request_method='GET',
229 renderer='rhodecode:templates/admin/settings/settings.mako')
229 renderer='rhodecode:templates/admin/settings/settings.mako')
230 def settings_mapping(self):
230 def settings_mapping(self):
231 c = self.load_default_context()
231 c = self.load_default_context()
232 c.active = 'mapping'
232 c.active = 'mapping'
233
233
234 data = render('rhodecode:templates/admin/settings/settings.mako',
234 data = render('rhodecode:templates/admin/settings/settings.mako',
235 self._get_template_context(c), self.request)
235 self._get_template_context(c), self.request)
236 html = formencode.htmlfill.render(
236 html = formencode.htmlfill.render(
237 data,
237 data,
238 defaults=self._form_defaults(),
238 defaults=self._form_defaults(),
239 encoding="UTF-8",
239 encoding="UTF-8",
240 force_defaults=False
240 force_defaults=False
241 )
241 )
242 return Response(html)
242 return Response(html)
243
243
244 @LoginRequired()
244 @LoginRequired()
245 @HasPermissionAllDecorator('hg.admin')
245 @HasPermissionAllDecorator('hg.admin')
246 @CSRFRequired()
246 @CSRFRequired()
247 @view_config(
247 @view_config(
248 route_name='admin_settings_mapping_update', request_method='POST',
248 route_name='admin_settings_mapping_update', request_method='POST',
249 renderer='rhodecode:templates/admin/settings/settings.mako')
249 renderer='rhodecode:templates/admin/settings/settings.mako')
250 def settings_mapping_update(self):
250 def settings_mapping_update(self):
251 _ = self.request.translate
251 _ = self.request.translate
252 c = self.load_default_context()
252 c = self.load_default_context()
253 c.active = 'mapping'
253 c.active = 'mapping'
254 rm_obsolete = self.request.POST.get('destroy', False)
254 rm_obsolete = self.request.POST.get('destroy', False)
255 invalidate_cache = self.request.POST.get('invalidate', False)
255 invalidate_cache = self.request.POST.get('invalidate', False)
256 log.debug(
256 log.debug(
257 'rescanning repo location with destroy obsolete=%s', rm_obsolete)
257 'rescanning repo location with destroy obsolete=%s', rm_obsolete)
258
258
259 if invalidate_cache:
259 if invalidate_cache:
260 log.debug('invalidating all repositories cache')
260 log.debug('invalidating all repositories cache')
261 for repo in Repository.get_all():
261 for repo in Repository.get_all():
262 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
262 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
263
263
264 filesystem_repos = ScmModel().repo_scan()
264 filesystem_repos = ScmModel().repo_scan()
265 added, removed = repo2db_mapper(filesystem_repos, rm_obsolete)
265 added, removed = repo2db_mapper(filesystem_repos, rm_obsolete)
266 _repr = lambda l: ', '.join(map(safe_unicode, l)) or '-'
266 _repr = lambda l: ', '.join(map(safe_unicode, l)) or '-'
267 h.flash(_('Repositories successfully '
267 h.flash(_('Repositories successfully '
268 'rescanned added: %s ; removed: %s') %
268 'rescanned added: %s ; removed: %s') %
269 (_repr(added), _repr(removed)),
269 (_repr(added), _repr(removed)),
270 category='success')
270 category='success')
271 raise HTTPFound(h.route_path('admin_settings_mapping'))
271 raise HTTPFound(h.route_path('admin_settings_mapping'))
272
272
273 @LoginRequired()
273 @LoginRequired()
274 @HasPermissionAllDecorator('hg.admin')
274 @HasPermissionAllDecorator('hg.admin')
275 @view_config(
275 @view_config(
276 route_name='admin_settings', request_method='GET',
276 route_name='admin_settings', request_method='GET',
277 renderer='rhodecode:templates/admin/settings/settings.mako')
277 renderer='rhodecode:templates/admin/settings/settings.mako')
278 @view_config(
278 @view_config(
279 route_name='admin_settings_global', request_method='GET',
279 route_name='admin_settings_global', request_method='GET',
280 renderer='rhodecode:templates/admin/settings/settings.mako')
280 renderer='rhodecode:templates/admin/settings/settings.mako')
281 def settings_global(self):
281 def settings_global(self):
282 c = self.load_default_context()
282 c = self.load_default_context()
283 c.active = 'global'
283 c.active = 'global'
284 c.personal_repo_group_default_pattern = RepoGroupModel()\
284 c.personal_repo_group_default_pattern = RepoGroupModel()\
285 .get_personal_group_name_pattern()
285 .get_personal_group_name_pattern()
286
286
287 data = render('rhodecode:templates/admin/settings/settings.mako',
287 data = render('rhodecode:templates/admin/settings/settings.mako',
288 self._get_template_context(c), self.request)
288 self._get_template_context(c), self.request)
289 html = formencode.htmlfill.render(
289 html = formencode.htmlfill.render(
290 data,
290 data,
291 defaults=self._form_defaults(),
291 defaults=self._form_defaults(),
292 encoding="UTF-8",
292 encoding="UTF-8",
293 force_defaults=False
293 force_defaults=False
294 )
294 )
295 return Response(html)
295 return Response(html)
296
296
297 @LoginRequired()
297 @LoginRequired()
298 @HasPermissionAllDecorator('hg.admin')
298 @HasPermissionAllDecorator('hg.admin')
299 @CSRFRequired()
299 @CSRFRequired()
300 @view_config(
300 @view_config(
301 route_name='admin_settings_update', request_method='POST',
301 route_name='admin_settings_update', request_method='POST',
302 renderer='rhodecode:templates/admin/settings/settings.mako')
302 renderer='rhodecode:templates/admin/settings/settings.mako')
303 @view_config(
303 @view_config(
304 route_name='admin_settings_global_update', request_method='POST',
304 route_name='admin_settings_global_update', request_method='POST',
305 renderer='rhodecode:templates/admin/settings/settings.mako')
305 renderer='rhodecode:templates/admin/settings/settings.mako')
306 def settings_global_update(self):
306 def settings_global_update(self):
307 _ = self.request.translate
307 _ = self.request.translate
308 c = self.load_default_context()
308 c = self.load_default_context()
309 c.active = 'global'
309 c.active = 'global'
310 c.personal_repo_group_default_pattern = RepoGroupModel()\
310 c.personal_repo_group_default_pattern = RepoGroupModel()\
311 .get_personal_group_name_pattern()
311 .get_personal_group_name_pattern()
312 application_form = ApplicationSettingsForm(self.request.translate)()
312 application_form = ApplicationSettingsForm(self.request.translate)()
313 try:
313 try:
314 form_result = application_form.to_python(dict(self.request.POST))
314 form_result = application_form.to_python(dict(self.request.POST))
315 except formencode.Invalid as errors:
315 except formencode.Invalid as errors:
316 h.flash(
316 h.flash(
317 _("Some form inputs contain invalid data."),
317 _("Some form inputs contain invalid data."),
318 category='error')
318 category='error')
319 data = render('rhodecode:templates/admin/settings/settings.mako',
319 data = render('rhodecode:templates/admin/settings/settings.mako',
320 self._get_template_context(c), self.request)
320 self._get_template_context(c), self.request)
321 html = formencode.htmlfill.render(
321 html = formencode.htmlfill.render(
322 data,
322 data,
323 defaults=errors.value,
323 defaults=errors.value,
324 errors=errors.error_dict or {},
324 errors=errors.error_dict or {},
325 prefix_error=False,
325 prefix_error=False,
326 encoding="UTF-8",
326 encoding="UTF-8",
327 force_defaults=False
327 force_defaults=False
328 )
328 )
329 return Response(html)
329 return Response(html)
330
330
331 settings = [
331 settings = [
332 ('title', 'rhodecode_title', 'unicode'),
332 ('title', 'rhodecode_title', 'unicode'),
333 ('realm', 'rhodecode_realm', 'unicode'),
333 ('realm', 'rhodecode_realm', 'unicode'),
334 ('pre_code', 'rhodecode_pre_code', 'unicode'),
334 ('pre_code', 'rhodecode_pre_code', 'unicode'),
335 ('post_code', 'rhodecode_post_code', 'unicode'),
335 ('post_code', 'rhodecode_post_code', 'unicode'),
336 ('captcha_public_key', 'rhodecode_captcha_public_key', 'unicode'),
336 ('captcha_public_key', 'rhodecode_captcha_public_key', 'unicode'),
337 ('captcha_private_key', 'rhodecode_captcha_private_key', 'unicode'),
337 ('captcha_private_key', 'rhodecode_captcha_private_key', 'unicode'),
338 ('create_personal_repo_group', 'rhodecode_create_personal_repo_group', 'bool'),
338 ('create_personal_repo_group', 'rhodecode_create_personal_repo_group', 'bool'),
339 ('personal_repo_group_pattern', 'rhodecode_personal_repo_group_pattern', 'unicode'),
339 ('personal_repo_group_pattern', 'rhodecode_personal_repo_group_pattern', 'unicode'),
340 ]
340 ]
341 try:
341 try:
342 for setting, form_key, type_ in settings:
342 for setting, form_key, type_ in settings:
343 sett = SettingsModel().create_or_update_setting(
343 sett = SettingsModel().create_or_update_setting(
344 setting, form_result[form_key], type_)
344 setting, form_result[form_key], type_)
345 Session().add(sett)
345 Session().add(sett)
346
346
347 Session().commit()
347 Session().commit()
348 SettingsModel().invalidate_settings_cache()
348 SettingsModel().invalidate_settings_cache()
349 h.flash(_('Updated application settings'), category='success')
349 h.flash(_('Updated application settings'), category='success')
350 except Exception:
350 except Exception:
351 log.exception("Exception while updating application settings")
351 log.exception("Exception while updating application settings")
352 h.flash(
352 h.flash(
353 _('Error occurred during updating application settings'),
353 _('Error occurred during updating application settings'),
354 category='error')
354 category='error')
355
355
356 raise HTTPFound(h.route_path('admin_settings_global'))
356 raise HTTPFound(h.route_path('admin_settings_global'))
357
357
358 @LoginRequired()
358 @LoginRequired()
359 @HasPermissionAllDecorator('hg.admin')
359 @HasPermissionAllDecorator('hg.admin')
360 @view_config(
360 @view_config(
361 route_name='admin_settings_visual', request_method='GET',
361 route_name='admin_settings_visual', request_method='GET',
362 renderer='rhodecode:templates/admin/settings/settings.mako')
362 renderer='rhodecode:templates/admin/settings/settings.mako')
363 def settings_visual(self):
363 def settings_visual(self):
364 c = self.load_default_context()
364 c = self.load_default_context()
365 c.active = 'visual'
365 c.active = 'visual'
366
366
367 data = render('rhodecode:templates/admin/settings/settings.mako',
367 data = render('rhodecode:templates/admin/settings/settings.mako',
368 self._get_template_context(c), self.request)
368 self._get_template_context(c), self.request)
369 html = formencode.htmlfill.render(
369 html = formencode.htmlfill.render(
370 data,
370 data,
371 defaults=self._form_defaults(),
371 defaults=self._form_defaults(),
372 encoding="UTF-8",
372 encoding="UTF-8",
373 force_defaults=False
373 force_defaults=False
374 )
374 )
375 return Response(html)
375 return Response(html)
376
376
377 @LoginRequired()
377 @LoginRequired()
378 @HasPermissionAllDecorator('hg.admin')
378 @HasPermissionAllDecorator('hg.admin')
379 @CSRFRequired()
379 @CSRFRequired()
380 @view_config(
380 @view_config(
381 route_name='admin_settings_visual_update', request_method='POST',
381 route_name='admin_settings_visual_update', request_method='POST',
382 renderer='rhodecode:templates/admin/settings/settings.mako')
382 renderer='rhodecode:templates/admin/settings/settings.mako')
383 def settings_visual_update(self):
383 def settings_visual_update(self):
384 _ = self.request.translate
384 _ = self.request.translate
385 c = self.load_default_context()
385 c = self.load_default_context()
386 c.active = 'visual'
386 c.active = 'visual'
387 application_form = ApplicationVisualisationForm(self.request.translate)()
387 application_form = ApplicationVisualisationForm(self.request.translate)()
388 try:
388 try:
389 form_result = application_form.to_python(dict(self.request.POST))
389 form_result = application_form.to_python(dict(self.request.POST))
390 except formencode.Invalid as errors:
390 except formencode.Invalid as errors:
391 h.flash(
391 h.flash(
392 _("Some form inputs contain invalid data."),
392 _("Some form inputs contain invalid data."),
393 category='error')
393 category='error')
394 data = render('rhodecode:templates/admin/settings/settings.mako',
394 data = render('rhodecode:templates/admin/settings/settings.mako',
395 self._get_template_context(c), self.request)
395 self._get_template_context(c), self.request)
396 html = formencode.htmlfill.render(
396 html = formencode.htmlfill.render(
397 data,
397 data,
398 defaults=errors.value,
398 defaults=errors.value,
399 errors=errors.error_dict or {},
399 errors=errors.error_dict or {},
400 prefix_error=False,
400 prefix_error=False,
401 encoding="UTF-8",
401 encoding="UTF-8",
402 force_defaults=False
402 force_defaults=False
403 )
403 )
404 return Response(html)
404 return Response(html)
405
405
406 try:
406 try:
407 settings = [
407 settings = [
408 ('show_public_icon', 'rhodecode_show_public_icon', 'bool'),
408 ('show_public_icon', 'rhodecode_show_public_icon', 'bool'),
409 ('show_private_icon', 'rhodecode_show_private_icon', 'bool'),
409 ('show_private_icon', 'rhodecode_show_private_icon', 'bool'),
410 ('stylify_metatags', 'rhodecode_stylify_metatags', 'bool'),
410 ('stylify_metatags', 'rhodecode_stylify_metatags', 'bool'),
411 ('repository_fields', 'rhodecode_repository_fields', 'bool'),
411 ('repository_fields', 'rhodecode_repository_fields', 'bool'),
412 ('dashboard_items', 'rhodecode_dashboard_items', 'int'),
412 ('dashboard_items', 'rhodecode_dashboard_items', 'int'),
413 ('admin_grid_items', 'rhodecode_admin_grid_items', 'int'),
413 ('admin_grid_items', 'rhodecode_admin_grid_items', 'int'),
414 ('show_version', 'rhodecode_show_version', 'bool'),
414 ('show_version', 'rhodecode_show_version', 'bool'),
415 ('use_gravatar', 'rhodecode_use_gravatar', 'bool'),
415 ('use_gravatar', 'rhodecode_use_gravatar', 'bool'),
416 ('markup_renderer', 'rhodecode_markup_renderer', 'unicode'),
416 ('markup_renderer', 'rhodecode_markup_renderer', 'unicode'),
417 ('gravatar_url', 'rhodecode_gravatar_url', 'unicode'),
417 ('gravatar_url', 'rhodecode_gravatar_url', 'unicode'),
418 ('clone_uri_tmpl', 'rhodecode_clone_uri_tmpl', 'unicode'),
418 ('clone_uri_tmpl', 'rhodecode_clone_uri_tmpl', 'unicode'),
419 ('clone_uri_ssh_tmpl', 'rhodecode_clone_uri_ssh_tmpl', 'unicode'),
419 ('clone_uri_ssh_tmpl', 'rhodecode_clone_uri_ssh_tmpl', 'unicode'),
420 ('support_url', 'rhodecode_support_url', 'unicode'),
420 ('support_url', 'rhodecode_support_url', 'unicode'),
421 ('show_revision_number', 'rhodecode_show_revision_number', 'bool'),
421 ('show_revision_number', 'rhodecode_show_revision_number', 'bool'),
422 ('show_sha_length', 'rhodecode_show_sha_length', 'int'),
422 ('show_sha_length', 'rhodecode_show_sha_length', 'int'),
423 ]
423 ]
424 for setting, form_key, type_ in settings:
424 for setting, form_key, type_ in settings:
425 sett = SettingsModel().create_or_update_setting(
425 sett = SettingsModel().create_or_update_setting(
426 setting, form_result[form_key], type_)
426 setting, form_result[form_key], type_)
427 Session().add(sett)
427 Session().add(sett)
428
428
429 Session().commit()
429 Session().commit()
430 SettingsModel().invalidate_settings_cache()
430 SettingsModel().invalidate_settings_cache()
431 h.flash(_('Updated visualisation settings'), category='success')
431 h.flash(_('Updated visualisation settings'), category='success')
432 except Exception:
432 except Exception:
433 log.exception("Exception updating visualization settings")
433 log.exception("Exception updating visualization settings")
434 h.flash(_('Error occurred during updating '
434 h.flash(_('Error occurred during updating '
435 'visualisation settings'),
435 'visualisation settings'),
436 category='error')
436 category='error')
437
437
438 raise HTTPFound(h.route_path('admin_settings_visual'))
438 raise HTTPFound(h.route_path('admin_settings_visual'))
439
439
440 @LoginRequired()
440 @LoginRequired()
441 @HasPermissionAllDecorator('hg.admin')
441 @HasPermissionAllDecorator('hg.admin')
442 @view_config(
442 @view_config(
443 route_name='admin_settings_issuetracker', request_method='GET',
443 route_name='admin_settings_issuetracker', request_method='GET',
444 renderer='rhodecode:templates/admin/settings/settings.mako')
444 renderer='rhodecode:templates/admin/settings/settings.mako')
445 def settings_issuetracker(self):
445 def settings_issuetracker(self):
446 c = self.load_default_context()
446 c = self.load_default_context()
447 c.active = 'issuetracker'
447 c.active = 'issuetracker'
448 defaults = SettingsModel().get_all_settings()
448 defaults = SettingsModel().get_all_settings()
449
449
450 entry_key = 'rhodecode_issuetracker_pat_'
450 entry_key = 'rhodecode_issuetracker_pat_'
451
451
452 c.issuetracker_entries = {}
452 c.issuetracker_entries = {}
453 for k, v in defaults.items():
453 for k, v in defaults.items():
454 if k.startswith(entry_key):
454 if k.startswith(entry_key):
455 uid = k[len(entry_key):]
455 uid = k[len(entry_key):]
456 c.issuetracker_entries[uid] = None
456 c.issuetracker_entries[uid] = None
457
457
458 for uid in c.issuetracker_entries:
458 for uid in c.issuetracker_entries:
459 c.issuetracker_entries[uid] = AttributeDict({
459 c.issuetracker_entries[uid] = AttributeDict({
460 'pat': defaults.get('rhodecode_issuetracker_pat_' + uid),
460 'pat': defaults.get('rhodecode_issuetracker_pat_' + uid),
461 'url': defaults.get('rhodecode_issuetracker_url_' + uid),
461 'url': defaults.get('rhodecode_issuetracker_url_' + uid),
462 'pref': defaults.get('rhodecode_issuetracker_pref_' + uid),
462 'pref': defaults.get('rhodecode_issuetracker_pref_' + uid),
463 'desc': defaults.get('rhodecode_issuetracker_desc_' + uid),
463 'desc': defaults.get('rhodecode_issuetracker_desc_' + uid),
464 })
464 })
465
465
466 return self._get_template_context(c)
466 return self._get_template_context(c)
467
467
468 @LoginRequired()
468 @LoginRequired()
469 @HasPermissionAllDecorator('hg.admin')
469 @HasPermissionAllDecorator('hg.admin')
470 @CSRFRequired()
470 @CSRFRequired()
471 @view_config(
471 @view_config(
472 route_name='admin_settings_issuetracker_test', request_method='POST',
472 route_name='admin_settings_issuetracker_test', request_method='POST',
473 renderer='string', xhr=True)
473 renderer='string', xhr=True)
474 def settings_issuetracker_test(self):
474 def settings_issuetracker_test(self):
475 return h.urlify_commit_message(
475 return h.urlify_commit_message(
476 self.request.POST.get('test_text', ''),
476 self.request.POST.get('test_text', ''),
477 'repo_group/test_repo1')
477 'repo_group/test_repo1')
478
478
479 @LoginRequired()
479 @LoginRequired()
480 @HasPermissionAllDecorator('hg.admin')
480 @HasPermissionAllDecorator('hg.admin')
481 @CSRFRequired()
481 @CSRFRequired()
482 @view_config(
482 @view_config(
483 route_name='admin_settings_issuetracker_update', request_method='POST',
483 route_name='admin_settings_issuetracker_update', request_method='POST',
484 renderer='rhodecode:templates/admin/settings/settings.mako')
484 renderer='rhodecode:templates/admin/settings/settings.mako')
485 def settings_issuetracker_update(self):
485 def settings_issuetracker_update(self):
486 _ = self.request.translate
486 _ = self.request.translate
487 self.load_default_context()
487 self.load_default_context()
488 settings_model = IssueTrackerSettingsModel()
488 settings_model = IssueTrackerSettingsModel()
489
489
490 try:
490 try:
491 form = IssueTrackerPatternsForm(self.request.translate)()
491 form = IssueTrackerPatternsForm(self.request.translate)()
492 data = form.to_python(self.request.POST)
492 data = form.to_python(self.request.POST)
493 except formencode.Invalid as errors:
493 except formencode.Invalid as errors:
494 log.exception('Failed to add new pattern')
494 log.exception('Failed to add new pattern')
495 error = errors
495 error = errors
496 h.flash(_('Invalid issue tracker pattern: {}'.format(error)),
496 h.flash(_('Invalid issue tracker pattern: {}'.format(error)),
497 category='error')
497 category='error')
498 raise HTTPFound(h.route_path('admin_settings_issuetracker'))
498 raise HTTPFound(h.route_path('admin_settings_issuetracker'))
499
499
500 if data:
500 if data:
501 for uid in data.get('delete_patterns', []):
501 for uid in data.get('delete_patterns', []):
502 settings_model.delete_entries(uid)
502 settings_model.delete_entries(uid)
503
503
504 for pattern in data.get('patterns', []):
504 for pattern in data.get('patterns', []):
505 for setting, value, type_ in pattern:
505 for setting, value, type_ in pattern:
506 sett = settings_model.create_or_update_setting(
506 sett = settings_model.create_or_update_setting(
507 setting, value, type_)
507 setting, value, type_)
508 Session().add(sett)
508 Session().add(sett)
509
509
510 Session().commit()
510 Session().commit()
511
511
512 SettingsModel().invalidate_settings_cache()
512 SettingsModel().invalidate_settings_cache()
513 h.flash(_('Updated issue tracker entries'), category='success')
513 h.flash(_('Updated issue tracker entries'), category='success')
514 raise HTTPFound(h.route_path('admin_settings_issuetracker'))
514 raise HTTPFound(h.route_path('admin_settings_issuetracker'))
515
515
516 @LoginRequired()
516 @LoginRequired()
517 @HasPermissionAllDecorator('hg.admin')
517 @HasPermissionAllDecorator('hg.admin')
518 @CSRFRequired()
518 @CSRFRequired()
519 @view_config(
519 @view_config(
520 route_name='admin_settings_issuetracker_delete', request_method='POST',
520 route_name='admin_settings_issuetracker_delete', request_method='POST',
521 renderer='rhodecode:templates/admin/settings/settings.mako')
521 renderer='rhodecode:templates/admin/settings/settings.mako')
522 def settings_issuetracker_delete(self):
522 def settings_issuetracker_delete(self):
523 _ = self.request.translate
523 _ = self.request.translate
524 self.load_default_context()
524 self.load_default_context()
525 uid = self.request.POST.get('uid')
525 uid = self.request.POST.get('uid')
526 try:
526 try:
527 IssueTrackerSettingsModel().delete_entries(uid)
527 IssueTrackerSettingsModel().delete_entries(uid)
528 except Exception:
528 except Exception:
529 log.exception('Failed to delete issue tracker setting %s', uid)
529 log.exception('Failed to delete issue tracker setting %s', uid)
530 raise HTTPNotFound()
530 raise HTTPNotFound()
531 h.flash(_('Removed issue tracker entry'), category='success')
531 h.flash(_('Removed issue tracker entry'), category='success')
532 raise HTTPFound(h.route_path('admin_settings_issuetracker'))
532 raise HTTPFound(h.route_path('admin_settings_issuetracker'))
533
533
534 @LoginRequired()
534 @LoginRequired()
535 @HasPermissionAllDecorator('hg.admin')
535 @HasPermissionAllDecorator('hg.admin')
536 @view_config(
536 @view_config(
537 route_name='admin_settings_email', request_method='GET',
537 route_name='admin_settings_email', request_method='GET',
538 renderer='rhodecode:templates/admin/settings/settings.mako')
538 renderer='rhodecode:templates/admin/settings/settings.mako')
539 def settings_email(self):
539 def settings_email(self):
540 c = self.load_default_context()
540 c = self.load_default_context()
541 c.active = 'email'
541 c.active = 'email'
542 c.rhodecode_ini = rhodecode.CONFIG
542 c.rhodecode_ini = rhodecode.CONFIG
543
543
544 data = render('rhodecode:templates/admin/settings/settings.mako',
544 data = render('rhodecode:templates/admin/settings/settings.mako',
545 self._get_template_context(c), self.request)
545 self._get_template_context(c), self.request)
546 html = formencode.htmlfill.render(
546 html = formencode.htmlfill.render(
547 data,
547 data,
548 defaults=self._form_defaults(),
548 defaults=self._form_defaults(),
549 encoding="UTF-8",
549 encoding="UTF-8",
550 force_defaults=False
550 force_defaults=False
551 )
551 )
552 return Response(html)
552 return Response(html)
553
553
554 @LoginRequired()
554 @LoginRequired()
555 @HasPermissionAllDecorator('hg.admin')
555 @HasPermissionAllDecorator('hg.admin')
556 @CSRFRequired()
556 @CSRFRequired()
557 @view_config(
557 @view_config(
558 route_name='admin_settings_email_update', request_method='POST',
558 route_name='admin_settings_email_update', request_method='POST',
559 renderer='rhodecode:templates/admin/settings/settings.mako')
559 renderer='rhodecode:templates/admin/settings/settings.mako')
560 def settings_email_update(self):
560 def settings_email_update(self):
561 _ = self.request.translate
561 _ = self.request.translate
562 c = self.load_default_context()
562 c = self.load_default_context()
563 c.active = 'email'
563 c.active = 'email'
564
564
565 test_email = self.request.POST.get('test_email')
565 test_email = self.request.POST.get('test_email')
566
566
567 if not test_email:
567 if not test_email:
568 h.flash(_('Please enter email address'), category='error')
568 h.flash(_('Please enter email address'), category='error')
569 raise HTTPFound(h.route_path('admin_settings_email'))
569 raise HTTPFound(h.route_path('admin_settings_email'))
570
570
571 email_kwargs = {
571 email_kwargs = {
572 'date': datetime.datetime.now(),
572 'date': datetime.datetime.now(),
573 'user': c.rhodecode_user,
573 'user': c.rhodecode_user,
574 'rhodecode_version': c.rhodecode_version
574 'rhodecode_version': c.rhodecode_version
575 }
575 }
576
576
577 (subject, headers, email_body,
577 (subject, headers, email_body,
578 email_body_plaintext) = EmailNotificationModel().render_email(
578 email_body_plaintext) = EmailNotificationModel().render_email(
579 EmailNotificationModel.TYPE_EMAIL_TEST, **email_kwargs)
579 EmailNotificationModel.TYPE_EMAIL_TEST, **email_kwargs)
580
580
581 recipients = [test_email] if test_email else None
581 recipients = [test_email] if test_email else None
582
582
583 run_task(tasks.send_email, recipients, subject,
583 run_task(tasks.send_email, recipients, subject,
584 email_body_plaintext, email_body)
584 email_body_plaintext, email_body)
585
585
586 h.flash(_('Send email task created'), category='success')
586 h.flash(_('Send email task created'), category='success')
587 raise HTTPFound(h.route_path('admin_settings_email'))
587 raise HTTPFound(h.route_path('admin_settings_email'))
588
588
589 @LoginRequired()
589 @LoginRequired()
590 @HasPermissionAllDecorator('hg.admin')
590 @HasPermissionAllDecorator('hg.admin')
591 @view_config(
591 @view_config(
592 route_name='admin_settings_hooks', request_method='GET',
592 route_name='admin_settings_hooks', request_method='GET',
593 renderer='rhodecode:templates/admin/settings/settings.mako')
593 renderer='rhodecode:templates/admin/settings/settings.mako')
594 def settings_hooks(self):
594 def settings_hooks(self):
595 c = self.load_default_context()
595 c = self.load_default_context()
596 c.active = 'hooks'
596 c.active = 'hooks'
597
597
598 model = SettingsModel()
598 model = SettingsModel()
599 c.hooks = model.get_builtin_hooks()
599 c.hooks = model.get_builtin_hooks()
600 c.custom_hooks = model.get_custom_hooks()
600 c.custom_hooks = model.get_custom_hooks()
601
601
602 data = render('rhodecode:templates/admin/settings/settings.mako',
602 data = render('rhodecode:templates/admin/settings/settings.mako',
603 self._get_template_context(c), self.request)
603 self._get_template_context(c), self.request)
604 html = formencode.htmlfill.render(
604 html = formencode.htmlfill.render(
605 data,
605 data,
606 defaults=self._form_defaults(),
606 defaults=self._form_defaults(),
607 encoding="UTF-8",
607 encoding="UTF-8",
608 force_defaults=False
608 force_defaults=False
609 )
609 )
610 return Response(html)
610 return Response(html)
611
611
612 @LoginRequired()
612 @LoginRequired()
613 @HasPermissionAllDecorator('hg.admin')
613 @HasPermissionAllDecorator('hg.admin')
614 @CSRFRequired()
614 @CSRFRequired()
615 @view_config(
615 @view_config(
616 route_name='admin_settings_hooks_update', request_method='POST',
616 route_name='admin_settings_hooks_update', request_method='POST',
617 renderer='rhodecode:templates/admin/settings/settings.mako')
617 renderer='rhodecode:templates/admin/settings/settings.mako')
618 @view_config(
618 @view_config(
619 route_name='admin_settings_hooks_delete', request_method='POST',
619 route_name='admin_settings_hooks_delete', request_method='POST',
620 renderer='rhodecode:templates/admin/settings/settings.mako')
620 renderer='rhodecode:templates/admin/settings/settings.mako')
621 def settings_hooks_update(self):
621 def settings_hooks_update(self):
622 _ = self.request.translate
622 _ = self.request.translate
623 c = self.load_default_context()
623 c = self.load_default_context()
624 c.active = 'hooks'
624 c.active = 'hooks'
625 if c.visual.allow_custom_hooks_settings:
625 if c.visual.allow_custom_hooks_settings:
626 ui_key = self.request.POST.get('new_hook_ui_key')
626 ui_key = self.request.POST.get('new_hook_ui_key')
627 ui_value = self.request.POST.get('new_hook_ui_value')
627 ui_value = self.request.POST.get('new_hook_ui_value')
628
628
629 hook_id = self.request.POST.get('hook_id')
629 hook_id = self.request.POST.get('hook_id')
630 new_hook = False
630 new_hook = False
631
631
632 model = SettingsModel()
632 model = SettingsModel()
633 try:
633 try:
634 if ui_value and ui_key:
634 if ui_value and ui_key:
635 model.create_or_update_hook(ui_key, ui_value)
635 model.create_or_update_hook(ui_key, ui_value)
636 h.flash(_('Added new hook'), category='success')
636 h.flash(_('Added new hook'), category='success')
637 new_hook = True
637 new_hook = True
638 elif hook_id:
638 elif hook_id:
639 RhodeCodeUi.delete(hook_id)
639 RhodeCodeUi.delete(hook_id)
640 Session().commit()
640 Session().commit()
641
641
642 # check for edits
642 # check for edits
643 update = False
643 update = False
644 _d = self.request.POST.dict_of_lists()
644 _d = self.request.POST.dict_of_lists()
645 for k, v in zip(_d.get('hook_ui_key', []),
645 for k, v in zip(_d.get('hook_ui_key', []),
646 _d.get('hook_ui_value_new', [])):
646 _d.get('hook_ui_value_new', [])):
647 model.create_or_update_hook(k, v)
647 model.create_or_update_hook(k, v)
648 update = True
648 update = True
649
649
650 if update and not new_hook:
650 if update and not new_hook:
651 h.flash(_('Updated hooks'), category='success')
651 h.flash(_('Updated hooks'), category='success')
652 Session().commit()
652 Session().commit()
653 except Exception:
653 except Exception:
654 log.exception("Exception during hook creation")
654 log.exception("Exception during hook creation")
655 h.flash(_('Error occurred during hook creation'),
655 h.flash(_('Error occurred during hook creation'),
656 category='error')
656 category='error')
657
657
658 raise HTTPFound(h.route_path('admin_settings_hooks'))
658 raise HTTPFound(h.route_path('admin_settings_hooks'))
659
659
660 @LoginRequired()
660 @LoginRequired()
661 @HasPermissionAllDecorator('hg.admin')
661 @HasPermissionAllDecorator('hg.admin')
662 @view_config(
662 @view_config(
663 route_name='admin_settings_search', request_method='GET',
663 route_name='admin_settings_search', request_method='GET',
664 renderer='rhodecode:templates/admin/settings/settings.mako')
664 renderer='rhodecode:templates/admin/settings/settings.mako')
665 def settings_search(self):
665 def settings_search(self):
666 c = self.load_default_context()
666 c = self.load_default_context()
667 c.active = 'search'
667 c.active = 'search'
668
668
669 searcher = searcher_from_config(self.request.registry.settings)
669 searcher = searcher_from_config(self.request.registry.settings)
670 c.statistics = searcher.statistics(self.request.translate)
670 c.statistics = searcher.statistics(self.request.translate)
671
671
672 return self._get_template_context(c)
672 return self._get_template_context(c)
673
673
674 @LoginRequired()
674 @LoginRequired()
675 @HasPermissionAllDecorator('hg.admin')
675 @HasPermissionAllDecorator('hg.admin')
676 @view_config(
676 @view_config(
677 route_name='admin_settings_automation', request_method='GET',
677 route_name='admin_settings_automation', request_method='GET',
678 renderer='rhodecode:templates/admin/settings/settings.mako')
678 renderer='rhodecode:templates/admin/settings/settings.mako')
679 def settings_automation(self):
679 def settings_automation(self):
680 c = self.load_default_context()
680 c = self.load_default_context()
681 c.active = 'automation'
681 c.active = 'automation'
682
682
683 return self._get_template_context(c)
683 return self._get_template_context(c)
684
684
685 @LoginRequired()
685 @LoginRequired()
686 @HasPermissionAllDecorator('hg.admin')
686 @HasPermissionAllDecorator('hg.admin')
687 @view_config(
687 @view_config(
688 route_name='admin_settings_labs', request_method='GET',
688 route_name='admin_settings_labs', request_method='GET',
689 renderer='rhodecode:templates/admin/settings/settings.mako')
689 renderer='rhodecode:templates/admin/settings/settings.mako')
690 def settings_labs(self):
690 def settings_labs(self):
691 c = self.load_default_context()
691 c = self.load_default_context()
692 if not c.labs_active:
692 if not c.labs_active:
693 raise HTTPFound(h.route_path('admin_settings'))
693 raise HTTPFound(h.route_path('admin_settings'))
694
694
695 c.active = 'labs'
695 c.active = 'labs'
696 c.lab_settings = _LAB_SETTINGS
696 c.lab_settings = _LAB_SETTINGS
697
697
698 data = render('rhodecode:templates/admin/settings/settings.mako',
698 data = render('rhodecode:templates/admin/settings/settings.mako',
699 self._get_template_context(c), self.request)
699 self._get_template_context(c), self.request)
700 html = formencode.htmlfill.render(
700 html = formencode.htmlfill.render(
701 data,
701 data,
702 defaults=self._form_defaults(),
702 defaults=self._form_defaults(),
703 encoding="UTF-8",
703 encoding="UTF-8",
704 force_defaults=False
704 force_defaults=False
705 )
705 )
706 return Response(html)
706 return Response(html)
707
707
708 @LoginRequired()
708 @LoginRequired()
709 @HasPermissionAllDecorator('hg.admin')
709 @HasPermissionAllDecorator('hg.admin')
710 @CSRFRequired()
710 @CSRFRequired()
711 @view_config(
711 @view_config(
712 route_name='admin_settings_labs_update', request_method='POST',
712 route_name='admin_settings_labs_update', request_method='POST',
713 renderer='rhodecode:templates/admin/settings/settings.mako')
713 renderer='rhodecode:templates/admin/settings/settings.mako')
714 def settings_labs_update(self):
714 def settings_labs_update(self):
715 _ = self.request.translate
715 _ = self.request.translate
716 c = self.load_default_context()
716 c = self.load_default_context()
717 c.active = 'labs'
717 c.active = 'labs'
718
718
719 application_form = LabsSettingsForm(self.request.translate)()
719 application_form = LabsSettingsForm(self.request.translate)()
720 try:
720 try:
721 form_result = application_form.to_python(dict(self.request.POST))
721 form_result = application_form.to_python(dict(self.request.POST))
722 except formencode.Invalid as errors:
722 except formencode.Invalid as errors:
723 h.flash(
723 h.flash(
724 _("Some form inputs contain invalid data."),
724 _("Some form inputs contain invalid data."),
725 category='error')
725 category='error')
726 data = render('rhodecode:templates/admin/settings/settings.mako',
726 data = render('rhodecode:templates/admin/settings/settings.mako',
727 self._get_template_context(c), self.request)
727 self._get_template_context(c), self.request)
728 html = formencode.htmlfill.render(
728 html = formencode.htmlfill.render(
729 data,
729 data,
730 defaults=errors.value,
730 defaults=errors.value,
731 errors=errors.error_dict or {},
731 errors=errors.error_dict or {},
732 prefix_error=False,
732 prefix_error=False,
733 encoding="UTF-8",
733 encoding="UTF-8",
734 force_defaults=False
734 force_defaults=False
735 )
735 )
736 return Response(html)
736 return Response(html)
737
737
738 try:
738 try:
739 session = Session()
739 session = Session()
740 for setting in _LAB_SETTINGS:
740 for setting in _LAB_SETTINGS:
741 setting_name = setting.key[len('rhodecode_'):]
741 setting_name = setting.key[len('rhodecode_'):]
742 sett = SettingsModel().create_or_update_setting(
742 sett = SettingsModel().create_or_update_setting(
743 setting_name, form_result[setting.key], setting.type)
743 setting_name, form_result[setting.key], setting.type)
744 session.add(sett)
744 session.add(sett)
745
745
746 except Exception:
746 except Exception:
747 log.exception('Exception while updating lab settings')
747 log.exception('Exception while updating lab settings')
748 h.flash(_('Error occurred during updating labs settings'),
748 h.flash(_('Error occurred during updating labs settings'),
749 category='error')
749 category='error')
750 else:
750 else:
751 Session().commit()
751 Session().commit()
752 SettingsModel().invalidate_settings_cache()
752 SettingsModel().invalidate_settings_cache()
753 h.flash(_('Updated Labs settings'), category='success')
753 h.flash(_('Updated Labs settings'), category='success')
754 raise HTTPFound(h.route_path('admin_settings_labs'))
754 raise HTTPFound(h.route_path('admin_settings_labs'))
755
755
756 data = render('rhodecode:templates/admin/settings/settings.mako',
756 data = render('rhodecode:templates/admin/settings/settings.mako',
757 self._get_template_context(c), self.request)
757 self._get_template_context(c), self.request)
758 html = formencode.htmlfill.render(
758 html = formencode.htmlfill.render(
759 data,
759 data,
760 defaults=self._form_defaults(),
760 defaults=self._form_defaults(),
761 encoding="UTF-8",
761 encoding="UTF-8",
762 force_defaults=False
762 force_defaults=False
763 )
763 )
764 return Response(html)
764 return Response(html)
765
765
766
766
767 # :param key: name of the setting including the 'rhodecode_' prefix
767 # :param key: name of the setting including the 'rhodecode_' prefix
768 # :param type: the RhodeCodeSetting type to use.
768 # :param type: the RhodeCodeSetting type to use.
769 # :param group: the i18ned group in which we should dispaly this setting
769 # :param group: the i18ned group in which we should dispaly this setting
770 # :param label: the i18ned label we should display for this setting
770 # :param label: the i18ned label we should display for this setting
771 # :param help: the i18ned help we should dispaly for this setting
771 # :param help: the i18ned help we should dispaly for this setting
772 LabSetting = collections.namedtuple(
772 LabSetting = collections.namedtuple(
773 'LabSetting', ('key', 'type', 'group', 'label', 'help'))
773 'LabSetting', ('key', 'type', 'group', 'label', 'help'))
774
774
775
775
776 # This list has to be kept in sync with the form
776 # This list has to be kept in sync with the form
777 # rhodecode.model.forms.LabsSettingsForm.
777 # rhodecode.model.forms.LabsSettingsForm.
778 _LAB_SETTINGS = [
778 _LAB_SETTINGS = [
779
779
780 ]
780 ]
@@ -1,202 +1,202 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2018 RhodeCode GmbH
3 # Copyright (C) 2016-2018 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 import urllib2
22 import urllib2
23
23
24 from pyramid.view import view_config
24 from pyramid.view import view_config
25
25
26 import rhodecode
26 import rhodecode
27 from rhodecode.apps._base import BaseAppView
27 from rhodecode.apps._base import BaseAppView
28 from rhodecode.apps.admin.navigation import navigation_list
28 from rhodecode.apps._base.navigation import navigation_list
29 from rhodecode.lib import helpers as h
29 from rhodecode.lib import helpers as h
30 from rhodecode.lib.auth import (LoginRequired, HasPermissionAllDecorator)
30 from rhodecode.lib.auth import (LoginRequired, HasPermissionAllDecorator)
31 from rhodecode.lib.utils2 import str2bool
31 from rhodecode.lib.utils2 import str2bool
32 from rhodecode.lib import system_info
32 from rhodecode.lib import system_info
33 from rhodecode.model.update import UpdateModel
33 from rhodecode.model.update import UpdateModel
34
34
35 log = logging.getLogger(__name__)
35 log = logging.getLogger(__name__)
36
36
37
37
38 class AdminSystemInfoSettingsView(BaseAppView):
38 class AdminSystemInfoSettingsView(BaseAppView):
39 def load_default_context(self):
39 def load_default_context(self):
40 c = self._get_local_tmpl_context()
40 c = self._get_local_tmpl_context()
41 return c
41 return c
42
42
43 @LoginRequired()
43 @LoginRequired()
44 @HasPermissionAllDecorator('hg.admin')
44 @HasPermissionAllDecorator('hg.admin')
45 @view_config(
45 @view_config(
46 route_name='admin_settings_system', request_method='GET',
46 route_name='admin_settings_system', request_method='GET',
47 renderer='rhodecode:templates/admin/settings/settings.mako')
47 renderer='rhodecode:templates/admin/settings/settings.mako')
48 def settings_system_info(self):
48 def settings_system_info(self):
49 _ = self.request.translate
49 _ = self.request.translate
50 c = self.load_default_context()
50 c = self.load_default_context()
51
51
52 c.active = 'system'
52 c.active = 'system'
53 c.navlist = navigation_list(self.request)
53 c.navlist = navigation_list(self.request)
54
54
55 # TODO(marcink), figure out how to allow only selected users to do this
55 # TODO(marcink), figure out how to allow only selected users to do this
56 c.allowed_to_snapshot = self._rhodecode_user.admin
56 c.allowed_to_snapshot = self._rhodecode_user.admin
57
57
58 snapshot = str2bool(self.request.params.get('snapshot'))
58 snapshot = str2bool(self.request.params.get('snapshot'))
59
59
60 c.rhodecode_update_url = UpdateModel().get_update_url()
60 c.rhodecode_update_url = UpdateModel().get_update_url()
61 server_info = system_info.get_system_info(self.request.environ)
61 server_info = system_info.get_system_info(self.request.environ)
62
62
63 for key, val in server_info.items():
63 for key, val in server_info.items():
64 setattr(c, key, val)
64 setattr(c, key, val)
65
65
66 def val(name, subkey='human_value'):
66 def val(name, subkey='human_value'):
67 return server_info[name][subkey]
67 return server_info[name][subkey]
68
68
69 def state(name):
69 def state(name):
70 return server_info[name]['state']
70 return server_info[name]['state']
71
71
72 def val2(name):
72 def val2(name):
73 val = server_info[name]['human_value']
73 val = server_info[name]['human_value']
74 state = server_info[name]['state']
74 state = server_info[name]['state']
75 return val, state
75 return val, state
76
76
77 update_info_msg = _('Note: please make sure this server can '
77 update_info_msg = _('Note: please make sure this server can '
78 'access `${url}` for the update link to work',
78 'access `${url}` for the update link to work',
79 mapping=dict(url=c.rhodecode_update_url))
79 mapping=dict(url=c.rhodecode_update_url))
80 version = UpdateModel().get_stored_version()
80 version = UpdateModel().get_stored_version()
81 is_outdated = UpdateModel().is_outdated(
81 is_outdated = UpdateModel().is_outdated(
82 rhodecode.__version__, version)
82 rhodecode.__version__, version)
83 update_state = {
83 update_state = {
84 'type': 'warning',
84 'type': 'warning',
85 'message': 'New version available: {}'.format(version)
85 'message': 'New version available: {}'.format(version)
86 } \
86 } \
87 if is_outdated else {}
87 if is_outdated else {}
88 c.data_items = [
88 c.data_items = [
89 # update info
89 # update info
90 (_('Update info'), h.literal(
90 (_('Update info'), h.literal(
91 '<span class="link" id="check_for_update" >%s.</span>' % (
91 '<span class="link" id="check_for_update" >%s.</span>' % (
92 _('Check for updates')) +
92 _('Check for updates')) +
93 '<br/> <span >%s.</span>' % (update_info_msg)
93 '<br/> <span >%s.</span>' % (update_info_msg)
94 ), ''),
94 ), ''),
95
95
96 # RhodeCode specific
96 # RhodeCode specific
97 (_('RhodeCode Version'), val('rhodecode_app')['text'], state('rhodecode_app')),
97 (_('RhodeCode Version'), val('rhodecode_app')['text'], state('rhodecode_app')),
98 (_('Latest version'), version, update_state),
98 (_('Latest version'), version, update_state),
99 (_('RhodeCode Base URL'), val('rhodecode_config')['config']['app.base_url'], state('rhodecode_config')),
99 (_('RhodeCode Base URL'), val('rhodecode_config')['config']['app.base_url'], state('rhodecode_config')),
100 (_('RhodeCode Server IP'), val('server')['server_ip'], state('server')),
100 (_('RhodeCode Server IP'), val('server')['server_ip'], state('server')),
101 (_('RhodeCode Server ID'), val('server')['server_id'], state('server')),
101 (_('RhodeCode Server ID'), val('server')['server_id'], state('server')),
102 (_('RhodeCode Configuration'), val('rhodecode_config')['path'], state('rhodecode_config')),
102 (_('RhodeCode Configuration'), val('rhodecode_config')['path'], state('rhodecode_config')),
103 (_('RhodeCode Certificate'), val('rhodecode_config')['cert_path'], state('rhodecode_config')),
103 (_('RhodeCode Certificate'), val('rhodecode_config')['cert_path'], state('rhodecode_config')),
104 (_('Workers'), val('rhodecode_config')['config']['server:main'].get('workers', '?'), state('rhodecode_config')),
104 (_('Workers'), val('rhodecode_config')['config']['server:main'].get('workers', '?'), state('rhodecode_config')),
105 (_('Worker Type'), val('rhodecode_config')['config']['server:main'].get('worker_class', 'sync'), state('rhodecode_config')),
105 (_('Worker Type'), val('rhodecode_config')['config']['server:main'].get('worker_class', 'sync'), state('rhodecode_config')),
106 ('', '', ''), # spacer
106 ('', '', ''), # spacer
107
107
108 # Database
108 # Database
109 (_('Database'), val('database')['url'], state('database')),
109 (_('Database'), val('database')['url'], state('database')),
110 (_('Database version'), val('database')['version'], state('database')),
110 (_('Database version'), val('database')['version'], state('database')),
111 ('', '', ''), # spacer
111 ('', '', ''), # spacer
112
112
113 # Platform/Python
113 # Platform/Python
114 (_('Platform'), val('platform')['name'], state('platform')),
114 (_('Platform'), val('platform')['name'], state('platform')),
115 (_('Platform UUID'), val('platform')['uuid'], state('platform')),
115 (_('Platform UUID'), val('platform')['uuid'], state('platform')),
116 (_('Lang'), val('locale'), state('locale')),
116 (_('Lang'), val('locale'), state('locale')),
117 (_('Python version'), val('python')['version'], state('python')),
117 (_('Python version'), val('python')['version'], state('python')),
118 (_('Python path'), val('python')['executable'], state('python')),
118 (_('Python path'), val('python')['executable'], state('python')),
119 ('', '', ''), # spacer
119 ('', '', ''), # spacer
120
120
121 # Systems stats
121 # Systems stats
122 (_('CPU'), val('cpu')['text'], state('cpu')),
122 (_('CPU'), val('cpu')['text'], state('cpu')),
123 (_('Load'), val('load')['text'], state('load')),
123 (_('Load'), val('load')['text'], state('load')),
124 (_('Memory'), val('memory')['text'], state('memory')),
124 (_('Memory'), val('memory')['text'], state('memory')),
125 (_('Uptime'), val('uptime')['text'], state('uptime')),
125 (_('Uptime'), val('uptime')['text'], state('uptime')),
126 ('', '', ''), # spacer
126 ('', '', ''), # spacer
127
127
128 # ulimit
128 # ulimit
129 (_('Ulimit'), val('ulimit')['text'], state('ulimit')),
129 (_('Ulimit'), val('ulimit')['text'], state('ulimit')),
130
130
131 # Repo storage
131 # Repo storage
132 (_('Storage location'), val('storage')['path'], state('storage')),
132 (_('Storage location'), val('storage')['path'], state('storage')),
133 (_('Storage info'), val('storage')['text'], state('storage')),
133 (_('Storage info'), val('storage')['text'], state('storage')),
134 (_('Storage inodes'), val('storage_inodes')['text'], state('storage_inodes')),
134 (_('Storage inodes'), val('storage_inodes')['text'], state('storage_inodes')),
135
135
136 (_('Gist storage location'), val('storage_gist')['path'], state('storage_gist')),
136 (_('Gist storage location'), val('storage_gist')['path'], state('storage_gist')),
137 (_('Gist storage info'), val('storage_gist')['text'], state('storage_gist')),
137 (_('Gist storage info'), val('storage_gist')['text'], state('storage_gist')),
138
138
139 (_('Archive cache storage location'), val('storage_archive')['path'], state('storage_archive')),
139 (_('Archive cache storage location'), val('storage_archive')['path'], state('storage_archive')),
140 (_('Archive cache info'), val('storage_archive')['text'], state('storage_archive')),
140 (_('Archive cache info'), val('storage_archive')['text'], state('storage_archive')),
141
141
142 (_('Temp storage location'), val('storage_temp')['path'], state('storage_temp')),
142 (_('Temp storage location'), val('storage_temp')['path'], state('storage_temp')),
143 (_('Temp storage info'), val('storage_temp')['text'], state('storage_temp')),
143 (_('Temp storage info'), val('storage_temp')['text'], state('storage_temp')),
144
144
145 (_('Search info'), val('search')['text'], state('search')),
145 (_('Search info'), val('search')['text'], state('search')),
146 (_('Search location'), val('search')['location'], state('search')),
146 (_('Search location'), val('search')['location'], state('search')),
147 ('', '', ''), # spacer
147 ('', '', ''), # spacer
148
148
149 # VCS specific
149 # VCS specific
150 (_('VCS Backends'), val('vcs_backends'), state('vcs_backends')),
150 (_('VCS Backends'), val('vcs_backends'), state('vcs_backends')),
151 (_('VCS Server'), val('vcs_server')['text'], state('vcs_server')),
151 (_('VCS Server'), val('vcs_server')['text'], state('vcs_server')),
152 (_('GIT'), val('git'), state('git')),
152 (_('GIT'), val('git'), state('git')),
153 (_('HG'), val('hg'), state('hg')),
153 (_('HG'), val('hg'), state('hg')),
154 (_('SVN'), val('svn'), state('svn')),
154 (_('SVN'), val('svn'), state('svn')),
155
155
156 ]
156 ]
157
157
158 if snapshot:
158 if snapshot:
159 if c.allowed_to_snapshot:
159 if c.allowed_to_snapshot:
160 c.data_items.pop(0) # remove server info
160 c.data_items.pop(0) # remove server info
161 self.request.override_renderer = 'admin/settings/settings_system_snapshot.mako'
161 self.request.override_renderer = 'admin/settings/settings_system_snapshot.mako'
162 else:
162 else:
163 h.flash('You are not allowed to do this', category='warning')
163 h.flash('You are not allowed to do this', category='warning')
164 return self._get_template_context(c)
164 return self._get_template_context(c)
165
165
166 @LoginRequired()
166 @LoginRequired()
167 @HasPermissionAllDecorator('hg.admin')
167 @HasPermissionAllDecorator('hg.admin')
168 @view_config(
168 @view_config(
169 route_name='admin_settings_system_update', request_method='GET',
169 route_name='admin_settings_system_update', request_method='GET',
170 renderer='rhodecode:templates/admin/settings/settings_system_update.mako')
170 renderer='rhodecode:templates/admin/settings/settings_system_update.mako')
171 def settings_system_info_check_update(self):
171 def settings_system_info_check_update(self):
172 _ = self.request.translate
172 _ = self.request.translate
173 c = self.load_default_context()
173 c = self.load_default_context()
174
174
175 update_url = UpdateModel().get_update_url()
175 update_url = UpdateModel().get_update_url()
176
176
177 _err = lambda s: '<div style="color:#ff8888; padding:4px 0px">{}</div>'.format(s)
177 _err = lambda s: '<div style="color:#ff8888; padding:4px 0px">{}</div>'.format(s)
178 try:
178 try:
179 data = UpdateModel().get_update_data(update_url)
179 data = UpdateModel().get_update_data(update_url)
180 except urllib2.URLError as e:
180 except urllib2.URLError as e:
181 log.exception("Exception contacting upgrade server")
181 log.exception("Exception contacting upgrade server")
182 self.request.override_renderer = 'string'
182 self.request.override_renderer = 'string'
183 return _err('Failed to contact upgrade server: %r' % e)
183 return _err('Failed to contact upgrade server: %r' % e)
184 except ValueError as e:
184 except ValueError as e:
185 log.exception("Bad data sent from update server")
185 log.exception("Bad data sent from update server")
186 self.request.override_renderer = 'string'
186 self.request.override_renderer = 'string'
187 return _err('Bad data sent from update server')
187 return _err('Bad data sent from update server')
188
188
189 latest = data['versions'][0]
189 latest = data['versions'][0]
190
190
191 c.update_url = update_url
191 c.update_url = update_url
192 c.latest_data = latest
192 c.latest_data = latest
193 c.latest_ver = latest['version']
193 c.latest_ver = latest['version']
194 c.cur_ver = rhodecode.__version__
194 c.cur_ver = rhodecode.__version__
195 c.should_upgrade = False
195 c.should_upgrade = False
196
196
197 is_oudated = UpdateModel().is_outdated(c.cur_ver, c.latest_ver)
197 is_oudated = UpdateModel().is_outdated(c.cur_ver, c.latest_ver)
198 if is_oudated:
198 if is_oudated:
199 c.should_upgrade = True
199 c.should_upgrade = True
200 c.important_notices = latest['general']
200 c.important_notices = latest['general']
201 UpdateModel().store_version(latest['version'])
201 UpdateModel().store_version(latest['version'])
202 return self._get_template_context(c)
202 return self._get_template_context(c)
@@ -1,101 +1,101 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2018 RhodeCode GmbH
3 # Copyright (C) 2016-2018 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 from rhodecode.apps.admin.navigation import NavigationRegistry
22 from rhodecode.apps._base.navigation import NavigationRegistry
23 from rhodecode.apps._base import ADMIN_PREFIX
23 from rhodecode.apps._base import ADMIN_PREFIX
24 from rhodecode.lib.utils2 import str2bool
24 from rhodecode.lib.utils2 import str2bool
25
25
26
26
27 def admin_routes(config):
27 def admin_routes(config):
28 """
28 """
29 User groups /_admin prefixed routes
29 User groups /_admin prefixed routes
30 """
30 """
31
31
32 config.add_route(
32 config.add_route(
33 name='user_group_members_data',
33 name='user_group_members_data',
34 pattern='/user_groups/{user_group_id:\d+}/members',
34 pattern='/user_groups/{user_group_id:\d+}/members',
35 user_group_route=True)
35 user_group_route=True)
36
36
37 # user groups perms
37 # user groups perms
38 config.add_route(
38 config.add_route(
39 name='edit_user_group_perms_summary',
39 name='edit_user_group_perms_summary',
40 pattern='/user_groups/{user_group_id:\d+}/edit/permissions_summary',
40 pattern='/user_groups/{user_group_id:\d+}/edit/permissions_summary',
41 user_group_route=True)
41 user_group_route=True)
42 config.add_route(
42 config.add_route(
43 name='edit_user_group_perms_summary_json',
43 name='edit_user_group_perms_summary_json',
44 pattern='/user_groups/{user_group_id:\d+}/edit/permissions_summary/json',
44 pattern='/user_groups/{user_group_id:\d+}/edit/permissions_summary/json',
45 user_group_route=True)
45 user_group_route=True)
46
46
47 # user groups edit
47 # user groups edit
48 config.add_route(
48 config.add_route(
49 name='edit_user_group',
49 name='edit_user_group',
50 pattern='/user_groups/{user_group_id:\d+}/edit',
50 pattern='/user_groups/{user_group_id:\d+}/edit',
51 user_group_route=True)
51 user_group_route=True)
52
52
53 # user groups update
53 # user groups update
54 config.add_route(
54 config.add_route(
55 name='user_groups_update',
55 name='user_groups_update',
56 pattern='/user_groups/{user_group_id:\d+}/update',
56 pattern='/user_groups/{user_group_id:\d+}/update',
57 user_group_route=True)
57 user_group_route=True)
58
58
59 config.add_route(
59 config.add_route(
60 name='edit_user_group_global_perms',
60 name='edit_user_group_global_perms',
61 pattern='/user_groups/{user_group_id:\d+}/edit/global_permissions',
61 pattern='/user_groups/{user_group_id:\d+}/edit/global_permissions',
62 user_group_route=True)
62 user_group_route=True)
63
63
64 config.add_route(
64 config.add_route(
65 name='edit_user_group_global_perms_update',
65 name='edit_user_group_global_perms_update',
66 pattern='/user_groups/{user_group_id:\d+}/edit/global_permissions/update',
66 pattern='/user_groups/{user_group_id:\d+}/edit/global_permissions/update',
67 user_group_route=True)
67 user_group_route=True)
68
68
69 config.add_route(
69 config.add_route(
70 name='edit_user_group_perms',
70 name='edit_user_group_perms',
71 pattern='/user_groups/{user_group_id:\d+}/edit/permissions',
71 pattern='/user_groups/{user_group_id:\d+}/edit/permissions',
72 user_group_route=True)
72 user_group_route=True)
73
73
74 config.add_route(
74 config.add_route(
75 name='edit_user_group_perms_update',
75 name='edit_user_group_perms_update',
76 pattern='/user_groups/{user_group_id:\d+}/edit/permissions/update',
76 pattern='/user_groups/{user_group_id:\d+}/edit/permissions/update',
77 user_group_route=True)
77 user_group_route=True)
78
78
79 config.add_route(
79 config.add_route(
80 name='edit_user_group_advanced',
80 name='edit_user_group_advanced',
81 pattern='/user_groups/{user_group_id:\d+}/edit/advanced',
81 pattern='/user_groups/{user_group_id:\d+}/edit/advanced',
82 user_group_route=True)
82 user_group_route=True)
83
83
84 config.add_route(
84 config.add_route(
85 name='edit_user_group_advanced_sync',
85 name='edit_user_group_advanced_sync',
86 pattern='/user_groups/{user_group_id:\d+}/edit/advanced/sync',
86 pattern='/user_groups/{user_group_id:\d+}/edit/advanced/sync',
87 user_group_route=True)
87 user_group_route=True)
88
88
89 # user groups delete
89 # user groups delete
90 config.add_route(
90 config.add_route(
91 name='user_groups_delete',
91 name='user_groups_delete',
92 pattern='/user_groups/{user_group_id:\d+}/delete',
92 pattern='/user_groups/{user_group_id:\d+}/delete',
93 user_group_route=True)
93 user_group_route=True)
94
94
95
95
96 def includeme(config):
96 def includeme(config):
97 # main admin routes
97 # main admin routes
98 config.include(admin_routes, route_prefix=ADMIN_PREFIX)
98 config.include(admin_routes, route_prefix=ADMIN_PREFIX)
99
99
100 # Scan module for configuration decorators.
100 # Scan module for configuration decorators.
101 config.scan('.views', ignore='.tests')
101 config.scan('.views', ignore='.tests')
@@ -1,132 +1,137 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2018 RhodeCode GmbH
3 # Copyright (C) 2012-2018 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import os
21 import os
22 import logging
22 import logging
23 import importlib
23 import importlib
24
24
25 from pkg_resources import iter_entry_points
25 from pkg_resources import iter_entry_points
26 from pyramid.authentication import SessionAuthenticationPolicy
26 from pyramid.authentication import SessionAuthenticationPolicy
27
27
28 from rhodecode.authentication.registry import AuthenticationPluginRegistry
28 from rhodecode.authentication.registry import AuthenticationPluginRegistry
29 from rhodecode.authentication.routes import root_factory
29 from rhodecode.authentication.routes import root_factory
30 from rhodecode.authentication.routes import AuthnRootResource
30 from rhodecode.authentication.routes import AuthnRootResource
31 from rhodecode.apps._base import ADMIN_PREFIX
31 from rhodecode.apps._base import ADMIN_PREFIX
32 from rhodecode.model.settings import SettingsModel
32 from rhodecode.model.settings import SettingsModel
33
33
34
34
35 log = logging.getLogger(__name__)
35 log = logging.getLogger(__name__)
36
36
37 # Plugin ID prefixes to distinct between normal and legacy plugins.
37 # Plugin ID prefixes to distinct between normal and legacy plugins.
38 plugin_prefix = 'egg:'
38 plugin_prefix = 'egg:'
39 legacy_plugin_prefix = 'py:'
39 legacy_plugin_prefix = 'py:'
40 plugin_default_auth_ttl = 30
40 plugin_default_auth_ttl = 30
41
41
42
42
43 # TODO: Currently this is only used to discover the authentication plugins.
43 # TODO: Currently this is only used to discover the authentication plugins.
44 # Later on this may be used in a generic way to look up and include all kinds
44 # Later on this may be used in a generic way to look up and include all kinds
45 # of supported enterprise plugins. Therefore this has to be moved and
45 # of supported enterprise plugins. Therefore this has to be moved and
46 # refactored to a real 'plugin look up' machinery.
46 # refactored to a real 'plugin look up' machinery.
47 # TODO: When refactoring this think about splitting it up into distinct
47 # TODO: When refactoring this think about splitting it up into distinct
48 # discover, load and include phases.
48 # discover, load and include phases.
49 def _discover_plugins(config, entry_point='enterprise.plugins1'):
49 def _discover_plugins(config, entry_point='enterprise.plugins1'):
50 log.debug('authentication: running plugin discovery for entrypoint %s',
51 entry_point)
52
50 for ep in iter_entry_points(entry_point):
53 for ep in iter_entry_points(entry_point):
51 plugin_id = '{}{}#{}'.format(
54 plugin_id = '{}{}#{}'.format(
52 plugin_prefix, ep.dist.project_name, ep.name)
55 plugin_prefix, ep.dist.project_name, ep.name)
53 log.debug('Plugin discovered: "%s"', plugin_id)
56 log.debug('Plugin discovered: "%s"', plugin_id)
54 try:
57 try:
55 module = ep.load()
58 module = ep.load()
56 plugin = module(plugin_id=plugin_id)
59 plugin = module(plugin_id=plugin_id)
57 config.include(plugin.includeme)
60 config.include(plugin.includeme)
58 except Exception as e:
61 except Exception as e:
59 log.exception(
62 log.exception(
60 'Exception while loading authentication plugin '
63 'Exception while loading authentication plugin '
61 '"{}": {}'.format(plugin_id, e.message))
64 '"{}": {}'.format(plugin_id, e.message))
62
65
63
66
64 def _import_legacy_plugin(plugin_id):
67 def _import_legacy_plugin(plugin_id):
65 module_name = plugin_id.split(legacy_plugin_prefix, 1)[-1]
68 module_name = plugin_id.split(legacy_plugin_prefix, 1)[-1]
66 module = importlib.import_module(module_name)
69 module = importlib.import_module(module_name)
67 return module.plugin_factory(plugin_id=plugin_id)
70 return module.plugin_factory(plugin_id=plugin_id)
68
71
69
72
70 def _discover_legacy_plugins(config, prefix=legacy_plugin_prefix):
73 def _discover_legacy_plugins(config, prefix=legacy_plugin_prefix):
71 """
74 """
72 Function that imports the legacy plugins stored in the 'auth_plugins'
75 Function that imports the legacy plugins stored in the 'auth_plugins'
73 setting in database which are using the specified prefix. Normally 'py:' is
76 setting in database which are using the specified prefix. Normally 'py:' is
74 used for the legacy plugins.
77 used for the legacy plugins.
75 """
78 """
79 log.debug('authentication: running legacy plugin discovery for prefix %s',
80 legacy_plugin_prefix)
76 try:
81 try:
77 auth_plugins = SettingsModel().get_setting_by_name('auth_plugins')
82 auth_plugins = SettingsModel().get_setting_by_name('auth_plugins')
78 enabled_plugins = auth_plugins.app_settings_value
83 enabled_plugins = auth_plugins.app_settings_value
79 legacy_plugins = [id_ for id_ in enabled_plugins if id_.startswith(prefix)]
84 legacy_plugins = [id_ for id_ in enabled_plugins if id_.startswith(prefix)]
80 except Exception:
85 except Exception:
81 legacy_plugins = []
86 legacy_plugins = []
82
87
83 for plugin_id in legacy_plugins:
88 for plugin_id in legacy_plugins:
84 log.debug('Legacy plugin discovered: "%s"', plugin_id)
89 log.debug('Legacy plugin discovered: "%s"', plugin_id)
85 try:
90 try:
86 plugin = _import_legacy_plugin(plugin_id)
91 plugin = _import_legacy_plugin(plugin_id)
87 config.include(plugin.includeme)
92 config.include(plugin.includeme)
88 except Exception as e:
93 except Exception as e:
89 log.exception(
94 log.exception(
90 'Exception while loading legacy authentication plugin '
95 'Exception while loading legacy authentication plugin '
91 '"{}": {}'.format(plugin_id, e.message))
96 '"{}": {}'.format(plugin_id, e.message))
92
97
93
98
94 def includeme(config):
99 def includeme(config):
95 # Set authentication policy.
100 # Set authentication policy.
96 authn_policy = SessionAuthenticationPolicy()
101 authn_policy = SessionAuthenticationPolicy()
97 config.set_authentication_policy(authn_policy)
102 config.set_authentication_policy(authn_policy)
98
103
99 # Create authentication plugin registry and add it to the pyramid registry.
104 # Create authentication plugin registry and add it to the pyramid registry.
100 authn_registry = AuthenticationPluginRegistry(config.get_settings())
105 authn_registry = AuthenticationPluginRegistry(config.get_settings())
101 config.add_directive('add_authn_plugin', authn_registry.add_authn_plugin)
106 config.add_directive('add_authn_plugin', authn_registry.add_authn_plugin)
102 config.registry.registerUtility(authn_registry)
107 config.registry.registerUtility(authn_registry)
103
108
104 # Create authentication traversal root resource.
109 # Create authentication traversal root resource.
105 authn_root_resource = root_factory()
110 authn_root_resource = root_factory()
106 config.add_directive('add_authn_resource',
111 config.add_directive('add_authn_resource',
107 authn_root_resource.add_authn_resource)
112 authn_root_resource.add_authn_resource)
108
113
109 # Add the authentication traversal route.
114 # Add the authentication traversal route.
110 config.add_route('auth_home',
115 config.add_route('auth_home',
111 ADMIN_PREFIX + '/auth*traverse',
116 ADMIN_PREFIX + '/auth*traverse',
112 factory=root_factory)
117 factory=root_factory)
113 # Add the authentication settings root views.
118 # Add the authentication settings root views.
114 config.add_view('rhodecode.authentication.views.AuthSettingsView',
119 config.add_view('rhodecode.authentication.views.AuthSettingsView',
115 attr='index',
120 attr='index',
116 request_method='GET',
121 request_method='GET',
117 route_name='auth_home',
122 route_name='auth_home',
118 context=AuthnRootResource)
123 context=AuthnRootResource)
119 config.add_view('rhodecode.authentication.views.AuthSettingsView',
124 config.add_view('rhodecode.authentication.views.AuthSettingsView',
120 attr='auth_settings',
125 attr='auth_settings',
121 request_method='POST',
126 request_method='POST',
122 route_name='auth_home',
127 route_name='auth_home',
123 context=AuthnRootResource)
128 context=AuthnRootResource)
124
129
125 for key in ['RC_CMD_SETUP_RC', 'RC_CMD_UPGRADE_DB', 'RC_CMD_SSH_WRAPPER']:
130 for key in ['RC_CMD_SETUP_RC', 'RC_CMD_UPGRADE_DB', 'RC_CMD_SSH_WRAPPER']:
126 if os.environ.get(key):
131 if os.environ.get(key):
127 # skip this heavy step below on certain CLI commands
132 # skip this heavy step below on certain CLI commands
128 return
133 return
129
134
130 # Auto discover authentication plugins and include their configuration.
135 # Auto discover authentication plugins and include their configuration.
131 _discover_plugins(config)
136 _discover_plugins(config)
132 _discover_legacy_plugins(config)
137 _discover_legacy_plugins(config)
@@ -1,585 +1,588 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2018 RhodeCode GmbH
3 # Copyright (C) 2010-2018 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import os
21 import os
22 import sys
22 import sys
23 import logging
23 import logging
24 import collections
24 import collections
25 import tempfile
25 import tempfile
26 import time
26
27
27 from paste.gzipper import make_gzip_middleware
28 from paste.gzipper import make_gzip_middleware
28 import pyramid.events
29 import pyramid.events
29 from pyramid.wsgi import wsgiapp
30 from pyramid.wsgi import wsgiapp
30 from pyramid.authorization import ACLAuthorizationPolicy
31 from pyramid.authorization import ACLAuthorizationPolicy
31 from pyramid.config import Configurator
32 from pyramid.config import Configurator
32 from pyramid.settings import asbool, aslist
33 from pyramid.settings import asbool, aslist
33 from pyramid.httpexceptions import (
34 from pyramid.httpexceptions import (
34 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound, HTTPNotFound)
35 HTTPException, HTTPError, HTTPInternalServerError, HTTPFound, HTTPNotFound)
35 from pyramid.renderers import render_to_response
36 from pyramid.renderers import render_to_response
36
37
37 from rhodecode.model import meta
38 from rhodecode.model import meta
38 from rhodecode.config import patches
39 from rhodecode.config import patches
39 from rhodecode.config import utils as config_utils
40 from rhodecode.config import utils as config_utils
40 from rhodecode.config.environment import load_pyramid_environment
41 from rhodecode.config.environment import load_pyramid_environment
41
42
42 import rhodecode.events
43 import rhodecode.events
43 from rhodecode.lib.middleware.vcs import VCSMiddleware
44 from rhodecode.lib.middleware.vcs import VCSMiddleware
44 from rhodecode.lib.request import Request
45 from rhodecode.lib.request import Request
45 from rhodecode.lib.vcs import VCSCommunicationError
46 from rhodecode.lib.vcs import VCSCommunicationError
46 from rhodecode.lib.exceptions import VCSServerUnavailable
47 from rhodecode.lib.exceptions import VCSServerUnavailable
47 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
48 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
48 from rhodecode.lib.middleware.https_fixup import HttpsFixup
49 from rhodecode.lib.middleware.https_fixup import HttpsFixup
49 from rhodecode.lib.celerylib.loader import configure_celery
50 from rhodecode.lib.celerylib.loader import configure_celery
50 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
51 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
51 from rhodecode.lib.utils2 import aslist as rhodecode_aslist, AttributeDict
52 from rhodecode.lib.utils2 import aslist as rhodecode_aslist, AttributeDict
52 from rhodecode.lib.exc_tracking import store_exception
53 from rhodecode.lib.exc_tracking import store_exception
53 from rhodecode.subscribers import (
54 from rhodecode.subscribers import (
54 scan_repositories_if_enabled, write_js_routes_if_enabled,
55 scan_repositories_if_enabled, write_js_routes_if_enabled,
55 write_metadata_if_needed, inject_app_settings)
56 write_metadata_if_needed, inject_app_settings)
56
57
57
58
58 log = logging.getLogger(__name__)
59 log = logging.getLogger(__name__)
59
60
60
61
61 def is_http_error(response):
62 def is_http_error(response):
62 # error which should have traceback
63 # error which should have traceback
63 return response.status_code > 499
64 return response.status_code > 499
64
65
65
66
66 def make_pyramid_app(global_config, **settings):
67 def make_pyramid_app(global_config, **settings):
67 """
68 """
68 Constructs the WSGI application based on Pyramid.
69 Constructs the WSGI application based on Pyramid.
69
70
70 Specials:
71 Specials:
71
72
72 * The application can also be integrated like a plugin via the call to
73 * The application can also be integrated like a plugin via the call to
73 `includeme`. This is accompanied with the other utility functions which
74 `includeme`. This is accompanied with the other utility functions which
74 are called. Changing this should be done with great care to not break
75 are called. Changing this should be done with great care to not break
75 cases when these fragments are assembled from another place.
76 cases when these fragments are assembled from another place.
76
77
77 """
78 """
78
79
79 # Allows to use format style "{ENV_NAME}" placeholders in the configuration. It
80 # Allows to use format style "{ENV_NAME}" placeholders in the configuration. It
80 # will be replaced by the value of the environment variable "NAME" in this case.
81 # will be replaced by the value of the environment variable "NAME" in this case.
81 environ = {
82 start_time = time.time()
82 'ENV_{}'.format(key): value for key, value in os.environ.items()}
83
84 environ = {'ENV_{}'.format(key): value for key, value in os.environ.items()}
83
85
84 global_config = _substitute_values(global_config, environ)
86 global_config = _substitute_values(global_config, environ)
85 settings = _substitute_values(settings, environ)
87 settings = _substitute_values(settings, environ)
86
88
87 sanitize_settings_and_apply_defaults(settings)
89 sanitize_settings_and_apply_defaults(settings)
88
90
89 config = Configurator(settings=settings)
91 config = Configurator(settings=settings)
90
92
91 # Apply compatibility patches
93 # Apply compatibility patches
92 patches.inspect_getargspec()
94 patches.inspect_getargspec()
93
95
94 load_pyramid_environment(global_config, settings)
96 load_pyramid_environment(global_config, settings)
95
97
96 # Static file view comes first
98 # Static file view comes first
97 includeme_first(config)
99 includeme_first(config)
98
100
99 includeme(config)
101 includeme(config)
100
102
101 pyramid_app = config.make_wsgi_app()
103 pyramid_app = config.make_wsgi_app()
102 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
104 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
103 pyramid_app.config = config
105 pyramid_app.config = config
104
106
105 config.configure_celery(global_config['__file__'])
107 config.configure_celery(global_config['__file__'])
106 # creating the app uses a connection - return it after we are done
108 # creating the app uses a connection - return it after we are done
107 meta.Session.remove()
109 meta.Session.remove()
108
110 total_time = time.time() - start_time
109 log.info('Pyramid app %s created and configured.', pyramid_app)
111 log.info('Pyramid app %s created and configured in %.2fs', pyramid_app, total_time)
110 return pyramid_app
112 return pyramid_app
111
113
112
114
113 def not_found_view(request):
115 def not_found_view(request):
114 """
116 """
115 This creates the view which should be registered as not-found-view to
117 This creates the view which should be registered as not-found-view to
116 pyramid.
118 pyramid.
117 """
119 """
118
120
119 if not getattr(request, 'vcs_call', None):
121 if not getattr(request, 'vcs_call', None):
120 # handle like regular case with our error_handler
122 # handle like regular case with our error_handler
121 return error_handler(HTTPNotFound(), request)
123 return error_handler(HTTPNotFound(), request)
122
124
123 # handle not found view as a vcs call
125 # handle not found view as a vcs call
124 settings = request.registry.settings
126 settings = request.registry.settings
125 ae_client = getattr(request, 'ae_client', None)
127 ae_client = getattr(request, 'ae_client', None)
126 vcs_app = VCSMiddleware(
128 vcs_app = VCSMiddleware(
127 HTTPNotFound(), request.registry, settings,
129 HTTPNotFound(), request.registry, settings,
128 appenlight_client=ae_client)
130 appenlight_client=ae_client)
129
131
130 return wsgiapp(vcs_app)(None, request)
132 return wsgiapp(vcs_app)(None, request)
131
133
132
134
133 def error_handler(exception, request):
135 def error_handler(exception, request):
134 import rhodecode
136 import rhodecode
135 from rhodecode.lib import helpers
137 from rhodecode.lib import helpers
136
138
137 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
139 rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode'
138
140
139 base_response = HTTPInternalServerError()
141 base_response = HTTPInternalServerError()
140 # prefer original exception for the response since it may have headers set
142 # prefer original exception for the response since it may have headers set
141 if isinstance(exception, HTTPException):
143 if isinstance(exception, HTTPException):
142 base_response = exception
144 base_response = exception
143 elif isinstance(exception, VCSCommunicationError):
145 elif isinstance(exception, VCSCommunicationError):
144 base_response = VCSServerUnavailable()
146 base_response = VCSServerUnavailable()
145
147
146 if is_http_error(base_response):
148 if is_http_error(base_response):
147 log.exception(
149 log.exception(
148 'error occurred handling this request for path: %s', request.path)
150 'error occurred handling this request for path: %s', request.path)
149
151
150 error_explanation = base_response.explanation or str(base_response)
152 error_explanation = base_response.explanation or str(base_response)
151 if base_response.status_code == 404:
153 if base_response.status_code == 404:
152 error_explanation += " Or you don't have permission to access it."
154 error_explanation += " Or you don't have permission to access it."
153 c = AttributeDict()
155 c = AttributeDict()
154 c.error_message = base_response.status
156 c.error_message = base_response.status
155 c.error_explanation = error_explanation
157 c.error_explanation = error_explanation
156 c.visual = AttributeDict()
158 c.visual = AttributeDict()
157
159
158 c.visual.rhodecode_support_url = (
160 c.visual.rhodecode_support_url = (
159 request.registry.settings.get('rhodecode_support_url') or
161 request.registry.settings.get('rhodecode_support_url') or
160 request.route_url('rhodecode_support')
162 request.route_url('rhodecode_support')
161 )
163 )
162 c.redirect_time = 0
164 c.redirect_time = 0
163 c.rhodecode_name = rhodecode_title
165 c.rhodecode_name = rhodecode_title
164 if not c.rhodecode_name:
166 if not c.rhodecode_name:
165 c.rhodecode_name = 'Rhodecode'
167 c.rhodecode_name = 'Rhodecode'
166
168
167 c.causes = []
169 c.causes = []
168 if is_http_error(base_response):
170 if is_http_error(base_response):
169 c.causes.append('Server is overloaded.')
171 c.causes.append('Server is overloaded.')
170 c.causes.append('Server database connection is lost.')
172 c.causes.append('Server database connection is lost.')
171 c.causes.append('Server expected unhandled error.')
173 c.causes.append('Server expected unhandled error.')
172
174
173 if hasattr(base_response, 'causes'):
175 if hasattr(base_response, 'causes'):
174 c.causes = base_response.causes
176 c.causes = base_response.causes
175
177
176 c.messages = helpers.flash.pop_messages(request=request)
178 c.messages = helpers.flash.pop_messages(request=request)
177
179
178 exc_info = sys.exc_info()
180 exc_info = sys.exc_info()
179 c.exception_id = id(exc_info)
181 c.exception_id = id(exc_info)
180 c.show_exception_id = isinstance(base_response, VCSServerUnavailable) \
182 c.show_exception_id = isinstance(base_response, VCSServerUnavailable) \
181 or base_response.status_code > 499
183 or base_response.status_code > 499
182 c.exception_id_url = request.route_url(
184 c.exception_id_url = request.route_url(
183 'admin_settings_exception_tracker_show', exception_id=c.exception_id)
185 'admin_settings_exception_tracker_show', exception_id=c.exception_id)
184
186
185 if c.show_exception_id:
187 if c.show_exception_id:
186 store_exception(c.exception_id, exc_info)
188 store_exception(c.exception_id, exc_info)
187
189
188 response = render_to_response(
190 response = render_to_response(
189 '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request,
191 '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request,
190 response=base_response)
192 response=base_response)
191
193
192 return response
194 return response
193
195
194
196
195 def includeme_first(config):
197 def includeme_first(config):
196 # redirect automatic browser favicon.ico requests to correct place
198 # redirect automatic browser favicon.ico requests to correct place
197 def favicon_redirect(context, request):
199 def favicon_redirect(context, request):
198 return HTTPFound(
200 return HTTPFound(
199 request.static_path('rhodecode:public/images/favicon.ico'))
201 request.static_path('rhodecode:public/images/favicon.ico'))
200
202
201 config.add_view(favicon_redirect, route_name='favicon')
203 config.add_view(favicon_redirect, route_name='favicon')
202 config.add_route('favicon', '/favicon.ico')
204 config.add_route('favicon', '/favicon.ico')
203
205
204 def robots_redirect(context, request):
206 def robots_redirect(context, request):
205 return HTTPFound(
207 return HTTPFound(
206 request.static_path('rhodecode:public/robots.txt'))
208 request.static_path('rhodecode:public/robots.txt'))
207
209
208 config.add_view(robots_redirect, route_name='robots')
210 config.add_view(robots_redirect, route_name='robots')
209 config.add_route('robots', '/robots.txt')
211 config.add_route('robots', '/robots.txt')
210
212
211 config.add_static_view(
213 config.add_static_view(
212 '_static/deform', 'deform:static')
214 '_static/deform', 'deform:static')
213 config.add_static_view(
215 config.add_static_view(
214 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
216 '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24)
215
217
216
218
217 def includeme(config):
219 def includeme(config):
220 log.debug('Initializing main includeme from %s', os.path.basename(__file__))
218 settings = config.registry.settings
221 settings = config.registry.settings
219 config.set_request_factory(Request)
222 config.set_request_factory(Request)
220
223
221 # plugin information
224 # plugin information
222 config.registry.rhodecode_plugins = collections.OrderedDict()
225 config.registry.rhodecode_plugins = collections.OrderedDict()
223
226
224 config.add_directive(
227 config.add_directive(
225 'register_rhodecode_plugin', register_rhodecode_plugin)
228 'register_rhodecode_plugin', register_rhodecode_plugin)
226
229
227 config.add_directive('configure_celery', configure_celery)
230 config.add_directive('configure_celery', configure_celery)
228
231
229 if asbool(settings.get('appenlight', 'false')):
232 if asbool(settings.get('appenlight', 'false')):
230 config.include('appenlight_client.ext.pyramid_tween')
233 config.include('appenlight_client.ext.pyramid_tween')
231
234
232 # Includes which are required. The application would fail without them.
235 # Includes which are required. The application would fail without them.
233 config.include('pyramid_mako')
236 config.include('pyramid_mako')
234 config.include('pyramid_beaker')
237 config.include('pyramid_beaker')
235 config.include('rhodecode.lib.rc_cache')
238 config.include('rhodecode.lib.rc_cache')
236
239
237 config.include('rhodecode.authentication')
240 config.include('rhodecode.authentication')
238 config.include('rhodecode.integrations')
241 config.include('rhodecode.integrations')
239
242
243 config.include('rhodecode.apps._base.navigation')
244 config.include('rhodecode.apps._base.subscribers')
245 config.include('rhodecode.tweens')
246
240 # apps
247 # apps
241 config.include('rhodecode.apps._base')
248 config.include('rhodecode.apps._base')
242 config.include('rhodecode.apps.ops')
249 config.include('rhodecode.apps.ops')
243
244 config.include('rhodecode.apps.admin')
250 config.include('rhodecode.apps.admin')
245 config.include('rhodecode.apps.channelstream')
251 config.include('rhodecode.apps.channelstream')
246 config.include('rhodecode.apps.login')
252 config.include('rhodecode.apps.login')
247 config.include('rhodecode.apps.home')
253 config.include('rhodecode.apps.home')
248 config.include('rhodecode.apps.journal')
254 config.include('rhodecode.apps.journal')
249 config.include('rhodecode.apps.repository')
255 config.include('rhodecode.apps.repository')
250 config.include('rhodecode.apps.repo_group')
256 config.include('rhodecode.apps.repo_group')
251 config.include('rhodecode.apps.user_group')
257 config.include('rhodecode.apps.user_group')
252 config.include('rhodecode.apps.search')
258 config.include('rhodecode.apps.search')
253 config.include('rhodecode.apps.user_profile')
259 config.include('rhodecode.apps.user_profile')
254 config.include('rhodecode.apps.user_group_profile')
260 config.include('rhodecode.apps.user_group_profile')
255 config.include('rhodecode.apps.my_account')
261 config.include('rhodecode.apps.my_account')
256 config.include('rhodecode.apps.svn_support')
262 config.include('rhodecode.apps.svn_support')
257 config.include('rhodecode.apps.ssh_support')
263 config.include('rhodecode.apps.ssh_support')
258 config.include('rhodecode.apps.gist')
264 config.include('rhodecode.apps.gist')
259
260 config.include('rhodecode.apps.debug_style')
265 config.include('rhodecode.apps.debug_style')
261 config.include('rhodecode.tweens')
262 config.include('rhodecode.api')
266 config.include('rhodecode.api')
263
267
264 config.add_route(
268 config.add_route('rhodecode_support', 'https://rhodecode.com/help/', static=True)
265 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
266
269
267 config.add_translation_dirs('rhodecode:i18n/')
270 config.add_translation_dirs('rhodecode:i18n/')
268 settings['default_locale_name'] = settings.get('lang', 'en')
271 settings['default_locale_name'] = settings.get('lang', 'en')
269
272
270 # Add subscribers.
273 # Add subscribers.
271 config.add_subscriber(inject_app_settings,
274 config.add_subscriber(inject_app_settings,
272 pyramid.events.ApplicationCreated)
275 pyramid.events.ApplicationCreated)
273 config.add_subscriber(scan_repositories_if_enabled,
276 config.add_subscriber(scan_repositories_if_enabled,
274 pyramid.events.ApplicationCreated)
277 pyramid.events.ApplicationCreated)
275 config.add_subscriber(write_metadata_if_needed,
278 config.add_subscriber(write_metadata_if_needed,
276 pyramid.events.ApplicationCreated)
279 pyramid.events.ApplicationCreated)
277 config.add_subscriber(write_js_routes_if_enabled,
280 config.add_subscriber(write_js_routes_if_enabled,
278 pyramid.events.ApplicationCreated)
281 pyramid.events.ApplicationCreated)
279
282
280 # request custom methods
283 # request custom methods
281 config.add_request_method(
284 config.add_request_method(
282 'rhodecode.lib.partial_renderer.get_partial_renderer',
285 'rhodecode.lib.partial_renderer.get_partial_renderer',
283 'get_partial_renderer')
286 'get_partial_renderer')
284
287
285 # Set the authorization policy.
288 # Set the authorization policy.
286 authz_policy = ACLAuthorizationPolicy()
289 authz_policy = ACLAuthorizationPolicy()
287 config.set_authorization_policy(authz_policy)
290 config.set_authorization_policy(authz_policy)
288
291
289 # Set the default renderer for HTML templates to mako.
292 # Set the default renderer for HTML templates to mako.
290 config.add_mako_renderer('.html')
293 config.add_mako_renderer('.html')
291
294
292 config.add_renderer(
295 config.add_renderer(
293 name='json_ext',
296 name='json_ext',
294 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
297 factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json')
295
298
296 # include RhodeCode plugins
299 # include RhodeCode plugins
297 includes = aslist(settings.get('rhodecode.includes', []))
300 includes = aslist(settings.get('rhodecode.includes', []))
298 for inc in includes:
301 for inc in includes:
299 config.include(inc)
302 config.include(inc)
300
303
301 # custom not found view, if our pyramid app doesn't know how to handle
304 # custom not found view, if our pyramid app doesn't know how to handle
302 # the request pass it to potential VCS handling ap
305 # the request pass it to potential VCS handling ap
303 config.add_notfound_view(not_found_view)
306 config.add_notfound_view(not_found_view)
304 if not settings.get('debugtoolbar.enabled', False):
307 if not settings.get('debugtoolbar.enabled', False):
305 # disabled debugtoolbar handle all exceptions via the error_handlers
308 # disabled debugtoolbar handle all exceptions via the error_handlers
306 config.add_view(error_handler, context=Exception)
309 config.add_view(error_handler, context=Exception)
307
310
308 # all errors including 403/404/50X
311 # all errors including 403/404/50X
309 config.add_view(error_handler, context=HTTPError)
312 config.add_view(error_handler, context=HTTPError)
310
313
311
314
312 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
315 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
313 """
316 """
314 Apply outer WSGI middlewares around the application.
317 Apply outer WSGI middlewares around the application.
315 """
318 """
316 registry = config.registry
319 registry = config.registry
317 settings = registry.settings
320 settings = registry.settings
318
321
319 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
322 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
320 pyramid_app = HttpsFixup(pyramid_app, settings)
323 pyramid_app = HttpsFixup(pyramid_app, settings)
321
324
322 pyramid_app, _ae_client = wrap_in_appenlight_if_enabled(
325 pyramid_app, _ae_client = wrap_in_appenlight_if_enabled(
323 pyramid_app, settings)
326 pyramid_app, settings)
324 registry.ae_client = _ae_client
327 registry.ae_client = _ae_client
325
328
326 if settings['gzip_responses']:
329 if settings['gzip_responses']:
327 pyramid_app = make_gzip_middleware(
330 pyramid_app = make_gzip_middleware(
328 pyramid_app, settings, compress_level=1)
331 pyramid_app, settings, compress_level=1)
329
332
330 # this should be the outer most middleware in the wsgi stack since
333 # this should be the outer most middleware in the wsgi stack since
331 # middleware like Routes make database calls
334 # middleware like Routes make database calls
332 def pyramid_app_with_cleanup(environ, start_response):
335 def pyramid_app_with_cleanup(environ, start_response):
333 try:
336 try:
334 return pyramid_app(environ, start_response)
337 return pyramid_app(environ, start_response)
335 finally:
338 finally:
336 # Dispose current database session and rollback uncommitted
339 # Dispose current database session and rollback uncommitted
337 # transactions.
340 # transactions.
338 meta.Session.remove()
341 meta.Session.remove()
339
342
340 # In a single threaded mode server, on non sqlite db we should have
343 # In a single threaded mode server, on non sqlite db we should have
341 # '0 Current Checked out connections' at the end of a request,
344 # '0 Current Checked out connections' at the end of a request,
342 # if not, then something, somewhere is leaving a connection open
345 # if not, then something, somewhere is leaving a connection open
343 pool = meta.Base.metadata.bind.engine.pool
346 pool = meta.Base.metadata.bind.engine.pool
344 log.debug('sa pool status: %s', pool.status())
347 log.debug('sa pool status: %s', pool.status())
345 log.debug('Request processing finalized')
348 log.debug('Request processing finalized')
346
349
347 return pyramid_app_with_cleanup
350 return pyramid_app_with_cleanup
348
351
349
352
350 def sanitize_settings_and_apply_defaults(settings):
353 def sanitize_settings_and_apply_defaults(settings):
351 """
354 """
352 Applies settings defaults and does all type conversion.
355 Applies settings defaults and does all type conversion.
353
356
354 We would move all settings parsing and preparation into this place, so that
357 We would move all settings parsing and preparation into this place, so that
355 we have only one place left which deals with this part. The remaining parts
358 we have only one place left which deals with this part. The remaining parts
356 of the application would start to rely fully on well prepared settings.
359 of the application would start to rely fully on well prepared settings.
357
360
358 This piece would later be split up per topic to avoid a big fat monster
361 This piece would later be split up per topic to avoid a big fat monster
359 function.
362 function.
360 """
363 """
361
364
362 settings.setdefault('rhodecode.edition', 'Community Edition')
365 settings.setdefault('rhodecode.edition', 'Community Edition')
363
366
364 if 'mako.default_filters' not in settings:
367 if 'mako.default_filters' not in settings:
365 # set custom default filters if we don't have it defined
368 # set custom default filters if we don't have it defined
366 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
369 settings['mako.imports'] = 'from rhodecode.lib.base import h_filter'
367 settings['mako.default_filters'] = 'h_filter'
370 settings['mako.default_filters'] = 'h_filter'
368
371
369 if 'mako.directories' not in settings:
372 if 'mako.directories' not in settings:
370 mako_directories = settings.setdefault('mako.directories', [
373 mako_directories = settings.setdefault('mako.directories', [
371 # Base templates of the original application
374 # Base templates of the original application
372 'rhodecode:templates',
375 'rhodecode:templates',
373 ])
376 ])
374 log.debug(
377 log.debug(
375 "Using the following Mako template directories: %s",
378 "Using the following Mako template directories: %s",
376 mako_directories)
379 mako_directories)
377
380
378 # Default includes, possible to change as a user
381 # Default includes, possible to change as a user
379 pyramid_includes = settings.setdefault('pyramid.includes', [
382 pyramid_includes = settings.setdefault('pyramid.includes', [
380 'rhodecode.lib.middleware.request_wrapper',
383 'rhodecode.lib.middleware.request_wrapper',
381 ])
384 ])
382 log.debug(
385 log.debug(
383 "Using the following pyramid.includes: %s",
386 "Using the following pyramid.includes: %s",
384 pyramid_includes)
387 pyramid_includes)
385
388
386 # TODO: johbo: Re-think this, usually the call to config.include
389 # TODO: johbo: Re-think this, usually the call to config.include
387 # should allow to pass in a prefix.
390 # should allow to pass in a prefix.
388 settings.setdefault('rhodecode.api.url', '/_admin/api')
391 settings.setdefault('rhodecode.api.url', '/_admin/api')
389
392
390 # Sanitize generic settings.
393 # Sanitize generic settings.
391 _list_setting(settings, 'default_encoding', 'UTF-8')
394 _list_setting(settings, 'default_encoding', 'UTF-8')
392 _bool_setting(settings, 'is_test', 'false')
395 _bool_setting(settings, 'is_test', 'false')
393 _bool_setting(settings, 'gzip_responses', 'false')
396 _bool_setting(settings, 'gzip_responses', 'false')
394
397
395 # Call split out functions that sanitize settings for each topic.
398 # Call split out functions that sanitize settings for each topic.
396 _sanitize_appenlight_settings(settings)
399 _sanitize_appenlight_settings(settings)
397 _sanitize_vcs_settings(settings)
400 _sanitize_vcs_settings(settings)
398 _sanitize_cache_settings(settings)
401 _sanitize_cache_settings(settings)
399
402
400 # configure instance id
403 # configure instance id
401 config_utils.set_instance_id(settings)
404 config_utils.set_instance_id(settings)
402
405
403 return settings
406 return settings
404
407
405
408
406 def _sanitize_appenlight_settings(settings):
409 def _sanitize_appenlight_settings(settings):
407 _bool_setting(settings, 'appenlight', 'false')
410 _bool_setting(settings, 'appenlight', 'false')
408
411
409
412
410 def _sanitize_vcs_settings(settings):
413 def _sanitize_vcs_settings(settings):
411 """
414 """
412 Applies settings defaults and does type conversion for all VCS related
415 Applies settings defaults and does type conversion for all VCS related
413 settings.
416 settings.
414 """
417 """
415 _string_setting(settings, 'vcs.svn.compatible_version', '')
418 _string_setting(settings, 'vcs.svn.compatible_version', '')
416 _string_setting(settings, 'git_rev_filter', '--all')
419 _string_setting(settings, 'git_rev_filter', '--all')
417 _string_setting(settings, 'vcs.hooks.protocol', 'http')
420 _string_setting(settings, 'vcs.hooks.protocol', 'http')
418 _string_setting(settings, 'vcs.hooks.host', '127.0.0.1')
421 _string_setting(settings, 'vcs.hooks.host', '127.0.0.1')
419 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
422 _string_setting(settings, 'vcs.scm_app_implementation', 'http')
420 _string_setting(settings, 'vcs.server', '')
423 _string_setting(settings, 'vcs.server', '')
421 _string_setting(settings, 'vcs.server.log_level', 'debug')
424 _string_setting(settings, 'vcs.server.log_level', 'debug')
422 _string_setting(settings, 'vcs.server.protocol', 'http')
425 _string_setting(settings, 'vcs.server.protocol', 'http')
423 _bool_setting(settings, 'startup.import_repos', 'false')
426 _bool_setting(settings, 'startup.import_repos', 'false')
424 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
427 _bool_setting(settings, 'vcs.hooks.direct_calls', 'false')
425 _bool_setting(settings, 'vcs.server.enable', 'true')
428 _bool_setting(settings, 'vcs.server.enable', 'true')
426 _bool_setting(settings, 'vcs.start_server', 'false')
429 _bool_setting(settings, 'vcs.start_server', 'false')
427 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
430 _list_setting(settings, 'vcs.backends', 'hg, git, svn')
428 _int_setting(settings, 'vcs.connection_timeout', 3600)
431 _int_setting(settings, 'vcs.connection_timeout', 3600)
429
432
430 # Support legacy values of vcs.scm_app_implementation. Legacy
433 # Support legacy values of vcs.scm_app_implementation. Legacy
431 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http', or
434 # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http', or
432 # disabled since 4.13 'vcsserver.scm_app' which is now mapped to 'http'.
435 # disabled since 4.13 'vcsserver.scm_app' which is now mapped to 'http'.
433 scm_app_impl = settings['vcs.scm_app_implementation']
436 scm_app_impl = settings['vcs.scm_app_implementation']
434 if scm_app_impl in ['rhodecode.lib.middleware.utils.scm_app_http', 'vcsserver.scm_app']:
437 if scm_app_impl in ['rhodecode.lib.middleware.utils.scm_app_http', 'vcsserver.scm_app']:
435 settings['vcs.scm_app_implementation'] = 'http'
438 settings['vcs.scm_app_implementation'] = 'http'
436
439
437
440
438 def _sanitize_cache_settings(settings):
441 def _sanitize_cache_settings(settings):
439 temp_store = tempfile.gettempdir()
442 temp_store = tempfile.gettempdir()
440 default_cache_dir = os.path.join(temp_store, 'rc_cache')
443 default_cache_dir = os.path.join(temp_store, 'rc_cache')
441
444
442 # save default, cache dir, and use it for all backends later.
445 # save default, cache dir, and use it for all backends later.
443 default_cache_dir = _string_setting(
446 default_cache_dir = _string_setting(
444 settings,
447 settings,
445 'cache_dir',
448 'cache_dir',
446 default_cache_dir, lower=False, default_when_empty=True)
449 default_cache_dir, lower=False, default_when_empty=True)
447
450
448 # ensure we have our dir created
451 # ensure we have our dir created
449 if not os.path.isdir(default_cache_dir):
452 if not os.path.isdir(default_cache_dir):
450 os.makedirs(default_cache_dir, mode=0755)
453 os.makedirs(default_cache_dir, mode=0755)
451
454
452 # exception store cache
455 # exception store cache
453 _string_setting(
456 _string_setting(
454 settings,
457 settings,
455 'exception_tracker.store_path',
458 'exception_tracker.store_path',
456 temp_store, lower=False, default_when_empty=True)
459 temp_store, lower=False, default_when_empty=True)
457
460
458 # cache_perms
461 # cache_perms
459 _string_setting(
462 _string_setting(
460 settings,
463 settings,
461 'rc_cache.cache_perms.backend',
464 'rc_cache.cache_perms.backend',
462 'dogpile.cache.rc.file_namespace', lower=False)
465 'dogpile.cache.rc.file_namespace', lower=False)
463 _int_setting(
466 _int_setting(
464 settings,
467 settings,
465 'rc_cache.cache_perms.expiration_time',
468 'rc_cache.cache_perms.expiration_time',
466 60)
469 60)
467 _string_setting(
470 _string_setting(
468 settings,
471 settings,
469 'rc_cache.cache_perms.arguments.filename',
472 'rc_cache.cache_perms.arguments.filename',
470 os.path.join(default_cache_dir, 'rc_cache_1'), lower=False)
473 os.path.join(default_cache_dir, 'rc_cache_1'), lower=False)
471
474
472 # cache_repo
475 # cache_repo
473 _string_setting(
476 _string_setting(
474 settings,
477 settings,
475 'rc_cache.cache_repo.backend',
478 'rc_cache.cache_repo.backend',
476 'dogpile.cache.rc.file_namespace', lower=False)
479 'dogpile.cache.rc.file_namespace', lower=False)
477 _int_setting(
480 _int_setting(
478 settings,
481 settings,
479 'rc_cache.cache_repo.expiration_time',
482 'rc_cache.cache_repo.expiration_time',
480 60)
483 60)
481 _string_setting(
484 _string_setting(
482 settings,
485 settings,
483 'rc_cache.cache_repo.arguments.filename',
486 'rc_cache.cache_repo.arguments.filename',
484 os.path.join(default_cache_dir, 'rc_cache_2'), lower=False)
487 os.path.join(default_cache_dir, 'rc_cache_2'), lower=False)
485
488
486 # cache_license
489 # cache_license
487 _string_setting(
490 _string_setting(
488 settings,
491 settings,
489 'rc_cache.cache_license.backend',
492 'rc_cache.cache_license.backend',
490 'dogpile.cache.rc.file_namespace', lower=False)
493 'dogpile.cache.rc.file_namespace', lower=False)
491 _int_setting(
494 _int_setting(
492 settings,
495 settings,
493 'rc_cache.cache_license.expiration_time',
496 'rc_cache.cache_license.expiration_time',
494 5*60)
497 5*60)
495 _string_setting(
498 _string_setting(
496 settings,
499 settings,
497 'rc_cache.cache_license.arguments.filename',
500 'rc_cache.cache_license.arguments.filename',
498 os.path.join(default_cache_dir, 'rc_cache_3'), lower=False)
501 os.path.join(default_cache_dir, 'rc_cache_3'), lower=False)
499
502
500 # cache_repo_longterm memory, 96H
503 # cache_repo_longterm memory, 96H
501 _string_setting(
504 _string_setting(
502 settings,
505 settings,
503 'rc_cache.cache_repo_longterm.backend',
506 'rc_cache.cache_repo_longterm.backend',
504 'dogpile.cache.rc.memory_lru', lower=False)
507 'dogpile.cache.rc.memory_lru', lower=False)
505 _int_setting(
508 _int_setting(
506 settings,
509 settings,
507 'rc_cache.cache_repo_longterm.expiration_time',
510 'rc_cache.cache_repo_longterm.expiration_time',
508 345600)
511 345600)
509 _int_setting(
512 _int_setting(
510 settings,
513 settings,
511 'rc_cache.cache_repo_longterm.max_size',
514 'rc_cache.cache_repo_longterm.max_size',
512 10000)
515 10000)
513
516
514 # sql_cache_short
517 # sql_cache_short
515 _string_setting(
518 _string_setting(
516 settings,
519 settings,
517 'rc_cache.sql_cache_short.backend',
520 'rc_cache.sql_cache_short.backend',
518 'dogpile.cache.rc.memory_lru', lower=False)
521 'dogpile.cache.rc.memory_lru', lower=False)
519 _int_setting(
522 _int_setting(
520 settings,
523 settings,
521 'rc_cache.sql_cache_short.expiration_time',
524 'rc_cache.sql_cache_short.expiration_time',
522 30)
525 30)
523 _int_setting(
526 _int_setting(
524 settings,
527 settings,
525 'rc_cache.sql_cache_short.max_size',
528 'rc_cache.sql_cache_short.max_size',
526 10000)
529 10000)
527
530
528
531
529 def _int_setting(settings, name, default):
532 def _int_setting(settings, name, default):
530 settings[name] = int(settings.get(name, default))
533 settings[name] = int(settings.get(name, default))
531 return settings[name]
534 return settings[name]
532
535
533
536
534 def _bool_setting(settings, name, default):
537 def _bool_setting(settings, name, default):
535 input_val = settings.get(name, default)
538 input_val = settings.get(name, default)
536 if isinstance(input_val, unicode):
539 if isinstance(input_val, unicode):
537 input_val = input_val.encode('utf8')
540 input_val = input_val.encode('utf8')
538 settings[name] = asbool(input_val)
541 settings[name] = asbool(input_val)
539 return settings[name]
542 return settings[name]
540
543
541
544
542 def _list_setting(settings, name, default):
545 def _list_setting(settings, name, default):
543 raw_value = settings.get(name, default)
546 raw_value = settings.get(name, default)
544
547
545 old_separator = ','
548 old_separator = ','
546 if old_separator in raw_value:
549 if old_separator in raw_value:
547 # If we get a comma separated list, pass it to our own function.
550 # If we get a comma separated list, pass it to our own function.
548 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
551 settings[name] = rhodecode_aslist(raw_value, sep=old_separator)
549 else:
552 else:
550 # Otherwise we assume it uses pyramids space/newline separation.
553 # Otherwise we assume it uses pyramids space/newline separation.
551 settings[name] = aslist(raw_value)
554 settings[name] = aslist(raw_value)
552 return settings[name]
555 return settings[name]
553
556
554
557
555 def _string_setting(settings, name, default, lower=True, default_when_empty=False):
558 def _string_setting(settings, name, default, lower=True, default_when_empty=False):
556 value = settings.get(name, default)
559 value = settings.get(name, default)
557
560
558 if default_when_empty and not value:
561 if default_when_empty and not value:
559 # use default value when value is empty
562 # use default value when value is empty
560 value = default
563 value = default
561
564
562 if lower:
565 if lower:
563 value = value.lower()
566 value = value.lower()
564 settings[name] = value
567 settings[name] = value
565 return settings[name]
568 return settings[name]
566
569
567
570
568 def _substitute_values(mapping, substitutions):
571 def _substitute_values(mapping, substitutions):
569
572
570 try:
573 try:
571 result = {
574 result = {
572 # Note: Cannot use regular replacements, since they would clash
575 # Note: Cannot use regular replacements, since they would clash
573 # with the implementation of ConfigParser. Using "format" instead.
576 # with the implementation of ConfigParser. Using "format" instead.
574 key: value.format(**substitutions)
577 key: value.format(**substitutions)
575 for key, value in mapping.items()
578 for key, value in mapping.items()
576 }
579 }
577 except KeyError as e:
580 except KeyError as e:
578 raise ValueError(
581 raise ValueError(
579 'Failed to substitute env variable: {}. '
582 'Failed to substitute env variable: {}. '
580 'Make sure you have specified this env variable without ENV_ prefix'.format(e))
583 'Make sure you have specified this env variable without ENV_ prefix'.format(e))
581 except ValueError as e:
584 except ValueError as e:
582 log.warning('Failed to substitute ENV variable: %s', e)
585 log.warning('Failed to substitute ENV variable: %s', e)
583 result = mapping
586 result = mapping
584
587
585 return result
588 return result
@@ -1,459 +1,459 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2018 RhodeCode GmbH
3 # Copyright (C) 2012-2018 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 deform
21 import deform
22 import logging
22 import logging
23 import peppercorn
23 import peppercorn
24 import webhelpers.paginate
24 import webhelpers.paginate
25
25
26 from pyramid.httpexceptions import HTTPFound, HTTPForbidden, HTTPNotFound
26 from pyramid.httpexceptions import HTTPFound, HTTPForbidden, HTTPNotFound
27
27
28 from rhodecode.integrations import integration_type_registry
28 from rhodecode.apps._base import BaseAppView
29 from rhodecode.apps._base import BaseAppView
29 from rhodecode.integrations import integration_type_registry
30 from rhodecode.apps._base.navigation import navigation_list
30 from rhodecode.apps.admin.navigation import navigation_list
31 from rhodecode.lib.auth import (
31 from rhodecode.lib.auth import (
32 LoginRequired, CSRFRequired, HasPermissionAnyDecorator,
32 LoginRequired, CSRFRequired, HasPermissionAnyDecorator,
33 HasRepoPermissionAnyDecorator, HasRepoGroupPermissionAnyDecorator)
33 HasRepoPermissionAnyDecorator, HasRepoGroupPermissionAnyDecorator)
34 from rhodecode.lib.utils2 import safe_int
34 from rhodecode.lib.utils2 import safe_int
35 from rhodecode.lib import helpers as h
35 from rhodecode.lib import helpers as h
36 from rhodecode.model.db import Repository, RepoGroup, Session, Integration
36 from rhodecode.model.db import Repository, RepoGroup, Session, Integration
37 from rhodecode.model.scm import ScmModel
37 from rhodecode.model.scm import ScmModel
38 from rhodecode.model.integration import IntegrationModel
38 from rhodecode.model.integration import IntegrationModel
39 from rhodecode.model.validation_schema.schemas.integration_schema import (
39 from rhodecode.model.validation_schema.schemas.integration_schema import (
40 make_integration_schema, IntegrationScopeType)
40 make_integration_schema, IntegrationScopeType)
41
41
42 log = logging.getLogger(__name__)
42 log = logging.getLogger(__name__)
43
43
44
44
45 class IntegrationSettingsViewBase(BaseAppView):
45 class IntegrationSettingsViewBase(BaseAppView):
46 """
46 """
47 Base Integration settings view used by both repo / global settings
47 Base Integration settings view used by both repo / global settings
48 """
48 """
49
49
50 def __init__(self, context, request):
50 def __init__(self, context, request):
51 super(IntegrationSettingsViewBase, self).__init__(context, request)
51 super(IntegrationSettingsViewBase, self).__init__(context, request)
52 self._load_view_context()
52 self._load_view_context()
53
53
54 def _load_view_context(self):
54 def _load_view_context(self):
55 """
55 """
56 This avoids boilerplate for repo/global+list/edit+views/templates
56 This avoids boilerplate for repo/global+list/edit+views/templates
57 by doing all possible contexts at the same time however it should
57 by doing all possible contexts at the same time however it should
58 be split up into separate functions once more "contexts" exist
58 be split up into separate functions once more "contexts" exist
59 """
59 """
60
60
61 self.IntegrationType = None
61 self.IntegrationType = None
62 self.repo = None
62 self.repo = None
63 self.repo_group = None
63 self.repo_group = None
64 self.integration = None
64 self.integration = None
65 self.integrations = {}
65 self.integrations = {}
66
66
67 request = self.request
67 request = self.request
68
68
69 if 'repo_name' in request.matchdict: # in repo settings context
69 if 'repo_name' in request.matchdict: # in repo settings context
70 repo_name = request.matchdict['repo_name']
70 repo_name = request.matchdict['repo_name']
71 self.repo = Repository.get_by_repo_name(repo_name)
71 self.repo = Repository.get_by_repo_name(repo_name)
72
72
73 if 'repo_group_name' in request.matchdict: # in group settings context
73 if 'repo_group_name' in request.matchdict: # in group settings context
74 repo_group_name = request.matchdict['repo_group_name']
74 repo_group_name = request.matchdict['repo_group_name']
75 self.repo_group = RepoGroup.get_by_group_name(repo_group_name)
75 self.repo_group = RepoGroup.get_by_group_name(repo_group_name)
76
76
77 if 'integration' in request.matchdict: # integration type context
77 if 'integration' in request.matchdict: # integration type context
78 integration_type = request.matchdict['integration']
78 integration_type = request.matchdict['integration']
79 if integration_type not in integration_type_registry:
79 if integration_type not in integration_type_registry:
80 raise HTTPNotFound()
80 raise HTTPNotFound()
81
81
82 self.IntegrationType = integration_type_registry[integration_type]
82 self.IntegrationType = integration_type_registry[integration_type]
83 if self.IntegrationType.is_dummy:
83 if self.IntegrationType.is_dummy:
84 raise HTTPNotFound()
84 raise HTTPNotFound()
85
85
86 if 'integration_id' in request.matchdict: # single integration context
86 if 'integration_id' in request.matchdict: # single integration context
87 integration_id = request.matchdict['integration_id']
87 integration_id = request.matchdict['integration_id']
88 self.integration = Integration.get(integration_id)
88 self.integration = Integration.get(integration_id)
89
89
90 # extra perms check just in case
90 # extra perms check just in case
91 if not self._has_perms_for_integration(self.integration):
91 if not self._has_perms_for_integration(self.integration):
92 raise HTTPForbidden()
92 raise HTTPForbidden()
93
93
94 self.settings = self.integration and self.integration.settings or {}
94 self.settings = self.integration and self.integration.settings or {}
95 self.admin_view = not (self.repo or self.repo_group)
95 self.admin_view = not (self.repo or self.repo_group)
96
96
97 def _has_perms_for_integration(self, integration):
97 def _has_perms_for_integration(self, integration):
98 perms = self.request.user.permissions
98 perms = self.request.user.permissions
99
99
100 if 'hg.admin' in perms['global']:
100 if 'hg.admin' in perms['global']:
101 return True
101 return True
102
102
103 if integration.repo:
103 if integration.repo:
104 return perms['repositories'].get(
104 return perms['repositories'].get(
105 integration.repo.repo_name) == 'repository.admin'
105 integration.repo.repo_name) == 'repository.admin'
106
106
107 if integration.repo_group:
107 if integration.repo_group:
108 return perms['repositories_groups'].get(
108 return perms['repositories_groups'].get(
109 integration.repo_group.group_name) == 'group.admin'
109 integration.repo_group.group_name) == 'group.admin'
110
110
111 return False
111 return False
112
112
113 def _get_local_tmpl_context(self, include_app_defaults=True):
113 def _get_local_tmpl_context(self, include_app_defaults=True):
114 _ = self.request.translate
114 _ = self.request.translate
115 c = super(IntegrationSettingsViewBase, self)._get_local_tmpl_context(
115 c = super(IntegrationSettingsViewBase, self)._get_local_tmpl_context(
116 include_app_defaults=include_app_defaults)
116 include_app_defaults=include_app_defaults)
117
117
118 c.active = 'integrations'
118 c.active = 'integrations'
119
119
120 return c
120 return c
121
121
122 def _form_schema(self):
122 def _form_schema(self):
123 schema = make_integration_schema(IntegrationType=self.IntegrationType,
123 schema = make_integration_schema(IntegrationType=self.IntegrationType,
124 settings=self.settings)
124 settings=self.settings)
125
125
126 # returns a clone, important if mutating the schema later
126 # returns a clone, important if mutating the schema later
127 return schema.bind(
127 return schema.bind(
128 permissions=self.request.user.permissions,
128 permissions=self.request.user.permissions,
129 no_scope=not self.admin_view)
129 no_scope=not self.admin_view)
130
130
131 def _form_defaults(self):
131 def _form_defaults(self):
132 _ = self.request.translate
132 _ = self.request.translate
133 defaults = {}
133 defaults = {}
134
134
135 if self.integration:
135 if self.integration:
136 defaults['settings'] = self.integration.settings or {}
136 defaults['settings'] = self.integration.settings or {}
137 defaults['options'] = {
137 defaults['options'] = {
138 'name': self.integration.name,
138 'name': self.integration.name,
139 'enabled': self.integration.enabled,
139 'enabled': self.integration.enabled,
140 'scope': {
140 'scope': {
141 'repo': self.integration.repo,
141 'repo': self.integration.repo,
142 'repo_group': self.integration.repo_group,
142 'repo_group': self.integration.repo_group,
143 'child_repos_only': self.integration.child_repos_only,
143 'child_repos_only': self.integration.child_repos_only,
144 },
144 },
145 }
145 }
146 else:
146 else:
147 if self.repo:
147 if self.repo:
148 scope = _('{repo_name} repository').format(
148 scope = _('{repo_name} repository').format(
149 repo_name=self.repo.repo_name)
149 repo_name=self.repo.repo_name)
150 elif self.repo_group:
150 elif self.repo_group:
151 scope = _('{repo_group_name} repo group').format(
151 scope = _('{repo_group_name} repo group').format(
152 repo_group_name=self.repo_group.group_name)
152 repo_group_name=self.repo_group.group_name)
153 else:
153 else:
154 scope = _('Global')
154 scope = _('Global')
155
155
156 defaults['options'] = {
156 defaults['options'] = {
157 'enabled': True,
157 'enabled': True,
158 'name': _('{name} integration').format(
158 'name': _('{name} integration').format(
159 name=self.IntegrationType.display_name),
159 name=self.IntegrationType.display_name),
160 }
160 }
161 defaults['options']['scope'] = {
161 defaults['options']['scope'] = {
162 'repo': self.repo,
162 'repo': self.repo,
163 'repo_group': self.repo_group,
163 'repo_group': self.repo_group,
164 }
164 }
165
165
166 return defaults
166 return defaults
167
167
168 def _delete_integration(self, integration):
168 def _delete_integration(self, integration):
169 _ = self.request.translate
169 _ = self.request.translate
170 Session().delete(integration)
170 Session().delete(integration)
171 Session().commit()
171 Session().commit()
172 h.flash(
172 h.flash(
173 _('Integration {integration_name} deleted successfully.').format(
173 _('Integration {integration_name} deleted successfully.').format(
174 integration_name=integration.name),
174 integration_name=integration.name),
175 category='success')
175 category='success')
176
176
177 if self.repo:
177 if self.repo:
178 redirect_to = self.request.route_path(
178 redirect_to = self.request.route_path(
179 'repo_integrations_home', repo_name=self.repo.repo_name)
179 'repo_integrations_home', repo_name=self.repo.repo_name)
180 elif self.repo_group:
180 elif self.repo_group:
181 redirect_to = self.request.route_path(
181 redirect_to = self.request.route_path(
182 'repo_group_integrations_home',
182 'repo_group_integrations_home',
183 repo_group_name=self.repo_group.group_name)
183 repo_group_name=self.repo_group.group_name)
184 else:
184 else:
185 redirect_to = self.request.route_path('global_integrations_home')
185 redirect_to = self.request.route_path('global_integrations_home')
186 raise HTTPFound(redirect_to)
186 raise HTTPFound(redirect_to)
187
187
188 def _integration_list(self):
188 def _integration_list(self):
189 """ List integrations """
189 """ List integrations """
190
190
191 c = self.load_default_context()
191 c = self.load_default_context()
192 if self.repo:
192 if self.repo:
193 scope = self.repo
193 scope = self.repo
194 elif self.repo_group:
194 elif self.repo_group:
195 scope = self.repo_group
195 scope = self.repo_group
196 else:
196 else:
197 scope = 'all'
197 scope = 'all'
198
198
199 integrations = []
199 integrations = []
200
200
201 for IntType, integration in IntegrationModel().get_integrations(
201 for IntType, integration in IntegrationModel().get_integrations(
202 scope=scope, IntegrationType=self.IntegrationType):
202 scope=scope, IntegrationType=self.IntegrationType):
203
203
204 # extra permissions check *just in case*
204 # extra permissions check *just in case*
205 if not self._has_perms_for_integration(integration):
205 if not self._has_perms_for_integration(integration):
206 continue
206 continue
207
207
208 integrations.append((IntType, integration))
208 integrations.append((IntType, integration))
209
209
210 sort_arg = self.request.GET.get('sort', 'name:asc')
210 sort_arg = self.request.GET.get('sort', 'name:asc')
211 sort_dir = 'asc'
211 sort_dir = 'asc'
212 if ':' in sort_arg:
212 if ':' in sort_arg:
213 sort_field, sort_dir = sort_arg.split(':')
213 sort_field, sort_dir = sort_arg.split(':')
214 else:
214 else:
215 sort_field = sort_arg, 'asc'
215 sort_field = sort_arg, 'asc'
216
216
217 assert sort_field in ('name', 'integration_type', 'enabled', 'scope')
217 assert sort_field in ('name', 'integration_type', 'enabled', 'scope')
218
218
219 integrations.sort(
219 integrations.sort(
220 key=lambda x: getattr(x[1], sort_field),
220 key=lambda x: getattr(x[1], sort_field),
221 reverse=(sort_dir == 'desc'))
221 reverse=(sort_dir == 'desc'))
222
222
223 page_url = webhelpers.paginate.PageURL(
223 page_url = webhelpers.paginate.PageURL(
224 self.request.path, self.request.GET)
224 self.request.path, self.request.GET)
225 page = safe_int(self.request.GET.get('page', 1), 1)
225 page = safe_int(self.request.GET.get('page', 1), 1)
226
226
227 integrations = h.Page(
227 integrations = h.Page(
228 integrations, page=page, items_per_page=10, url=page_url)
228 integrations, page=page, items_per_page=10, url=page_url)
229
229
230 c.rev_sort_dir = sort_dir != 'desc' and 'desc' or 'asc'
230 c.rev_sort_dir = sort_dir != 'desc' and 'desc' or 'asc'
231
231
232 c.current_IntegrationType = self.IntegrationType
232 c.current_IntegrationType = self.IntegrationType
233 c.integrations_list = integrations
233 c.integrations_list = integrations
234 c.available_integrations = integration_type_registry
234 c.available_integrations = integration_type_registry
235
235
236 return self._get_template_context(c)
236 return self._get_template_context(c)
237
237
238 def _settings_get(self, defaults=None, form=None):
238 def _settings_get(self, defaults=None, form=None):
239 """
239 """
240 View that displays the integration settings as a form.
240 View that displays the integration settings as a form.
241 """
241 """
242 c = self.load_default_context()
242 c = self.load_default_context()
243
243
244 defaults = defaults or self._form_defaults()
244 defaults = defaults or self._form_defaults()
245 schema = self._form_schema()
245 schema = self._form_schema()
246
246
247 if self.integration:
247 if self.integration:
248 buttons = ('submit', 'delete')
248 buttons = ('submit', 'delete')
249 else:
249 else:
250 buttons = ('submit',)
250 buttons = ('submit',)
251
251
252 form = form or deform.Form(schema, appstruct=defaults, buttons=buttons)
252 form = form or deform.Form(schema, appstruct=defaults, buttons=buttons)
253
253
254 c.form = form
254 c.form = form
255 c.current_IntegrationType = self.IntegrationType
255 c.current_IntegrationType = self.IntegrationType
256 c.integration = self.integration
256 c.integration = self.integration
257
257
258 return self._get_template_context(c)
258 return self._get_template_context(c)
259
259
260 def _settings_post(self):
260 def _settings_post(self):
261 """
261 """
262 View that validates and stores the integration settings.
262 View that validates and stores the integration settings.
263 """
263 """
264 _ = self.request.translate
264 _ = self.request.translate
265
265
266 controls = self.request.POST.items()
266 controls = self.request.POST.items()
267 pstruct = peppercorn.parse(controls)
267 pstruct = peppercorn.parse(controls)
268
268
269 if self.integration and pstruct.get('delete'):
269 if self.integration and pstruct.get('delete'):
270 return self._delete_integration(self.integration)
270 return self._delete_integration(self.integration)
271
271
272 schema = self._form_schema()
272 schema = self._form_schema()
273
273
274 skip_settings_validation = False
274 skip_settings_validation = False
275 if self.integration and 'enabled' not in pstruct.get('options', {}):
275 if self.integration and 'enabled' not in pstruct.get('options', {}):
276 skip_settings_validation = True
276 skip_settings_validation = True
277 schema['settings'].validator = None
277 schema['settings'].validator = None
278 for field in schema['settings'].children:
278 for field in schema['settings'].children:
279 field.validator = None
279 field.validator = None
280 field.missing = ''
280 field.missing = ''
281
281
282 if self.integration:
282 if self.integration:
283 buttons = ('submit', 'delete')
283 buttons = ('submit', 'delete')
284 else:
284 else:
285 buttons = ('submit',)
285 buttons = ('submit',)
286
286
287 form = deform.Form(schema, buttons=buttons)
287 form = deform.Form(schema, buttons=buttons)
288
288
289 if not self.admin_view:
289 if not self.admin_view:
290 # scope is read only field in these cases, and has to be added
290 # scope is read only field in these cases, and has to be added
291 options = pstruct.setdefault('options', {})
291 options = pstruct.setdefault('options', {})
292 if 'scope' not in options:
292 if 'scope' not in options:
293 options['scope'] = IntegrationScopeType().serialize(None, {
293 options['scope'] = IntegrationScopeType().serialize(None, {
294 'repo': self.repo,
294 'repo': self.repo,
295 'repo_group': self.repo_group,
295 'repo_group': self.repo_group,
296 })
296 })
297
297
298 try:
298 try:
299 valid_data = form.validate_pstruct(pstruct)
299 valid_data = form.validate_pstruct(pstruct)
300 except deform.ValidationFailure as e:
300 except deform.ValidationFailure as e:
301 h.flash(
301 h.flash(
302 _('Errors exist when saving integration settings. '
302 _('Errors exist when saving integration settings. '
303 'Please check the form inputs.'),
303 'Please check the form inputs.'),
304 category='error')
304 category='error')
305 return self._settings_get(form=e)
305 return self._settings_get(form=e)
306
306
307 if not self.integration:
307 if not self.integration:
308 self.integration = Integration()
308 self.integration = Integration()
309 self.integration.integration_type = self.IntegrationType.key
309 self.integration.integration_type = self.IntegrationType.key
310 Session().add(self.integration)
310 Session().add(self.integration)
311
311
312 scope = valid_data['options']['scope']
312 scope = valid_data['options']['scope']
313
313
314 IntegrationModel().update_integration(self.integration,
314 IntegrationModel().update_integration(self.integration,
315 name=valid_data['options']['name'],
315 name=valid_data['options']['name'],
316 enabled=valid_data['options']['enabled'],
316 enabled=valid_data['options']['enabled'],
317 settings=valid_data['settings'],
317 settings=valid_data['settings'],
318 repo=scope['repo'],
318 repo=scope['repo'],
319 repo_group=scope['repo_group'],
319 repo_group=scope['repo_group'],
320 child_repos_only=scope['child_repos_only'],
320 child_repos_only=scope['child_repos_only'],
321 )
321 )
322
322
323 self.integration.settings = valid_data['settings']
323 self.integration.settings = valid_data['settings']
324 Session().commit()
324 Session().commit()
325 # Display success message and redirect.
325 # Display success message and redirect.
326 h.flash(
326 h.flash(
327 _('Integration {integration_name} updated successfully.').format(
327 _('Integration {integration_name} updated successfully.').format(
328 integration_name=self.IntegrationType.display_name),
328 integration_name=self.IntegrationType.display_name),
329 category='success')
329 category='success')
330
330
331 # if integration scope changes, we must redirect to the right place
331 # if integration scope changes, we must redirect to the right place
332 # keeping in mind if the original view was for /repo/ or /_admin/
332 # keeping in mind if the original view was for /repo/ or /_admin/
333 admin_view = not (self.repo or self.repo_group)
333 admin_view = not (self.repo or self.repo_group)
334
334
335 if self.integration.repo and not admin_view:
335 if self.integration.repo and not admin_view:
336 redirect_to = self.request.route_path(
336 redirect_to = self.request.route_path(
337 'repo_integrations_edit',
337 'repo_integrations_edit',
338 repo_name=self.integration.repo.repo_name,
338 repo_name=self.integration.repo.repo_name,
339 integration=self.integration.integration_type,
339 integration=self.integration.integration_type,
340 integration_id=self.integration.integration_id)
340 integration_id=self.integration.integration_id)
341 elif self.integration.repo_group and not admin_view:
341 elif self.integration.repo_group and not admin_view:
342 redirect_to = self.request.route_path(
342 redirect_to = self.request.route_path(
343 'repo_group_integrations_edit',
343 'repo_group_integrations_edit',
344 repo_group_name=self.integration.repo_group.group_name,
344 repo_group_name=self.integration.repo_group.group_name,
345 integration=self.integration.integration_type,
345 integration=self.integration.integration_type,
346 integration_id=self.integration.integration_id)
346 integration_id=self.integration.integration_id)
347 else:
347 else:
348 redirect_to = self.request.route_path(
348 redirect_to = self.request.route_path(
349 'global_integrations_edit',
349 'global_integrations_edit',
350 integration=self.integration.integration_type,
350 integration=self.integration.integration_type,
351 integration_id=self.integration.integration_id)
351 integration_id=self.integration.integration_id)
352
352
353 return HTTPFound(redirect_to)
353 return HTTPFound(redirect_to)
354
354
355 def _new_integration(self):
355 def _new_integration(self):
356 c = self.load_default_context()
356 c = self.load_default_context()
357 c.available_integrations = integration_type_registry
357 c.available_integrations = integration_type_registry
358 return self._get_template_context(c)
358 return self._get_template_context(c)
359
359
360 def load_default_context(self):
360 def load_default_context(self):
361 raise NotImplementedError()
361 raise NotImplementedError()
362
362
363
363
364 class GlobalIntegrationsView(IntegrationSettingsViewBase):
364 class GlobalIntegrationsView(IntegrationSettingsViewBase):
365 def load_default_context(self):
365 def load_default_context(self):
366 c = self._get_local_tmpl_context()
366 c = self._get_local_tmpl_context()
367 c.repo = self.repo
367 c.repo = self.repo
368 c.repo_group = self.repo_group
368 c.repo_group = self.repo_group
369 c.navlist = navigation_list(self.request)
369 c.navlist = navigation_list(self.request)
370
370
371 return c
371 return c
372
372
373 @LoginRequired()
373 @LoginRequired()
374 @HasPermissionAnyDecorator('hg.admin')
374 @HasPermissionAnyDecorator('hg.admin')
375 def integration_list(self):
375 def integration_list(self):
376 return self._integration_list()
376 return self._integration_list()
377
377
378 @LoginRequired()
378 @LoginRequired()
379 @HasPermissionAnyDecorator('hg.admin')
379 @HasPermissionAnyDecorator('hg.admin')
380 def settings_get(self):
380 def settings_get(self):
381 return self._settings_get()
381 return self._settings_get()
382
382
383 @LoginRequired()
383 @LoginRequired()
384 @HasPermissionAnyDecorator('hg.admin')
384 @HasPermissionAnyDecorator('hg.admin')
385 @CSRFRequired()
385 @CSRFRequired()
386 def settings_post(self):
386 def settings_post(self):
387 return self._settings_post()
387 return self._settings_post()
388
388
389 @LoginRequired()
389 @LoginRequired()
390 @HasPermissionAnyDecorator('hg.admin')
390 @HasPermissionAnyDecorator('hg.admin')
391 def new_integration(self):
391 def new_integration(self):
392 return self._new_integration()
392 return self._new_integration()
393
393
394
394
395 class RepoIntegrationsView(IntegrationSettingsViewBase):
395 class RepoIntegrationsView(IntegrationSettingsViewBase):
396 def load_default_context(self):
396 def load_default_context(self):
397 c = self._get_local_tmpl_context()
397 c = self._get_local_tmpl_context()
398
398
399 c.repo = self.repo
399 c.repo = self.repo
400 c.repo_group = self.repo_group
400 c.repo_group = self.repo_group
401
401
402 self.db_repo = self.repo
402 self.db_repo = self.repo
403 c.rhodecode_db_repo = self.repo
403 c.rhodecode_db_repo = self.repo
404 c.repo_name = self.db_repo.repo_name
404 c.repo_name = self.db_repo.repo_name
405 c.repository_pull_requests = ScmModel().get_pull_requests(self.repo)
405 c.repository_pull_requests = ScmModel().get_pull_requests(self.repo)
406
406
407 return c
407 return c
408
408
409 @LoginRequired()
409 @LoginRequired()
410 @HasRepoPermissionAnyDecorator('repository.admin')
410 @HasRepoPermissionAnyDecorator('repository.admin')
411 def integration_list(self):
411 def integration_list(self):
412 return self._integration_list()
412 return self._integration_list()
413
413
414 @LoginRequired()
414 @LoginRequired()
415 @HasRepoPermissionAnyDecorator('repository.admin')
415 @HasRepoPermissionAnyDecorator('repository.admin')
416 def settings_get(self):
416 def settings_get(self):
417 return self._settings_get()
417 return self._settings_get()
418
418
419 @LoginRequired()
419 @LoginRequired()
420 @HasRepoPermissionAnyDecorator('repository.admin')
420 @HasRepoPermissionAnyDecorator('repository.admin')
421 @CSRFRequired()
421 @CSRFRequired()
422 def settings_post(self):
422 def settings_post(self):
423 return self._settings_post()
423 return self._settings_post()
424
424
425 @LoginRequired()
425 @LoginRequired()
426 @HasRepoPermissionAnyDecorator('repository.admin')
426 @HasRepoPermissionAnyDecorator('repository.admin')
427 def new_integration(self):
427 def new_integration(self):
428 return self._new_integration()
428 return self._new_integration()
429
429
430
430
431 class RepoGroupIntegrationsView(IntegrationSettingsViewBase):
431 class RepoGroupIntegrationsView(IntegrationSettingsViewBase):
432 def load_default_context(self):
432 def load_default_context(self):
433 c = self._get_local_tmpl_context()
433 c = self._get_local_tmpl_context()
434 c.repo = self.repo
434 c.repo = self.repo
435 c.repo_group = self.repo_group
435 c.repo_group = self.repo_group
436 c.navlist = navigation_list(self.request)
436 c.navlist = navigation_list(self.request)
437
437
438 return c
438 return c
439
439
440 @LoginRequired()
440 @LoginRequired()
441 @HasRepoGroupPermissionAnyDecorator('group.admin')
441 @HasRepoGroupPermissionAnyDecorator('group.admin')
442 def integration_list(self):
442 def integration_list(self):
443 return self._integration_list()
443 return self._integration_list()
444
444
445 @LoginRequired()
445 @LoginRequired()
446 @HasRepoGroupPermissionAnyDecorator('group.admin')
446 @HasRepoGroupPermissionAnyDecorator('group.admin')
447 def settings_get(self):
447 def settings_get(self):
448 return self._settings_get()
448 return self._settings_get()
449
449
450 @LoginRequired()
450 @LoginRequired()
451 @HasRepoGroupPermissionAnyDecorator('group.admin')
451 @HasRepoGroupPermissionAnyDecorator('group.admin')
452 @CSRFRequired()
452 @CSRFRequired()
453 def settings_post(self):
453 def settings_post(self):
454 return self._settings_post()
454 return self._settings_post()
455
455
456 @LoginRequired()
456 @LoginRequired()
457 @HasRepoGroupPermissionAnyDecorator('group.admin')
457 @HasRepoGroupPermissionAnyDecorator('group.admin')
458 def new_integration(self):
458 def new_integration(self):
459 return self._new_integration()
459 return self._new_integration()
General Comments 0
You need to be logged in to leave comments. Login now