##// END OF EJS Templates
settings: use invalidate after updating settings.
marcink -
r276:7f18164f default
parent child Browse files
Show More
@@ -1,852 +1,854 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21
22 22 """
23 23 settings controller for rhodecode admin
24 24 """
25 25
26 26 import collections
27 27 import logging
28 28 import urllib2
29 29
30 30 import datetime
31 31 import formencode
32 32 from formencode import htmlfill
33 33 import packaging.version
34 34 from pylons import request, tmpl_context as c, url, config
35 35 from pylons.controllers.util import redirect
36 36 from pylons.i18n.translation import _, lazy_ugettext
37 37 from webob.exc import HTTPBadRequest
38 38
39 39 import rhodecode
40 40 from rhodecode.lib import auth
41 41 from rhodecode.lib import helpers as h
42 42 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
43 43 from rhodecode.lib.base import BaseController, render
44 44 from rhodecode.lib.celerylib import tasks, run_task
45 45 from rhodecode.lib.utils import repo2db_mapper
46 46 from rhodecode.lib.utils2 import (
47 47 str2bool, safe_unicode, AttributeDict, safe_int)
48 48 from rhodecode.lib.compat import OrderedDict
49 49 from rhodecode.lib.ext_json import json
50 50 from rhodecode.lib.utils import jsonify
51 51
52 52 from rhodecode.model.db import RhodeCodeUi, Repository
53 53 from rhodecode.model.forms import ApplicationSettingsForm, \
54 54 ApplicationUiSettingsForm, ApplicationVisualisationForm, \
55 55 LabsSettingsForm, IssueTrackerPatternsForm
56 56
57 57 from rhodecode.model.scm import ScmModel
58 58 from rhodecode.model.notification import EmailNotificationModel
59 59 from rhodecode.model.meta import Session
60 60 from rhodecode.model.settings import (
61 61 IssueTrackerSettingsModel, VcsSettingsModel, SettingNotFound,
62 62 SettingsModel)
63 63 from rhodecode.model.supervisor import SupervisorModel, SUPERVISOR_MASTER
64 64
65 65
66 66 log = logging.getLogger(__name__)
67 67
68 68
69 69 class SettingsController(BaseController):
70 70 """REST Controller styled on the Atom Publishing Protocol"""
71 71 # To properly map this controller, ensure your config/routing.py
72 72 # file has a resource setup:
73 73 # map.resource('setting', 'settings', controller='admin/settings',
74 74 # path_prefix='/admin', name_prefix='admin_')
75 75
76 76 @LoginRequired()
77 77 def __before__(self):
78 78 super(SettingsController, self).__before__()
79 79 c.labs_active = str2bool(
80 80 rhodecode.CONFIG.get('labs_settings_active', 'false'))
81 81 c.navlist = navigation.get_navlist(request)
82 82
83 83 def _get_hg_ui_settings(self):
84 84 ret = RhodeCodeUi.query().all()
85 85
86 86 if not ret:
87 87 raise Exception('Could not get application ui settings !')
88 88 settings = {}
89 89 for each in ret:
90 90 k = each.ui_key
91 91 v = each.ui_value
92 92 if k == '/':
93 93 k = 'root_path'
94 94
95 95 if k in ['push_ssl', 'publish']:
96 96 v = str2bool(v)
97 97
98 98 if k.find('.') != -1:
99 99 k = k.replace('.', '_')
100 100
101 101 if each.ui_section in ['hooks', 'extensions']:
102 102 v = each.ui_active
103 103
104 104 settings[each.ui_section + '_' + k] = v
105 105 return settings
106 106
107 107 @HasPermissionAllDecorator('hg.admin')
108 108 @auth.CSRFRequired()
109 109 @jsonify
110 110 def delete_svn_pattern(self):
111 111 if not request.is_xhr:
112 112 raise HTTPBadRequest()
113 113
114 114 delete_pattern_id = request.POST.get('delete_svn_pattern')
115 115 model = VcsSettingsModel()
116 116 try:
117 117 model.delete_global_svn_pattern(delete_pattern_id)
118 118 except SettingNotFound:
119 119 raise HTTPBadRequest()
120 120
121 121 Session().commit()
122 122 return True
123 123
124 124 @HasPermissionAllDecorator('hg.admin')
125 125 @auth.CSRFRequired()
126 126 def settings_vcs_update(self):
127 127 """POST /admin/settings: All items in the collection"""
128 128 # url('admin_settings_vcs')
129 129 c.active = 'vcs'
130 130
131 131 model = VcsSettingsModel()
132 132 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
133 133 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
134 134
135 135 application_form = ApplicationUiSettingsForm()()
136 136 try:
137 137 form_result = application_form.to_python(dict(request.POST))
138 138 except formencode.Invalid as errors:
139 139 h.flash(
140 140 _("Some form inputs contain invalid data."),
141 141 category='error')
142 142 return htmlfill.render(
143 143 render('admin/settings/settings.html'),
144 144 defaults=errors.value,
145 145 errors=errors.error_dict or {},
146 146 prefix_error=False,
147 147 encoding="UTF-8",
148 148 force_defaults=False
149 149 )
150 150
151 151 try:
152 152 model.update_global_ssl_setting(form_result['web_push_ssl'])
153 153 if c.visual.allow_repo_location_change:
154 154 model.update_global_path_setting(
155 155 form_result['paths_root_path'])
156 156 model.update_global_hook_settings(form_result)
157 157 model.create_global_svn_settings(form_result)
158 158 model.create_or_update_global_hg_settings(form_result)
159 159 model.create_or_update_global_pr_settings(form_result)
160 160 except Exception:
161 161 log.exception("Exception while updating settings")
162 162 h.flash(_('Error occurred during updating '
163 163 'application settings'), category='error')
164 164 else:
165 165 Session().commit()
166 166 h.flash(_('Updated VCS settings'), category='success')
167 167 return redirect(url('admin_settings_vcs'))
168 168
169 169 return htmlfill.render(
170 170 render('admin/settings/settings.html'),
171 171 defaults=self._form_defaults(),
172 172 encoding="UTF-8",
173 173 force_defaults=False)
174 174
175 175 @HasPermissionAllDecorator('hg.admin')
176 176 def settings_vcs(self):
177 177 """GET /admin/settings: All items in the collection"""
178 178 # url('admin_settings_vcs')
179 179 c.active = 'vcs'
180 180 model = VcsSettingsModel()
181 181 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
182 182 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
183 183
184 184 return htmlfill.render(
185 185 render('admin/settings/settings.html'),
186 186 defaults=self._form_defaults(),
187 187 encoding="UTF-8",
188 188 force_defaults=False)
189 189
190 190 @HasPermissionAllDecorator('hg.admin')
191 191 @auth.CSRFRequired()
192 192 def settings_mapping_update(self):
193 193 """POST /admin/settings/mapping: All items in the collection"""
194 194 # url('admin_settings_mapping')
195 195 c.active = 'mapping'
196 196 rm_obsolete = request.POST.get('destroy', False)
197 197 invalidate_cache = request.POST.get('invalidate', False)
198 198 log.debug(
199 199 'rescanning repo location with destroy obsolete=%s', rm_obsolete)
200 200
201 201 if invalidate_cache:
202 202 log.debug('invalidating all repositories cache')
203 203 for repo in Repository.get_all():
204 204 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
205 205
206 206 filesystem_repos = ScmModel().repo_scan()
207 207 added, removed = repo2db_mapper(filesystem_repos, rm_obsolete)
208 208 _repr = lambda l: ', '.join(map(safe_unicode, l)) or '-'
209 209 h.flash(_('Repositories successfully '
210 210 'rescanned added: %s ; removed: %s') %
211 211 (_repr(added), _repr(removed)),
212 212 category='success')
213 213 return redirect(url('admin_settings_mapping'))
214 214
215 215 @HasPermissionAllDecorator('hg.admin')
216 216 def settings_mapping(self):
217 217 """GET /admin/settings/mapping: All items in the collection"""
218 218 # url('admin_settings_mapping')
219 219 c.active = 'mapping'
220 220
221 221 return htmlfill.render(
222 222 render('admin/settings/settings.html'),
223 223 defaults=self._form_defaults(),
224 224 encoding="UTF-8",
225 225 force_defaults=False)
226 226
227 227 @HasPermissionAllDecorator('hg.admin')
228 228 @auth.CSRFRequired()
229 229 def settings_global_update(self):
230 230 """POST /admin/settings/global: All items in the collection"""
231 231 # url('admin_settings_global')
232 232 c.active = 'global'
233 233 application_form = ApplicationSettingsForm()()
234 234 try:
235 235 form_result = application_form.to_python(dict(request.POST))
236 236 except formencode.Invalid as errors:
237 237 return htmlfill.render(
238 238 render('admin/settings/settings.html'),
239 239 defaults=errors.value,
240 240 errors=errors.error_dict or {},
241 241 prefix_error=False,
242 242 encoding="UTF-8",
243 243 force_defaults=False)
244 244
245 245 try:
246 246 settings = [
247 247 ('title', 'rhodecode_title'),
248 248 ('realm', 'rhodecode_realm'),
249 249 ('pre_code', 'rhodecode_pre_code'),
250 250 ('post_code', 'rhodecode_post_code'),
251 251 ('captcha_public_key', 'rhodecode_captcha_public_key'),
252 252 ('captcha_private_key', 'rhodecode_captcha_private_key'),
253 253 ]
254 254 for setting, form_key in settings:
255 255 sett = SettingsModel().create_or_update_setting(
256 256 setting, form_result[form_key])
257 257 Session().add(sett)
258 258
259 259 Session().commit()
260 SettingsModel().invalidate_settings_cache()
260 261 h.flash(_('Updated application settings'), category='success')
261
262 262 except Exception:
263 263 log.exception("Exception while updating application settings")
264 264 h.flash(
265 265 _('Error occurred during updating application settings'),
266 266 category='error')
267 267
268 268 return redirect(url('admin_settings_global'))
269 269
270 270 @HasPermissionAllDecorator('hg.admin')
271 271 def settings_global(self):
272 272 """GET /admin/settings/global: All items in the collection"""
273 273 # url('admin_settings_global')
274 274 c.active = 'global'
275 275
276 276 return htmlfill.render(
277 277 render('admin/settings/settings.html'),
278 278 defaults=self._form_defaults(),
279 279 encoding="UTF-8",
280 280 force_defaults=False)
281 281
282 282 @HasPermissionAllDecorator('hg.admin')
283 283 @auth.CSRFRequired()
284 284 def settings_visual_update(self):
285 285 """POST /admin/settings/visual: All items in the collection"""
286 286 # url('admin_settings_visual')
287 287 c.active = 'visual'
288 288 application_form = ApplicationVisualisationForm()()
289 289 try:
290 290 form_result = application_form.to_python(dict(request.POST))
291 291 except formencode.Invalid as errors:
292 292 return htmlfill.render(
293 293 render('admin/settings/settings.html'),
294 294 defaults=errors.value,
295 295 errors=errors.error_dict or {},
296 296 prefix_error=False,
297 297 encoding="UTF-8",
298 298 force_defaults=False
299 299 )
300 300
301 301 try:
302 302 settings = [
303 303 ('show_public_icon', 'rhodecode_show_public_icon', 'bool'),
304 304 ('show_private_icon', 'rhodecode_show_private_icon', 'bool'),
305 305 ('stylify_metatags', 'rhodecode_stylify_metatags', 'bool'),
306 306 ('repository_fields', 'rhodecode_repository_fields', 'bool'),
307 307 ('dashboard_items', 'rhodecode_dashboard_items', 'int'),
308 308 ('admin_grid_items', 'rhodecode_admin_grid_items', 'int'),
309 309 ('show_version', 'rhodecode_show_version', 'bool'),
310 310 ('use_gravatar', 'rhodecode_use_gravatar', 'bool'),
311 311 ('markup_renderer', 'rhodecode_markup_renderer', 'unicode'),
312 312 ('gravatar_url', 'rhodecode_gravatar_url', 'unicode'),
313 313 ('clone_uri_tmpl', 'rhodecode_clone_uri_tmpl', 'unicode'),
314 314 ('support_url', 'rhodecode_support_url', 'unicode'),
315 315 ('show_revision_number', 'rhodecode_show_revision_number', 'bool'),
316 316 ('show_sha_length', 'rhodecode_show_sha_length', 'int'),
317 317 ]
318 318 for setting, form_key, type_ in settings:
319 319 sett = SettingsModel().create_or_update_setting(
320 320 setting, form_result[form_key], type_)
321 321 Session().add(sett)
322 322
323 323 Session().commit()
324
324 SettingsModel().invalidate_settings_cache()
325 325 h.flash(_('Updated visualisation settings'), category='success')
326 326 except Exception:
327 327 log.exception("Exception updating visualization settings")
328 328 h.flash(_('Error occurred during updating '
329 329 'visualisation settings'),
330 330 category='error')
331 331
332 332 return redirect(url('admin_settings_visual'))
333 333
334 334 @HasPermissionAllDecorator('hg.admin')
335 335 def settings_visual(self):
336 336 """GET /admin/settings/visual: All items in the collection"""
337 337 # url('admin_settings_visual')
338 338 c.active = 'visual'
339 339
340 340 return htmlfill.render(
341 341 render('admin/settings/settings.html'),
342 342 defaults=self._form_defaults(),
343 343 encoding="UTF-8",
344 344 force_defaults=False)
345 345
346 346 @HasPermissionAllDecorator('hg.admin')
347 347 @auth.CSRFRequired()
348 348 def settings_issuetracker_test(self):
349 349 if request.is_xhr:
350 350 return h.urlify_commit_message(
351 351 request.POST.get('test_text', ''),
352 352 'repo_group/test_repo1')
353 353 else:
354 354 raise HTTPBadRequest()
355 355
356 356 @HasPermissionAllDecorator('hg.admin')
357 357 @auth.CSRFRequired()
358 358 def settings_issuetracker_delete(self):
359 359 uid = request.POST.get('uid')
360 360 IssueTrackerSettingsModel().delete_entries(uid)
361 361 h.flash(_('Removed issue tracker entry'), category='success')
362 362 return redirect(url('admin_settings_issuetracker'))
363 363
364 364 @HasPermissionAllDecorator('hg.admin')
365 365 def settings_issuetracker(self):
366 366 """GET /admin/settings/issue-tracker: All items in the collection"""
367 367 # url('admin_settings_issuetracker')
368 368 c.active = 'issuetracker'
369 369 defaults = SettingsModel().get_all_settings()
370 370
371 371 entry_key = 'rhodecode_issuetracker_pat_'
372 372
373 373 c.issuetracker_entries = {}
374 374 for k, v in defaults.items():
375 375 if k.startswith(entry_key):
376 376 uid = k[len(entry_key):]
377 377 c.issuetracker_entries[uid] = None
378 378
379 379 for uid in c.issuetracker_entries:
380 380 c.issuetracker_entries[uid] = AttributeDict({
381 381 'pat': defaults.get('rhodecode_issuetracker_pat_' + uid),
382 382 'url': defaults.get('rhodecode_issuetracker_url_' + uid),
383 383 'pref': defaults.get('rhodecode_issuetracker_pref_' + uid),
384 384 'desc': defaults.get('rhodecode_issuetracker_desc_' + uid),
385 385 })
386 386
387 387 return render('admin/settings/settings.html')
388 388
389 389 @HasPermissionAllDecorator('hg.admin')
390 390 @auth.CSRFRequired()
391 391 def settings_issuetracker_save(self):
392 392 settings_model = IssueTrackerSettingsModel()
393 393
394 394 form = IssueTrackerPatternsForm()().to_python(request.POST)
395 395 for uid in form['delete_patterns']:
396 396 settings_model.delete_entries(uid)
397 397
398 398 for pattern in form['patterns']:
399 399 for setting, value, type_ in pattern:
400 400 sett = settings_model.create_or_update_setting(
401 401 setting, value, type_)
402 402 Session().add(sett)
403 403
404 404 Session().commit()
405 405
406 SettingsModel().invalidate_settings_cache()
406 407 h.flash(_('Updated issue tracker entries'), category='success')
407 408 return redirect(url('admin_settings_issuetracker'))
408 409
409 410 @HasPermissionAllDecorator('hg.admin')
410 411 @auth.CSRFRequired()
411 412 def settings_email_update(self):
412 413 """POST /admin/settings/email: All items in the collection"""
413 414 # url('admin_settings_email')
414 415 c.active = 'email'
415 416
416 417 test_email = request.POST.get('test_email')
417 418
418 419 if not test_email:
419 420 h.flash(_('Please enter email address'), category='error')
420 421 return redirect(url('admin_settings_email'))
421 422
422 423 email_kwargs = {
423 424 'date': datetime.datetime.now(),
424 425 'user': c.rhodecode_user,
425 426 'rhodecode_version': c.rhodecode_version
426 427 }
427 428
428 429 (subject, headers, email_body,
429 430 email_body_plaintext) = EmailNotificationModel().render_email(
430 431 EmailNotificationModel.TYPE_EMAIL_TEST, **email_kwargs)
431 432
432 433 recipients = [test_email] if test_email else None
433 434
434 435 run_task(tasks.send_email, recipients, subject,
435 436 email_body_plaintext, email_body)
436 437
437 438 h.flash(_('Send email task created'), category='success')
438 439 return redirect(url('admin_settings_email'))
439 440
440 441 @HasPermissionAllDecorator('hg.admin')
441 442 def settings_email(self):
442 443 """GET /admin/settings/email: All items in the collection"""
443 444 # url('admin_settings_email')
444 445 c.active = 'email'
445 446 c.rhodecode_ini = rhodecode.CONFIG
446 447
447 448 return htmlfill.render(
448 449 render('admin/settings/settings.html'),
449 450 defaults=self._form_defaults(),
450 451 encoding="UTF-8",
451 452 force_defaults=False)
452 453
453 454 @HasPermissionAllDecorator('hg.admin')
454 455 @auth.CSRFRequired()
455 456 def settings_hooks_update(self):
456 457 """POST or DELETE /admin/settings/hooks: All items in the collection"""
457 458 # url('admin_settings_hooks')
458 459 c.active = 'hooks'
459 460 if c.visual.allow_custom_hooks_settings:
460 461 ui_key = request.POST.get('new_hook_ui_key')
461 462 ui_value = request.POST.get('new_hook_ui_value')
462 463
463 464 hook_id = request.POST.get('hook_id')
464 465 new_hook = False
465 466
466 467 model = SettingsModel()
467 468 try:
468 469 if ui_value and ui_key:
469 470 model.create_or_update_hook(ui_key, ui_value)
470 471 h.flash(_('Added new hook'), category='success')
471 472 new_hook = True
472 473 elif hook_id:
473 474 RhodeCodeUi.delete(hook_id)
474 475 Session().commit()
475 476
476 477 # check for edits
477 478 update = False
478 479 _d = request.POST.dict_of_lists()
479 480 for k, v in zip(_d.get('hook_ui_key', []),
480 481 _d.get('hook_ui_value_new', [])):
481 482 model.create_or_update_hook(k, v)
482 483 update = True
483 484
484 485 if update and not new_hook:
485 486 h.flash(_('Updated hooks'), category='success')
486 487 Session().commit()
487 488 except Exception:
488 489 log.exception("Exception during hook creation")
489 490 h.flash(_('Error occurred during hook creation'),
490 491 category='error')
491 492
492 493 return redirect(url('admin_settings_hooks'))
493 494
494 495 @HasPermissionAllDecorator('hg.admin')
495 496 def settings_hooks(self):
496 497 """GET /admin/settings/hooks: All items in the collection"""
497 498 # url('admin_settings_hooks')
498 499 c.active = 'hooks'
499 500
500 501 model = SettingsModel()
501 502 c.hooks = model.get_builtin_hooks()
502 503 c.custom_hooks = model.get_custom_hooks()
503 504
504 505 return htmlfill.render(
505 506 render('admin/settings/settings.html'),
506 507 defaults=self._form_defaults(),
507 508 encoding="UTF-8",
508 509 force_defaults=False)
509 510
510 511 @HasPermissionAllDecorator('hg.admin')
511 512 def settings_search(self):
512 513 """GET /admin/settings/search: All items in the collection"""
513 514 # url('admin_settings_search')
514 515 c.active = 'search'
515 516
516 517 from rhodecode.lib.index import searcher_from_config
517 518 searcher = searcher_from_config(config)
518 519 c.statistics = searcher.statistics()
519 520
520 521 return render('admin/settings/settings.html')
521 522
522 523 @HasPermissionAllDecorator('hg.admin')
523 524 def settings_system(self):
524 525 """GET /admin/settings/system: All items in the collection"""
525 526 # url('admin_settings_system')
526 527 c.active = 'system'
527 528
528 529 defaults = self._form_defaults()
529 530 c.rhodecode_ini = rhodecode.CONFIG
530 531 c.rhodecode_update_url = defaults.get('rhodecode_update_url')
531 532 server_info = ScmModel().get_server_info(request.environ)
532 533 for key, val in server_info.iteritems():
533 534 setattr(c, key, val)
534 535
535 536 if c.disk['percent'] > 90:
536 537 h.flash(h.literal(_(
537 538 'Critical: your disk space is very low <b>%s%%</b> used' %
538 539 c.disk['percent'])), 'error')
539 540 elif c.disk['percent'] > 70:
540 541 h.flash(h.literal(_(
541 542 'Warning: your disk space is running low <b>%s%%</b> used' %
542 543 c.disk['percent'])), 'warning')
543 544
544 545 try:
545 546 c.uptime_age = h._age(
546 547 h.time_to_datetime(c.boot_time), False, show_suffix=False)
547 548 except TypeError:
548 549 c.uptime_age = c.boot_time
549 550
550 551 try:
551 552 c.system_memory = '%s/%s, %s%% (%s%%) used%s' % (
552 553 h.format_byte_size_binary(c.memory['used']),
553 554 h.format_byte_size_binary(c.memory['total']),
554 555 c.memory['percent2'],
555 556 c.memory['percent'],
556 557 ' %s' % c.memory['error'] if 'error' in c.memory else '')
557 558 except TypeError:
558 559 c.system_memory = 'NOT AVAILABLE'
559 560
560 561 return htmlfill.render(
561 562 render('admin/settings/settings.html'),
562 563 defaults=defaults,
563 564 encoding="UTF-8",
564 565 force_defaults=False)
565 566
566 567 @staticmethod
567 568 def get_update_data(update_url):
568 569 """Return the JSON update data."""
569 570 ver = rhodecode.__version__
570 571 log.debug('Checking for upgrade on `%s` server', update_url)
571 572 opener = urllib2.build_opener()
572 573 opener.addheaders = [('User-agent', 'RhodeCode-SCM/%s' % ver)]
573 574 response = opener.open(update_url)
574 575 response_data = response.read()
575 576 data = json.loads(response_data)
576 577
577 578 return data
578 579
579 580 @HasPermissionAllDecorator('hg.admin')
580 581 def settings_system_update(self):
581 582 """GET /admin/settings/system/updates: All items in the collection"""
582 583 # url('admin_settings_system_update')
583 584 defaults = self._form_defaults()
584 585 update_url = defaults.get('rhodecode_update_url', '')
585 586
586 587 _err = lambda s: '<div style="color:#ff8888; padding:4px 0px">%s</div>' % (s)
587 588 try:
588 589 data = self.get_update_data(update_url)
589 590 except urllib2.URLError as e:
590 591 log.exception("Exception contacting upgrade server")
591 592 return _err('Failed to contact upgrade server: %r' % e)
592 593 except ValueError as e:
593 594 log.exception("Bad data sent from update server")
594 595 return _err('Bad data sent from update server')
595 596
596 597 latest = data['versions'][0]
597 598
598 599 c.update_url = update_url
599 600 c.latest_data = latest
600 601 c.latest_ver = latest['version']
601 602 c.cur_ver = rhodecode.__version__
602 603 c.should_upgrade = False
603 604
604 605 if (packaging.version.Version(c.latest_ver) >
605 606 packaging.version.Version(c.cur_ver)):
606 607 c.should_upgrade = True
607 608 c.important_notices = latest['general']
608 609
609 610 return render('admin/settings/settings_system_update.html')
610 611
611 612 @HasPermissionAllDecorator('hg.admin')
612 613 def settings_supervisor(self):
613 614 c.rhodecode_ini = rhodecode.CONFIG
614 615 c.active = 'supervisor'
615 616
616 617 c.supervisor_procs = OrderedDict([
617 618 (SUPERVISOR_MASTER, {}),
618 619 ])
619 620
620 621 c.log_size = 10240
621 622 supervisor = SupervisorModel()
622 623
623 624 _connection = supervisor.get_connection(
624 625 c.rhodecode_ini.get('supervisor.uri'))
625 626 c.connection_error = None
626 627 try:
627 628 _connection.supervisor.getAllProcessInfo()
628 629 except Exception as e:
629 630 c.connection_error = str(e)
630 631 log.exception("Exception reading supervisor data")
631 632 return render('admin/settings/settings.html')
632 633
633 634 groupid = c.rhodecode_ini.get('supervisor.group_id')
634 635
635 636 # feed our group processes to the main
636 637 for proc in supervisor.get_group_processes(_connection, groupid):
637 638 c.supervisor_procs[proc['name']] = {}
638 639
639 640 for k in c.supervisor_procs.keys():
640 641 try:
641 642 # master process info
642 643 if k == SUPERVISOR_MASTER:
643 644 _data = supervisor.get_master_state(_connection)
644 645 _data['name'] = 'supervisor master'
645 646 _data['description'] = 'pid %s, id: %s, ver: %s' % (
646 647 _data['pid'], _data['id'], _data['ver'])
647 648 c.supervisor_procs[k] = _data
648 649 else:
649 650 procid = groupid + ":" + k
650 651 c.supervisor_procs[k] = supervisor.get_process_info(_connection, procid)
651 652 except Exception as e:
652 653 log.exception("Exception reading supervisor data")
653 654 c.supervisor_procs[k] = {'_rhodecode_error': str(e)}
654 655
655 656 return render('admin/settings/settings.html')
656 657
657 658 @HasPermissionAllDecorator('hg.admin')
658 659 def settings_supervisor_log(self, procid):
659 660 import rhodecode
660 661 c.rhodecode_ini = rhodecode.CONFIG
661 662 c.active = 'supervisor_tail'
662 663
663 664 supervisor = SupervisorModel()
664 665 _connection = supervisor.get_connection(c.rhodecode_ini.get('supervisor.uri'))
665 666 groupid = c.rhodecode_ini.get('supervisor.group_id')
666 667 procid = groupid + ":" + procid if procid != SUPERVISOR_MASTER else procid
667 668
668 669 c.log_size = 10240
669 670 offset = abs(safe_int(request.GET.get('offset', c.log_size))) * -1
670 671 c.log = supervisor.read_process_log(_connection, procid, offset, 0)
671 672
672 673 return render('admin/settings/settings.html')
673 674
674 675 @HasPermissionAllDecorator('hg.admin')
675 676 @auth.CSRFRequired()
676 677 def settings_labs_update(self):
677 678 """POST /admin/settings/labs: All items in the collection"""
678 679 # url('admin_settings/labs', method={'POST'})
679 680 c.active = 'labs'
680 681
681 682 application_form = LabsSettingsForm()()
682 683 try:
683 684 form_result = application_form.to_python(dict(request.POST))
684 685 except formencode.Invalid as errors:
685 686 h.flash(
686 687 _('Some form inputs contain invalid data.'),
687 688 category='error')
688 689 return htmlfill.render(
689 690 render('admin/settings/settings.html'),
690 691 defaults=errors.value,
691 692 errors=errors.error_dict or {},
692 693 prefix_error=False,
693 694 encoding='UTF-8',
694 695 force_defaults=False
695 696 )
696 697
697 698 try:
698 699 session = Session()
699 700 for setting in _LAB_SETTINGS:
700 701 setting_name = setting.key[len('rhodecode_'):]
701 702 sett = SettingsModel().create_or_update_setting(
702 703 setting_name, form_result[setting.key], setting.type)
703 704 session.add(sett)
704 705
705 706 except Exception:
706 707 log.exception('Exception while updating lab settings')
707 708 h.flash(_('Error occurred during updating labs settings'),
708 709 category='error')
709 710 else:
710 711 Session().commit()
712 SettingsModel().invalidate_settings_cache()
711 713 h.flash(_('Updated Labs settings'), category='success')
712 714 return redirect(url('admin_settings_labs'))
713 715
714 716 return htmlfill.render(
715 717 render('admin/settings/settings.html'),
716 718 defaults=self._form_defaults(),
717 719 encoding='UTF-8',
718 720 force_defaults=False)
719 721
720 722 @HasPermissionAllDecorator('hg.admin')
721 723 def settings_labs(self):
722 724 """GET /admin/settings/labs: All items in the collection"""
723 725 # url('admin_settings_labs')
724 726 if not c.labs_active:
725 727 redirect(url('admin_settings'))
726 728
727 729 c.active = 'labs'
728 730 c.lab_settings = _LAB_SETTINGS
729 731
730 732 return htmlfill.render(
731 733 render('admin/settings/settings.html'),
732 734 defaults=self._form_defaults(),
733 735 encoding='UTF-8',
734 736 force_defaults=False)
735 737
736 738 def _form_defaults(self):
737 739 defaults = SettingsModel().get_all_settings()
738 740 defaults.update(self._get_hg_ui_settings())
739 741 defaults.update({
740 742 'new_svn_branch': '',
741 743 'new_svn_tag': '',
742 744 })
743 745 return defaults
744 746
745 747
746 748 # :param key: name of the setting including the 'rhodecode_' prefix
747 749 # :param type: the RhodeCodeSetting type to use.
748 750 # :param group: the i18ned group in which we should dispaly this setting
749 751 # :param label: the i18ned label we should display for this setting
750 752 # :param help: the i18ned help we should dispaly for this setting
751 753 LabSetting = collections.namedtuple(
752 754 'LabSetting', ('key', 'type', 'group', 'label', 'help'))
753 755
754 756
755 757 # This list has to be kept in sync with the form
756 758 # rhodecode.model.forms.LabsSettingsForm.
757 759 _LAB_SETTINGS = [
758 760 LabSetting(
759 761 key='rhodecode_hg_use_rebase_for_merging',
760 762 type='bool',
761 763 group=lazy_ugettext('Mercurial server-side merge'),
762 764 label=lazy_ugettext('Use rebase instead of creating a merge commit when merging via web interface'),
763 765 help='' # Do not translate the empty string!
764 766 ),
765 767 LabSetting(
766 768 key='rhodecode_proxy_subversion_http_requests',
767 769 type='bool',
768 770 group=lazy_ugettext('Subversion HTTP Support'),
769 771 label=lazy_ugettext('Proxy subversion HTTP requests'),
770 772 help='' # Do not translate the empty string!
771 773 ),
772 774 LabSetting(
773 775 key='rhodecode_subversion_http_server_url',
774 776 type='str',
775 777 group=lazy_ugettext('Subversion HTTP Server URL'),
776 778 label='', # Do not translate the empty string!
777 779 help=lazy_ugettext('e.g. http://localhost:8080/')
778 780 ),
779 781 ]
780 782
781 783
782 784 NavListEntry = collections.namedtuple('NavListEntry', ['key', 'name', 'url'])
783 785
784 786
785 787 class NavEntry(object):
786 788
787 789 def __init__(self, key, name, view_name, pyramid=False):
788 790 self.key = key
789 791 self.name = name
790 792 self.view_name = view_name
791 793 self.pyramid = pyramid
792 794
793 795 def generate_url(self, request):
794 796 if self.pyramid:
795 797 if hasattr(request, 'route_path'):
796 798 return request.route_path(self.view_name)
797 799 else:
798 800 # TODO: johbo: Remove this after migrating to pyramid.
799 801 # We need the pyramid request here to generate URLs to pyramid
800 802 # views from within pylons views.
801 803 from pyramid.threadlocal import get_current_request
802 804 pyramid_request = get_current_request()
803 805 return pyramid_request.route_path(self.view_name)
804 806 else:
805 807 return url(self.view_name)
806 808
807 809
808 810 class NavigationRegistry(object):
809 811
810 812 _base_entries = [
811 813 NavEntry('global', lazy_ugettext('Global'), 'admin_settings_global'),
812 814 NavEntry('vcs', lazy_ugettext('VCS'), 'admin_settings_vcs'),
813 815 NavEntry('visual', lazy_ugettext('Visual'), 'admin_settings_visual'),
814 816 NavEntry('mapping', lazy_ugettext('Remap and Rescan'),
815 817 'admin_settings_mapping'),
816 818 NavEntry('issuetracker', lazy_ugettext('Issue Tracker'),
817 819 'admin_settings_issuetracker'),
818 820 NavEntry('email', lazy_ugettext('Email'), 'admin_settings_email'),
819 821 NavEntry('hooks', lazy_ugettext('Hooks'), 'admin_settings_hooks'),
820 822 NavEntry('search', lazy_ugettext('Full Text Search'),
821 823 'admin_settings_search'),
822 824 NavEntry('system', lazy_ugettext('System Info'),
823 825 'admin_settings_system'),
824 826 NavEntry('open_source', lazy_ugettext('Open Source Licenses'),
825 827 'admin_settings_open_source', pyramid=True),
826 828 # TODO: marcink: we disable supervisor now until the supervisor stats
827 829 # page is fixed in the nix configuration
828 830 # NavEntry('supervisor', lazy_ugettext('Supervisor'),
829 831 # 'admin_settings_supervisor'),
830 832 ]
831 833
832 834 def __init__(self):
833 835 self._registered_entries = collections.OrderedDict([
834 836 (item.key, item) for item in self.__class__._base_entries
835 837 ])
836 838
837 839 # Add the labs entry when it's activated.
838 840 labs_active = str2bool(
839 841 rhodecode.CONFIG.get('labs_settings_active', 'false'))
840 842 if labs_active:
841 843 self.add_entry(
842 844 NavEntry('labs', lazy_ugettext('Labs'), 'admin_settings_labs'))
843 845
844 846 def add_entry(self, entry):
845 847 self._registered_entries[entry.key] = entry
846 848
847 849 def get_navlist(self, request):
848 850 navlist = [NavListEntry(i.key, i.name, i.generate_url(request))
849 851 for i in self._registered_entries.values()]
850 852 return navlist
851 853
852 854 navigation = NavigationRegistry()
@@ -1,220 +1,226 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2015-2016 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21
22 22 import beaker
23 23 import logging
24 24
25 25 from beaker.cache import _cache_decorate, cache_regions, region_invalidate
26 26
27 27 from rhodecode.lib.utils import safe_str, md5
28 28 from rhodecode.model.db import Session, CacheKey, IntegrityError
29 29
30 30 log = logging.getLogger(__name__)
31 31
32 32 FILE_TREE = 'cache_file_tree'
33 33 FILE_TREE_META = 'cache_file_tree_metadata'
34 34 FILE_SEARCH_TREE_META = 'cache_file_search_metadata'
35 35 SUMMARY_STATS = 'cache_summary_stats'
36 36
37 37 # This list of caches gets purged when invalidation happens
38 38 USED_REPO_CACHES = (FILE_TREE, FILE_TREE_META, FILE_TREE_META)
39 39
40 40 DEFAULT_CACHE_MANAGER_CONFIG = {
41 41 'type': 'memorylru_base',
42 42 'max_items': 10240,
43 43 'key_length': 256,
44 44 'enabled': True
45 45 }
46 46
47 47
48 48 def configure_cache_region(
49 49 region_name, region_kw, default_cache_kw, default_expire=60):
50 50 default_type = default_cache_kw.get('type', 'memory')
51 51 default_lock_dir = default_cache_kw.get('lock_dir')
52 52 default_data_dir = default_cache_kw.get('data_dir')
53 53
54 54 region_kw['lock_dir'] = region_kw.get('lock_dir', default_lock_dir)
55 55 region_kw['data_dir'] = region_kw.get('data_dir', default_data_dir)
56 56 region_kw['type'] = region_kw.get('type', default_type)
57 57 region_kw['expire'] = int(region_kw.get('expire', default_expire))
58 58
59 59 beaker.cache.cache_regions[region_name] = region_kw
60 60
61 61
62 62 def get_cache_manager(region_name, cache_name, custom_ttl=None):
63 63 """
64 64 Creates a Beaker cache manager. Such instance can be used like that::
65 65
66 66 _namespace = caches.get_repo_namespace_key(caches.XXX, repo_name)
67 67 cache_manager = caches.get_cache_manager('repo_cache_long', _namespace)
68 68 _cache_key = caches.compute_key_from_params(repo_name, commit.raw_id)
69 69 def heavy_compute():
70 70 ...
71 71 result = cache_manager.get(_cache_key, createfunc=heavy_compute)
72 72
73 73 :param region_name: region from ini file
74 74 :param cache_name: custom cache name, usually prefix+repo_name. eg
75 75 file_switcher_repo1
76 76 :param custom_ttl: override .ini file timeout on this cache
77 77 :return: instance of cache manager
78 78 """
79 79
80 80 cache_config = cache_regions.get(region_name, DEFAULT_CACHE_MANAGER_CONFIG)
81 81 if custom_ttl:
82 82 log.debug('Updating region %s with custom ttl: %s',
83 83 region_name, custom_ttl)
84 84 cache_config.update({'expire': custom_ttl})
85 85
86 86 return beaker.cache.Cache._get_cache(cache_name, cache_config)
87 87
88 88
89 89 def clear_cache_manager(cache_manager):
90 """
91 namespace = 'foobar'
92 cache_manager = get_cache_manager('repo_cache_long', namespace)
93 clear_cache_manager(cache_manager)
94 """
95
90 96 log.debug('Clearing all values for cache manager %s', cache_manager)
91 97 cache_manager.clear()
92 98
93 99
94 100 def clear_repo_caches(repo_name):
95 101 # invalidate cache manager for this repo
96 102 for prefix in USED_REPO_CACHES:
97 103 namespace = get_repo_namespace_key(prefix, repo_name)
98 104 cache_manager = get_cache_manager('repo_cache_long', namespace)
99 105 clear_cache_manager(cache_manager)
100 106
101 107
102 108 def compute_key_from_params(*args):
103 109 """
104 110 Helper to compute key from given params to be used in cache manager
105 111 """
106 112 return md5("_".join(map(safe_str, args)))
107 113
108 114
109 115 def get_repo_namespace_key(prefix, repo_name):
110 116 return '{0}_{1}'.format(prefix, compute_key_from_params(repo_name))
111 117
112 118
113 119 def conditional_cache(region, prefix, condition, func):
114 120 """
115 121 Conditional caching function use like::
116 122 def _c(arg):
117 123 # heavy computation function
118 124 return data
119 125
120 126 # depending on the condition the compute is wrapped in cache or not
121 127 compute = conditional_cache('short_term', 'cache_desc',
122 128 condition=True, func=func)
123 129 return compute(arg)
124 130
125 131 :param region: name of cache region
126 132 :param prefix: cache region prefix
127 133 :param condition: condition for cache to be triggered, and
128 134 return data cached
129 135 :param func: wrapped heavy function to compute
130 136
131 137 """
132 138 wrapped = func
133 139 if condition:
134 140 log.debug('conditional_cache: True, wrapping call of '
135 141 'func: %s into %s region cache', region, func)
136 142 cached_region = _cache_decorate((prefix,), None, None, region)
137 143 wrapped = cached_region(func)
138 144 return wrapped
139 145
140 146
141 147 class ActiveRegionCache(object):
142 148 def __init__(self, context):
143 149 self.context = context
144 150
145 151 def invalidate(self, *args, **kwargs):
146 152 return False
147 153
148 154 def compute(self):
149 155 log.debug('Context cache: getting obj %s from cache', self.context)
150 156 return self.context.compute_func(self.context.cache_key)
151 157
152 158
153 159 class FreshRegionCache(ActiveRegionCache):
154 160 def invalidate(self):
155 161 log.debug('Context cache: invalidating cache for %s', self.context)
156 162 region_invalidate(
157 163 self.context.compute_func, None, self.context.cache_key)
158 164 return True
159 165
160 166
161 167 class InvalidationContext(object):
162 168 def __repr__(self):
163 169 return '<InvalidationContext:{}[{}]>'.format(
164 170 self.repo_name, self.cache_type)
165 171
166 172 def __init__(self, compute_func, repo_name, cache_type,
167 173 raise_exception=False):
168 174 self.compute_func = compute_func
169 175 self.repo_name = repo_name
170 176 self.cache_type = cache_type
171 177 self.cache_key = compute_key_from_params(
172 178 repo_name, cache_type)
173 179 self.raise_exception = raise_exception
174 180
175 181 def get_cache_obj(self):
176 182 cache_key = CacheKey.get_cache_key(
177 183 self.repo_name, self.cache_type)
178 184 cache_obj = CacheKey.get_active_cache(cache_key)
179 185 if not cache_obj:
180 186 cache_obj = CacheKey(cache_key, self.repo_name)
181 187 return cache_obj
182 188
183 189 def __enter__(self):
184 190 """
185 191 Test if current object is valid, and return CacheRegion function
186 192 that does invalidation and calculation
187 193 """
188 194
189 195 self.cache_obj = self.get_cache_obj()
190 196 if self.cache_obj.cache_active:
191 197 # means our cache obj is existing and marked as it's
192 198 # cache is not outdated, we return BaseInvalidator
193 199 self.skip_cache_active_change = True
194 200 return ActiveRegionCache(self)
195 201
196 202 # the key is either not existing or set to False, we return
197 203 # the real invalidator which re-computes value. We additionally set
198 204 # the flag to actually update the Database objects
199 205 self.skip_cache_active_change = False
200 206 return FreshRegionCache(self)
201 207
202 208 def __exit__(self, exc_type, exc_val, exc_tb):
203 209
204 210 if self.skip_cache_active_change:
205 211 return
206 212
207 213 try:
208 214 self.cache_obj.cache_active = True
209 215 Session().add(self.cache_obj)
210 216 Session().commit()
211 217 except IntegrityError:
212 218 # if we catch integrity error, it means we inserted this object
213 219 # assumption is that's really an edge race-condition case and
214 220 # it's safe is to skip it
215 221 Session().rollback()
216 222 except Exception:
217 223 log.exception('Failed to commit on cache key update')
218 224 Session().rollback()
219 225 if self.raise_exception:
220 226 raise
@@ -1,690 +1,696 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import hashlib
22 22 import logging
23 23 from collections import namedtuple
24 24 from functools import wraps
25 25
26 26 from rhodecode.lib import caches
27 27 from rhodecode.lib.caching_query import FromCache
28 28 from rhodecode.lib.utils2 import (
29 29 Optional, AttributeDict, safe_str, remove_prefix, str2bool)
30 30 from rhodecode.model import BaseModel
31 31 from rhodecode.model.db import (
32 32 RepoRhodeCodeUi, RepoRhodeCodeSetting, RhodeCodeUi, RhodeCodeSetting)
33 33 from rhodecode.model.meta import Session
34 34
35 35
36 36 log = logging.getLogger(__name__)
37 37
38 38
39 39 UiSetting = namedtuple(
40 40 'UiSetting', ['section', 'key', 'value', 'active'])
41 41
42 42 SOCIAL_PLUGINS_LIST = ['github', 'bitbucket', 'twitter', 'google']
43 43
44 44
45 45 class SettingNotFound(Exception):
46 46 def __init__(self):
47 47 super(SettingNotFound, self).__init__('Setting is not found')
48 48
49 49
50 50 class SettingsModel(BaseModel):
51 51 BUILTIN_HOOKS = (
52 52 RhodeCodeUi.HOOK_REPO_SIZE, RhodeCodeUi.HOOK_PUSH,
53 53 RhodeCodeUi.HOOK_PRE_PUSH, RhodeCodeUi.HOOK_PULL,
54 54 RhodeCodeUi.HOOK_PRE_PULL)
55 55 HOOKS_SECTION = 'hooks'
56 56
57 57 def __init__(self, sa=None, repo=None):
58 58 self.repo = repo
59 59 self.UiDbModel = RepoRhodeCodeUi if repo else RhodeCodeUi
60 60 self.SettingsDbModel = (
61 61 RepoRhodeCodeSetting if repo else RhodeCodeSetting)
62 62 super(SettingsModel, self).__init__(sa)
63 63
64 64 def get_ui_by_key(self, key):
65 65 q = self.UiDbModel.query()
66 66 q = q.filter(self.UiDbModel.ui_key == key)
67 67 q = self._filter_by_repo(RepoRhodeCodeUi, q)
68 68 return q.scalar()
69 69
70 70 def get_ui_by_section(self, section):
71 71 q = self.UiDbModel.query()
72 72 q = q.filter(self.UiDbModel.ui_section == section)
73 73 q = self._filter_by_repo(RepoRhodeCodeUi, q)
74 74 return q.all()
75 75
76 76 def get_ui_by_section_and_key(self, section, key):
77 77 q = self.UiDbModel.query()
78 78 q = q.filter(self.UiDbModel.ui_section == section)
79 79 q = q.filter(self.UiDbModel.ui_key == key)
80 80 q = self._filter_by_repo(RepoRhodeCodeUi, q)
81 81 return q.scalar()
82 82
83 83 def get_ui(self, section=None, key=None):
84 84 q = self.UiDbModel.query()
85 85 q = self._filter_by_repo(RepoRhodeCodeUi, q)
86 86
87 87 if section:
88 88 q = q.filter(self.UiDbModel.ui_section == section)
89 89 if key:
90 90 q = q.filter(self.UiDbModel.ui_key == key)
91 91
92 92 # TODO: mikhail: add caching
93 93 result = [
94 94 UiSetting(
95 95 section=safe_str(r.ui_section), key=safe_str(r.ui_key),
96 96 value=safe_str(r.ui_value), active=r.ui_active
97 97 )
98 98 for r in q.all()
99 99 ]
100 100 return result
101 101
102 102 def get_builtin_hooks(self):
103 103 q = self.UiDbModel.query()
104 104 q = q.filter(self.UiDbModel.ui_key.in_(self.BUILTIN_HOOKS))
105 105 return self._get_hooks(q)
106 106
107 107 def get_custom_hooks(self):
108 108 q = self.UiDbModel.query()
109 109 q = q.filter(~self.UiDbModel.ui_key.in_(self.BUILTIN_HOOKS))
110 110 return self._get_hooks(q)
111 111
112 112 def create_ui_section_value(self, section, val, key=None, active=True):
113 113 new_ui = self.UiDbModel()
114 114 new_ui.ui_section = section
115 115 new_ui.ui_value = val
116 116 new_ui.ui_active = active
117 117
118 118 if self.repo:
119 119 repo = self._get_repo(self.repo)
120 120 repository_id = repo.repo_id
121 121 new_ui.repository_id = repository_id
122 122
123 123 if not key:
124 124 # keys are unique so they need appended info
125 125 if self.repo:
126 126 key = hashlib.sha1(
127 127 '{}{}{}'.format(section, val, repository_id)).hexdigest()
128 128 else:
129 129 key = hashlib.sha1('{}{}'.format(section, val)).hexdigest()
130 130
131 131 new_ui.ui_key = key
132 132
133 133 Session().add(new_ui)
134 134 return new_ui
135 135
136 136 def create_or_update_hook(self, key, value):
137 137 ui = (
138 138 self.get_ui_by_section_and_key(self.HOOKS_SECTION, key) or
139 139 self.UiDbModel())
140 140 ui.ui_section = self.HOOKS_SECTION
141 141 ui.ui_active = True
142 142 ui.ui_key = key
143 143 ui.ui_value = value
144 144
145 145 if self.repo:
146 146 repo = self._get_repo(self.repo)
147 147 repository_id = repo.repo_id
148 148 ui.repository_id = repository_id
149 149
150 150 Session().add(ui)
151 151 return ui
152 152
153 153 def delete_ui(self, id_):
154 154 ui = self.UiDbModel.get(id_)
155 155 if not ui:
156 156 raise SettingNotFound()
157 157 Session().delete(ui)
158 158
159 159 def get_setting_by_name(self, name):
160 160 q = self._get_settings_query()
161 161 q = q.filter(self.SettingsDbModel.app_settings_name == name)
162 162 return q.scalar()
163 163
164 164 def create_or_update_setting(
165 165 self, name, val=Optional(''), type_=Optional('unicode')):
166 166 """
167 167 Creates or updates RhodeCode setting. If updates is triggered it will
168 168 only update parameters that are explicityl set Optional instance will
169 169 be skipped
170 170
171 171 :param name:
172 172 :param val:
173 173 :param type_:
174 174 :return:
175 175 """
176 176
177 177 res = self.get_setting_by_name(name)
178 178 repo = self._get_repo(self.repo) if self.repo else None
179 179
180 180 if not res:
181 181 val = Optional.extract(val)
182 182 type_ = Optional.extract(type_)
183 183
184 184 args = (
185 185 (repo.repo_id, name, val, type_)
186 186 if repo else (name, val, type_))
187 187 res = self.SettingsDbModel(*args)
188 188
189 189 else:
190 190 if self.repo:
191 191 res.repository_id = repo.repo_id
192 192
193 193 res.app_settings_name = name
194 194 if not isinstance(type_, Optional):
195 195 # update if set
196 196 res.app_settings_type = type_
197 197 if not isinstance(val, Optional):
198 198 # update if set
199 199 res.app_settings_value = val
200 200
201 201 Session.add(res)
202 202 return res
203 203
204 def invalidate_settings_cache(self):
205 namespace = 'rhodecode_settings'
206 cache_manager = caches.get_cache_manager('sql_cache_short', namespace)
207 caches.clear_cache_manager(cache_manager)
208
204 209 def get_all_settings(self, cache=False):
205 210 def _compute():
206 211 q = self._get_settings_query()
207 212 if not q:
208 213 raise Exception('Could not get application settings !')
209 214
210 215 settings = {
211 216 'rhodecode_' + result.app_settings_name: result.app_settings_value
212 217 for result in q
213 218 }
214 219 return settings
215 220
216 221 if cache:
222 log.debug('Fetching app settings using cache')
217 223 repo = self._get_repo(self.repo) if self.repo else None
218 224 namespace = 'rhodecode_settings'
219 225 cache_manager = caches.get_cache_manager(
220 226 'sql_cache_short', namespace)
221 227 _cache_key = (
222 228 "get_repo_{}_settings".format(repo.repo_id)
223 229 if repo else "get_app_settings")
224 230
225 231 return cache_manager.get(_cache_key, createfunc=_compute)
226 232
227 233 else:
228 234 return _compute()
229 235
230 236 def get_auth_settings(self):
231 237 q = self._get_settings_query()
232 238 q = q.filter(
233 239 self.SettingsDbModel.app_settings_name.startswith('auth_'))
234 240 rows = q.all()
235 241 auth_settings = {
236 242 row.app_settings_name: row.app_settings_value for row in rows}
237 243 return auth_settings
238 244
239 245 def get_auth_plugins(self):
240 246 auth_plugins = self.get_setting_by_name("auth_plugins")
241 247 return auth_plugins.app_settings_value
242 248
243 249 def get_default_repo_settings(self, strip_prefix=False):
244 250 q = self._get_settings_query()
245 251 q = q.filter(
246 252 self.SettingsDbModel.app_settings_name.startswith('default_'))
247 253 rows = q.all()
248 254
249 255 result = {}
250 256 for row in rows:
251 257 key = row.app_settings_name
252 258 if strip_prefix:
253 259 key = remove_prefix(key, prefix='default_')
254 260 result.update({key: row.app_settings_value})
255 261 return result
256 262
257 263 def get_repo(self):
258 264 repo = self._get_repo(self.repo)
259 265 if not repo:
260 266 raise Exception(
261 267 'Repository {} cannot be found'.format(self.repo))
262 268 return repo
263 269
264 270 def _filter_by_repo(self, model, query):
265 271 if self.repo:
266 272 repo = self.get_repo()
267 273 query = query.filter(model.repository_id == repo.repo_id)
268 274 return query
269 275
270 276 def _get_hooks(self, query):
271 277 query = query.filter(self.UiDbModel.ui_section == self.HOOKS_SECTION)
272 278 query = self._filter_by_repo(RepoRhodeCodeUi, query)
273 279 return query.all()
274 280
275 281 def _get_settings_query(self):
276 282 q = self.SettingsDbModel.query()
277 283 return self._filter_by_repo(RepoRhodeCodeSetting, q)
278 284
279 285 def list_enabled_social_plugins(self, settings):
280 286 enabled = []
281 287 for plug in SOCIAL_PLUGINS_LIST:
282 288 if str2bool(settings.get('rhodecode_auth_{}_enabled'.format(plug)
283 289 )):
284 290 enabled.append(plug)
285 291 return enabled
286 292
287 293
288 294 def assert_repo_settings(func):
289 295 @wraps(func)
290 296 def _wrapper(self, *args, **kwargs):
291 297 if not self.repo_settings:
292 298 raise Exception('Repository is not specified')
293 299 return func(self, *args, **kwargs)
294 300 return _wrapper
295 301
296 302
297 303 class IssueTrackerSettingsModel(object):
298 304 INHERIT_SETTINGS = 'inherit_issue_tracker_settings'
299 305 SETTINGS_PREFIX = 'issuetracker_'
300 306
301 307 def __init__(self, sa=None, repo=None):
302 308 self.global_settings = SettingsModel(sa=sa)
303 309 self.repo_settings = SettingsModel(sa=sa, repo=repo) if repo else None
304 310
305 311 @property
306 312 def inherit_global_settings(self):
307 313 if not self.repo_settings:
308 314 return True
309 315 setting = self.repo_settings.get_setting_by_name(self.INHERIT_SETTINGS)
310 316 return setting.app_settings_value if setting else True
311 317
312 318 @inherit_global_settings.setter
313 319 def inherit_global_settings(self, value):
314 320 if self.repo_settings:
315 321 settings = self.repo_settings.create_or_update_setting(
316 322 self.INHERIT_SETTINGS, value, type_='bool')
317 323 Session().add(settings)
318 324
319 325 def _get_keyname(self, key, uid, prefix=''):
320 326 return '{0}{1}{2}_{3}'.format(
321 327 prefix, self.SETTINGS_PREFIX, key, uid)
322 328
323 329 def _make_dict_for_settings(self, qs):
324 330 prefix_match = self._get_keyname('pat', '', 'rhodecode_')
325 331
326 332 issuetracker_entries = {}
327 333 # create keys
328 334 for k, v in qs.items():
329 335 if k.startswith(prefix_match):
330 336 uid = k[len(prefix_match):]
331 337 issuetracker_entries[uid] = None
332 338
333 339 # populate
334 340 for uid in issuetracker_entries:
335 341 issuetracker_entries[uid] = AttributeDict({
336 342 'pat': qs.get(self._get_keyname('pat', uid, 'rhodecode_')),
337 343 'url': qs.get(self._get_keyname('url', uid, 'rhodecode_')),
338 344 'pref': qs.get(self._get_keyname('pref', uid, 'rhodecode_')),
339 345 'desc': qs.get(self._get_keyname('desc', uid, 'rhodecode_')),
340 346 })
341 347 return issuetracker_entries
342 348
343 349 def get_global_settings(self, cache=False):
344 350 """
345 351 Returns list of global issue tracker settings
346 352 """
347 353 defaults = self.global_settings.get_all_settings(cache=cache)
348 354 settings = self._make_dict_for_settings(defaults)
349 355 return settings
350 356
351 357 def get_repo_settings(self, cache=False):
352 358 """
353 359 Returns list of issue tracker settings per repository
354 360 """
355 361 if not self.repo_settings:
356 362 raise Exception('Repository is not specified')
357 363 all_settings = self.repo_settings.get_all_settings(cache=cache)
358 364 settings = self._make_dict_for_settings(all_settings)
359 365 return settings
360 366
361 367 def get_settings(self, cache=False):
362 368 if self.inherit_global_settings:
363 369 return self.get_global_settings(cache=cache)
364 370 else:
365 371 return self.get_repo_settings(cache=cache)
366 372
367 373 def delete_entries(self, uid):
368 374 if self.repo_settings:
369 375 all_patterns = self.get_repo_settings()
370 376 settings_model = self.repo_settings
371 377 else:
372 378 all_patterns = self.get_global_settings()
373 379 settings_model = self.global_settings
374 380 entries = all_patterns.get(uid)
375 381
376 382 for del_key in entries:
377 383 setting_name = self._get_keyname(del_key, uid)
378 384 entry = settings_model.get_setting_by_name(setting_name)
379 385 if entry:
380 386 Session().delete(entry)
381 387
382 388 Session().commit()
383 389
384 390 def create_or_update_setting(
385 391 self, name, val=Optional(''), type_=Optional('unicode')):
386 392 if self.repo_settings:
387 393 setting = self.repo_settings.create_or_update_setting(
388 394 name, val, type_)
389 395 else:
390 396 setting = self.global_settings.create_or_update_setting(
391 397 name, val, type_)
392 398 return setting
393 399
394 400
395 401 class VcsSettingsModel(object):
396 402
397 403 INHERIT_SETTINGS = 'inherit_vcs_settings'
398 404 GENERAL_SETTINGS = ('use_outdated_comments', 'pr_merge_enabled')
399 405 HOOKS_SETTINGS = (
400 406 ('hooks', 'changegroup.repo_size'),
401 407 ('hooks', 'changegroup.push_logger'),
402 408 ('hooks', 'outgoing.pull_logger'))
403 409 HG_SETTINGS = (
404 410 ('extensions', 'largefiles'), ('phases', 'publish'))
405 411 GLOBAL_HG_SETTINGS = HG_SETTINGS + (('extensions', 'hgsubversion'), )
406 412 SVN_BRANCH_SECTION = 'vcs_svn_branch'
407 413 SVN_TAG_SECTION = 'vcs_svn_tag'
408 414 SSL_SETTING = ('web', 'push_ssl')
409 415 PATH_SETTING = ('paths', '/')
410 416
411 417 def __init__(self, sa=None, repo=None):
412 418 self.global_settings = SettingsModel(sa=sa)
413 419 self.repo_settings = SettingsModel(sa=sa, repo=repo) if repo else None
414 420 self._ui_settings = self.HG_SETTINGS + self.HOOKS_SETTINGS
415 421 self._svn_sections = (self.SVN_BRANCH_SECTION, self.SVN_TAG_SECTION)
416 422
417 423 @property
418 424 @assert_repo_settings
419 425 def inherit_global_settings(self):
420 426 setting = self.repo_settings.get_setting_by_name(self.INHERIT_SETTINGS)
421 427 return setting.app_settings_value if setting else True
422 428
423 429 @inherit_global_settings.setter
424 430 @assert_repo_settings
425 431 def inherit_global_settings(self, value):
426 432 self.repo_settings.create_or_update_setting(
427 433 self.INHERIT_SETTINGS, value, type_='bool')
428 434
429 435 def get_global_svn_branch_patterns(self):
430 436 return self.global_settings.get_ui_by_section(self.SVN_BRANCH_SECTION)
431 437
432 438 @assert_repo_settings
433 439 def get_repo_svn_branch_patterns(self):
434 440 return self.repo_settings.get_ui_by_section(self.SVN_BRANCH_SECTION)
435 441
436 442 def get_global_svn_tag_patterns(self):
437 443 return self.global_settings.get_ui_by_section(self.SVN_TAG_SECTION)
438 444
439 445 @assert_repo_settings
440 446 def get_repo_svn_tag_patterns(self):
441 447 return self.repo_settings.get_ui_by_section(self.SVN_TAG_SECTION)
442 448
443 449 def get_global_settings(self):
444 450 return self._collect_all_settings(global_=True)
445 451
446 452 @assert_repo_settings
447 453 def get_repo_settings(self):
448 454 return self._collect_all_settings(global_=False)
449 455
450 456 @assert_repo_settings
451 457 def create_or_update_repo_settings(
452 458 self, data, inherit_global_settings=False):
453 459 from rhodecode.model.scm import ScmModel
454 460
455 461 self.inherit_global_settings = inherit_global_settings
456 462
457 463 repo = self.repo_settings.get_repo()
458 464 if not inherit_global_settings:
459 465 if repo.repo_type == 'svn':
460 466 self.create_repo_svn_settings(data)
461 467 else:
462 468 self.create_or_update_repo_hook_settings(data)
463 469 self.create_or_update_repo_pr_settings(data)
464 470
465 471 if repo.repo_type == 'hg':
466 472 self.create_or_update_repo_hg_settings(data)
467 473
468 474 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
469 475
470 476 @assert_repo_settings
471 477 def create_or_update_repo_hook_settings(self, data):
472 478 for section, key in self.HOOKS_SETTINGS:
473 479 data_key = self._get_form_ui_key(section, key)
474 480 if data_key not in data:
475 481 raise ValueError(
476 482 'The given data does not contain {} key'.format(data_key))
477 483
478 484 active = data.get(data_key)
479 485 repo_setting = self.repo_settings.get_ui_by_section_and_key(
480 486 section, key)
481 487 if not repo_setting:
482 488 global_setting = self.global_settings.\
483 489 get_ui_by_section_and_key(section, key)
484 490 self.repo_settings.create_ui_section_value(
485 491 section, global_setting.ui_value, key=key, active=active)
486 492 else:
487 493 repo_setting.ui_active = active
488 494 Session().add(repo_setting)
489 495
490 496 def update_global_hook_settings(self, data):
491 497 for section, key in self.HOOKS_SETTINGS:
492 498 data_key = self._get_form_ui_key(section, key)
493 499 if data_key not in data:
494 500 raise ValueError(
495 501 'The given data does not contain {} key'.format(data_key))
496 502 active = data.get(data_key)
497 503 repo_setting = self.global_settings.get_ui_by_section_and_key(
498 504 section, key)
499 505 repo_setting.ui_active = active
500 506 Session().add(repo_setting)
501 507
502 508 @assert_repo_settings
503 509 def create_or_update_repo_pr_settings(self, data):
504 510 return self._create_or_update_general_settings(
505 511 self.repo_settings, data)
506 512
507 513 def create_or_update_global_pr_settings(self, data):
508 514 return self._create_or_update_general_settings(
509 515 self.global_settings, data)
510 516
511 517 @assert_repo_settings
512 518 def create_repo_svn_settings(self, data):
513 519 return self._create_svn_settings(self.repo_settings, data)
514 520
515 521 def create_global_svn_settings(self, data):
516 522 return self._create_svn_settings(self.global_settings, data)
517 523
518 524 @assert_repo_settings
519 525 def create_or_update_repo_hg_settings(self, data):
520 526 largefiles, phases = self.HG_SETTINGS
521 527 largefiles_key, phases_key = self._get_hg_settings(
522 528 self.HG_SETTINGS, data)
523 529 self._create_or_update_ui(
524 530 self.repo_settings, *largefiles, value='',
525 531 active=data[largefiles_key])
526 532 self._create_or_update_ui(
527 533 self.repo_settings, *phases, value=safe_str(data[phases_key]))
528 534
529 535 def create_or_update_global_hg_settings(self, data):
530 536 largefiles, phases, subversion = self.GLOBAL_HG_SETTINGS
531 537 largefiles_key, phases_key, subversion_key = self._get_hg_settings(
532 538 self.GLOBAL_HG_SETTINGS, data)
533 539 self._create_or_update_ui(
534 540 self.global_settings, *largefiles, value='',
535 541 active=data[largefiles_key])
536 542 self._create_or_update_ui(
537 543 self.global_settings, *phases, value=safe_str(data[phases_key]))
538 544 self._create_or_update_ui(
539 545 self.global_settings, *subversion, active=data[subversion_key])
540 546
541 547 def update_global_ssl_setting(self, value):
542 548 self._create_or_update_ui(
543 549 self.global_settings, *self.SSL_SETTING, value=value)
544 550
545 551 def update_global_path_setting(self, value):
546 552 self._create_or_update_ui(
547 553 self.global_settings, *self.PATH_SETTING, value=value)
548 554
549 555 @assert_repo_settings
550 556 def delete_repo_svn_pattern(self, id_):
551 557 self.repo_settings.delete_ui(id_)
552 558
553 559 def delete_global_svn_pattern(self, id_):
554 560 self.global_settings.delete_ui(id_)
555 561
556 562 @assert_repo_settings
557 563 def get_repo_ui_settings(self, section=None, key=None):
558 564 global_uis = self.global_settings.get_ui(section, key)
559 565 repo_uis = self.repo_settings.get_ui(section, key)
560 566 filtered_repo_uis = self._filter_ui_settings(repo_uis)
561 567 filtered_repo_uis_keys = [
562 568 (s.section, s.key) for s in filtered_repo_uis]
563 569
564 570 def _is_global_ui_filtered(ui):
565 571 return (
566 572 (ui.section, ui.key) in filtered_repo_uis_keys
567 573 or ui.section in self._svn_sections)
568 574
569 575 filtered_global_uis = [
570 576 ui for ui in global_uis if not _is_global_ui_filtered(ui)]
571 577
572 578 return filtered_global_uis + filtered_repo_uis
573 579
574 580 def get_global_ui_settings(self, section=None, key=None):
575 581 return self.global_settings.get_ui(section, key)
576 582
577 583 def get_ui_settings(self, section=None, key=None):
578 584 if not self.repo_settings or self.inherit_global_settings:
579 585 return self.get_global_ui_settings(section, key)
580 586 else:
581 587 return self.get_repo_ui_settings(section, key)
582 588
583 589 def get_svn_patterns(self, section=None):
584 590 if not self.repo_settings:
585 591 return self.get_global_ui_settings(section)
586 592 else:
587 593 return self.get_repo_ui_settings(section)
588 594
589 595 @assert_repo_settings
590 596 def get_repo_general_settings(self):
591 597 global_settings = self.global_settings.get_all_settings()
592 598 repo_settings = self.repo_settings.get_all_settings()
593 599 filtered_repo_settings = self._filter_general_settings(repo_settings)
594 600 global_settings.update(filtered_repo_settings)
595 601 return global_settings
596 602
597 603 def get_global_general_settings(self):
598 604 return self.global_settings.get_all_settings()
599 605
600 606 def get_general_settings(self):
601 607 if not self.repo_settings or self.inherit_global_settings:
602 608 return self.get_global_general_settings()
603 609 else:
604 610 return self.get_repo_general_settings()
605 611
606 612 def get_repos_location(self):
607 613 return self.global_settings.get_ui_by_key('/').ui_value
608 614
609 615 def _filter_ui_settings(self, settings):
610 616 filtered_settings = [
611 617 s for s in settings if self._should_keep_setting(s)]
612 618 return filtered_settings
613 619
614 620 def _should_keep_setting(self, setting):
615 621 keep = (
616 622 (setting.section, setting.key) in self._ui_settings or
617 623 setting.section in self._svn_sections)
618 624 return keep
619 625
620 626 def _filter_general_settings(self, settings):
621 627 keys = ['rhodecode_{}'.format(key) for key in self.GENERAL_SETTINGS]
622 628 return {
623 629 k: settings[k]
624 630 for k in settings if k in keys}
625 631
626 632 def _collect_all_settings(self, global_=False):
627 633 settings = self.global_settings if global_ else self.repo_settings
628 634 result = {}
629 635
630 636 for section, key in self._ui_settings:
631 637 ui = settings.get_ui_by_section_and_key(section, key)
632 638 result_key = self._get_form_ui_key(section, key)
633 639 if ui:
634 640 if section in ('hooks', 'extensions'):
635 641 result[result_key] = ui.ui_active
636 642 else:
637 643 result[result_key] = ui.ui_value
638 644
639 645 for name in self.GENERAL_SETTINGS:
640 646 setting = settings.get_setting_by_name(name)
641 647 if setting:
642 648 result_key = 'rhodecode_{}'.format(name)
643 649 result[result_key] = setting.app_settings_value
644 650
645 651 return result
646 652
647 653 def _get_form_ui_key(self, section, key):
648 654 return '{section}_{key}'.format(
649 655 section=section, key=key.replace('.', '_'))
650 656
651 657 def _create_or_update_ui(
652 658 self, settings, section, key, value=None, active=None):
653 659 ui = settings.get_ui_by_section_and_key(section, key)
654 660 if not ui:
655 661 active = True if active is None else active
656 662 settings.create_ui_section_value(
657 663 section, value, key=key, active=active)
658 664 else:
659 665 if active is not None:
660 666 ui.ui_active = active
661 667 if value is not None:
662 668 ui.ui_value = value
663 669 Session().add(ui)
664 670
665 671 def _create_svn_settings(self, settings, data):
666 672 svn_settings = {
667 673 'new_svn_branch': self.SVN_BRANCH_SECTION,
668 674 'new_svn_tag': self.SVN_TAG_SECTION
669 675 }
670 676 for key in svn_settings:
671 677 if data.get(key):
672 678 settings.create_ui_section_value(svn_settings[key], data[key])
673 679
674 680 def _create_or_update_general_settings(self, settings, data):
675 681 for name in self.GENERAL_SETTINGS:
676 682 data_key = 'rhodecode_{}'.format(name)
677 683 if data_key not in data:
678 684 raise ValueError(
679 685 'The given data does not contain {} key'.format(data_key))
680 686 setting = settings.create_or_update_setting(
681 687 name, data[data_key], 'bool')
682 688 Session().add(setting)
683 689
684 690 def _get_hg_settings(self, settings, data):
685 691 data_keys = [self._get_form_ui_key(*s) for s in settings]
686 692 for data_key in data_keys:
687 693 if data_key not in data:
688 694 raise ValueError(
689 695 'The given data does not contain {} key'.format(data_key))
690 696 return data_keys
General Comments 0
You need to be logged in to leave comments. Login now