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