##// END OF EJS Templates
admin: Move admin settings navigation to admin module.
Martin Bornhold -
r295:392597a3 default
parent child Browse files
Show More
@@ -0,0 +1,106 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2016-2016 RhodeCode GmbH
4 #
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21
22 import logging
23 import collections
24 from pylons import url
25 from pylons.i18n.translation import lazy_ugettext
26 from zope.interface import implementer
27
28 import rhodecode
29 from rhodecode.admin.interfaces import IAdminNavigationRegistry
30 from rhodecode.lib.utils2 import str2bool
31
32
33 log = logging.getLogger(__name__)
34
35 NavListEntry = collections.namedtuple('NavListEntry', ['key', 'name', 'url'])
36
37
38 class NavEntry(object):
39
40 def __init__(self, key, name, view_name, pyramid=False):
41 self.key = key
42 self.name = name
43 self.view_name = view_name
44 self.pyramid = pyramid
45
46 def generate_url(self, request):
47 if self.pyramid:
48 if hasattr(request, 'route_path'):
49 return request.route_path(self.view_name)
50 else:
51 # TODO: johbo: Remove this after migrating to pyramid.
52 # We need the pyramid request here to generate URLs to pyramid
53 # views from within pylons views.
54 from pyramid.threadlocal import get_current_request
55 pyramid_request = get_current_request()
56 return pyramid_request.route_path(self.view_name)
57 else:
58 return url(self.view_name)
59
60
61 @implementer(IAdminNavigationRegistry)
62 class NavigationRegistry(object):
63
64 _base_entries = [
65 NavEntry('global', lazy_ugettext('Global'), 'admin_settings_global'),
66 NavEntry('vcs', lazy_ugettext('VCS'), 'admin_settings_vcs'),
67 NavEntry('visual', lazy_ugettext('Visual'), 'admin_settings_visual'),
68 NavEntry('mapping', lazy_ugettext('Remap and Rescan'),
69 'admin_settings_mapping'),
70 NavEntry('issuetracker', lazy_ugettext('Issue Tracker'),
71 'admin_settings_issuetracker'),
72 NavEntry('email', lazy_ugettext('Email'), 'admin_settings_email'),
73 NavEntry('hooks', lazy_ugettext('Hooks'), 'admin_settings_hooks'),
74 NavEntry('search', lazy_ugettext('Full Text Search'),
75 'admin_settings_search'),
76 NavEntry('system', lazy_ugettext('System Info'),
77 'admin_settings_system'),
78 NavEntry('open_source', lazy_ugettext('Open Source Licenses'),
79 'admin_settings_open_source', pyramid=True),
80 # TODO: marcink: we disable supervisor now until the supervisor stats
81 # page is fixed in the nix configuration
82 # NavEntry('supervisor', lazy_ugettext('Supervisor'),
83 # 'admin_settings_supervisor'),
84 ]
85
86 def __init__(self):
87 self._registered_entries = collections.OrderedDict([
88 (item.key, item) for item in self.__class__._base_entries
89 ])
90
91 # Add the labs entry when it's activated.
92 labs_active = str2bool(
93 rhodecode.CONFIG.get('labs_settings_active', 'false'))
94 if labs_active:
95 self.add_entry(
96 NavEntry('labs', lazy_ugettext('Labs'), 'admin_settings_labs'))
97
98 def add_entry(self, entry):
99 self._registered_entries[entry.key] = entry
100
101 def get_navlist(self, request):
102 navlist = [NavListEntry(i.key, i.name, i.generate_url(request))
103 for i in self._registered_entries.values()]
104 return navlist
105
106 navigation = NavigationRegistry()
@@ -1,885 +1,813 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 from rhodecode.admin.navigation import navigation
40 41 from rhodecode.lib import auth
41 42 from rhodecode.lib import helpers as h
42 43 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
43 44 from rhodecode.lib.base import BaseController, render
44 45 from rhodecode.lib.celerylib import tasks, run_task
45 46 from rhodecode.lib.utils import repo2db_mapper
46 47 from rhodecode.lib.utils2 import (
47 48 str2bool, safe_unicode, AttributeDict, safe_int)
48 49 from rhodecode.lib.compat import OrderedDict
49 50 from rhodecode.lib.ext_json import json
50 51 from rhodecode.lib.utils import jsonify
51 52
52 from rhodecode.model.db import RhodeCodeUi, Repository, User
53 from rhodecode.model.db import RhodeCodeUi, Repository
53 54 from rhodecode.model.forms import ApplicationSettingsForm, \
54 55 ApplicationUiSettingsForm, ApplicationVisualisationForm, \
55 56 LabsSettingsForm, IssueTrackerPatternsForm
56 57
57 58 from rhodecode.model.scm import ScmModel
58 59 from rhodecode.model.notification import EmailNotificationModel
59 60 from rhodecode.model.meta import Session
60 61 from rhodecode.model.settings import (
61 62 IssueTrackerSettingsModel, VcsSettingsModel, SettingNotFound,
62 63 SettingsModel)
63 64
64 65 from rhodecode.model.supervisor import SupervisorModel, SUPERVISOR_MASTER
65 66
66 67
67 68 log = logging.getLogger(__name__)
68 69
69 70
70 71 class SettingsController(BaseController):
71 72 """REST Controller styled on the Atom Publishing Protocol"""
72 73 # To properly map this controller, ensure your config/routing.py
73 74 # file has a resource setup:
74 75 # map.resource('setting', 'settings', controller='admin/settings',
75 76 # path_prefix='/admin', name_prefix='admin_')
76 77
77 78 @LoginRequired()
78 79 def __before__(self):
79 80 super(SettingsController, self).__before__()
80 81 c.labs_active = str2bool(
81 82 rhodecode.CONFIG.get('labs_settings_active', 'false'))
82 83 c.navlist = navigation.get_navlist(request)
83 84
84 85 def _get_hg_ui_settings(self):
85 86 ret = RhodeCodeUi.query().all()
86 87
87 88 if not ret:
88 89 raise Exception('Could not get application ui settings !')
89 90 settings = {}
90 91 for each in ret:
91 92 k = each.ui_key
92 93 v = each.ui_value
93 94 if k == '/':
94 95 k = 'root_path'
95 96
96 97 if k in ['push_ssl', 'publish']:
97 98 v = str2bool(v)
98 99
99 100 if k.find('.') != -1:
100 101 k = k.replace('.', '_')
101 102
102 103 if each.ui_section in ['hooks', 'extensions']:
103 104 v = each.ui_active
104 105
105 106 settings[each.ui_section + '_' + k] = v
106 107 return settings
107 108
108 109 @HasPermissionAllDecorator('hg.admin')
109 110 @auth.CSRFRequired()
110 111 @jsonify
111 112 def delete_svn_pattern(self):
112 113 if not request.is_xhr:
113 114 raise HTTPBadRequest()
114 115
115 116 delete_pattern_id = request.POST.get('delete_svn_pattern')
116 117 model = VcsSettingsModel()
117 118 try:
118 119 model.delete_global_svn_pattern(delete_pattern_id)
119 120 except SettingNotFound:
120 121 raise HTTPBadRequest()
121 122
122 123 Session().commit()
123 124 return True
124 125
125 126 @HasPermissionAllDecorator('hg.admin')
126 127 @auth.CSRFRequired()
127 128 def settings_vcs_update(self):
128 129 """POST /admin/settings: All items in the collection"""
129 130 # url('admin_settings_vcs')
130 131 c.active = 'vcs'
131 132
132 133 model = VcsSettingsModel()
133 134 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
134 135 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
135 136
136 137 application_form = ApplicationUiSettingsForm()()
137 138 try:
138 139 form_result = application_form.to_python(dict(request.POST))
139 140 except formencode.Invalid as errors:
140 141 h.flash(
141 142 _("Some form inputs contain invalid data."),
142 143 category='error')
143 144 return htmlfill.render(
144 145 render('admin/settings/settings.html'),
145 146 defaults=errors.value,
146 147 errors=errors.error_dict or {},
147 148 prefix_error=False,
148 149 encoding="UTF-8",
149 150 force_defaults=False
150 151 )
151 152
152 153 try:
153 154 model.update_global_ssl_setting(form_result['web_push_ssl'])
154 155 if c.visual.allow_repo_location_change:
155 156 model.update_global_path_setting(
156 157 form_result['paths_root_path'])
157 158 model.update_global_hook_settings(form_result)
158 159 model.create_global_svn_settings(form_result)
159 160 model.create_or_update_global_hg_settings(form_result)
160 161 model.create_or_update_global_pr_settings(form_result)
161 162 except Exception:
162 163 log.exception("Exception while updating settings")
163 164 h.flash(_('Error occurred during updating '
164 165 'application settings'), category='error')
165 166 else:
166 167 Session().commit()
167 168 h.flash(_('Updated VCS settings'), category='success')
168 169 return redirect(url('admin_settings_vcs'))
169 170
170 171 return htmlfill.render(
171 172 render('admin/settings/settings.html'),
172 173 defaults=self._form_defaults(),
173 174 encoding="UTF-8",
174 175 force_defaults=False)
175 176
176 177 @HasPermissionAllDecorator('hg.admin')
177 178 def settings_vcs(self):
178 179 """GET /admin/settings: All items in the collection"""
179 180 # url('admin_settings_vcs')
180 181 c.active = 'vcs'
181 182 model = VcsSettingsModel()
182 183 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
183 184 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
184 185
185 186 return htmlfill.render(
186 187 render('admin/settings/settings.html'),
187 188 defaults=self._form_defaults(),
188 189 encoding="UTF-8",
189 190 force_defaults=False)
190 191
191 192 @HasPermissionAllDecorator('hg.admin')
192 193 @auth.CSRFRequired()
193 194 def settings_mapping_update(self):
194 195 """POST /admin/settings/mapping: All items in the collection"""
195 196 # url('admin_settings_mapping')
196 197 c.active = 'mapping'
197 198 rm_obsolete = request.POST.get('destroy', False)
198 199 invalidate_cache = request.POST.get('invalidate', False)
199 200 log.debug(
200 201 'rescanning repo location with destroy obsolete=%s', rm_obsolete)
201 202
202 203 if invalidate_cache:
203 204 log.debug('invalidating all repositories cache')
204 205 for repo in Repository.get_all():
205 206 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
206 207
207 208 filesystem_repos = ScmModel().repo_scan()
208 209 added, removed = repo2db_mapper(filesystem_repos, rm_obsolete)
209 210 _repr = lambda l: ', '.join(map(safe_unicode, l)) or '-'
210 211 h.flash(_('Repositories successfully '
211 212 'rescanned added: %s ; removed: %s') %
212 213 (_repr(added), _repr(removed)),
213 214 category='success')
214 215 return redirect(url('admin_settings_mapping'))
215 216
216 217 @HasPermissionAllDecorator('hg.admin')
217 218 def settings_mapping(self):
218 219 """GET /admin/settings/mapping: All items in the collection"""
219 220 # url('admin_settings_mapping')
220 221 c.active = 'mapping'
221 222
222 223 return htmlfill.render(
223 224 render('admin/settings/settings.html'),
224 225 defaults=self._form_defaults(),
225 226 encoding="UTF-8",
226 227 force_defaults=False)
227 228
228 229 @HasPermissionAllDecorator('hg.admin')
229 230 @auth.CSRFRequired()
230 231 def settings_global_update(self):
231 232 """POST /admin/settings/global: All items in the collection"""
232 233 # url('admin_settings_global')
233 234 c.active = 'global'
234 235 application_form = ApplicationSettingsForm()()
235 236 try:
236 237 form_result = application_form.to_python(dict(request.POST))
237 238 except formencode.Invalid as errors:
238 239 return htmlfill.render(
239 240 render('admin/settings/settings.html'),
240 241 defaults=errors.value,
241 242 errors=errors.error_dict or {},
242 243 prefix_error=False,
243 244 encoding="UTF-8",
244 245 force_defaults=False)
245 246
246 247 try:
247 248 settings = [
248 249 ('title', 'rhodecode_title'),
249 250 ('realm', 'rhodecode_realm'),
250 251 ('pre_code', 'rhodecode_pre_code'),
251 252 ('post_code', 'rhodecode_post_code'),
252 253 ('captcha_public_key', 'rhodecode_captcha_public_key'),
253 254 ('captcha_private_key', 'rhodecode_captcha_private_key'),
254 255 ]
255 256 for setting, form_key in settings:
256 257 sett = SettingsModel().create_or_update_setting(
257 258 setting, form_result[form_key])
258 259 Session().add(sett)
259 260
260 261 Session().commit()
261 262 SettingsModel().invalidate_settings_cache()
262 263 h.flash(_('Updated application settings'), category='success')
263 264 except Exception:
264 265 log.exception("Exception while updating application settings")
265 266 h.flash(
266 267 _('Error occurred during updating application settings'),
267 268 category='error')
268 269
269 270 return redirect(url('admin_settings_global'))
270 271
271 272 @HasPermissionAllDecorator('hg.admin')
272 273 def settings_global(self):
273 274 """GET /admin/settings/global: All items in the collection"""
274 275 # url('admin_settings_global')
275 276 c.active = 'global'
276 277
277 278 return htmlfill.render(
278 279 render('admin/settings/settings.html'),
279 280 defaults=self._form_defaults(),
280 281 encoding="UTF-8",
281 282 force_defaults=False)
282 283
283 284 @HasPermissionAllDecorator('hg.admin')
284 285 @auth.CSRFRequired()
285 286 def settings_visual_update(self):
286 287 """POST /admin/settings/visual: All items in the collection"""
287 288 # url('admin_settings_visual')
288 289 c.active = 'visual'
289 290 application_form = ApplicationVisualisationForm()()
290 291 try:
291 292 form_result = application_form.to_python(dict(request.POST))
292 293 except formencode.Invalid as errors:
293 294 return htmlfill.render(
294 295 render('admin/settings/settings.html'),
295 296 defaults=errors.value,
296 297 errors=errors.error_dict or {},
297 298 prefix_error=False,
298 299 encoding="UTF-8",
299 300 force_defaults=False
300 301 )
301 302
302 303 try:
303 304 settings = [
304 305 ('show_public_icon', 'rhodecode_show_public_icon', 'bool'),
305 306 ('show_private_icon', 'rhodecode_show_private_icon', 'bool'),
306 307 ('stylify_metatags', 'rhodecode_stylify_metatags', 'bool'),
307 308 ('repository_fields', 'rhodecode_repository_fields', 'bool'),
308 309 ('dashboard_items', 'rhodecode_dashboard_items', 'int'),
309 310 ('admin_grid_items', 'rhodecode_admin_grid_items', 'int'),
310 311 ('show_version', 'rhodecode_show_version', 'bool'),
311 312 ('use_gravatar', 'rhodecode_use_gravatar', 'bool'),
312 313 ('markup_renderer', 'rhodecode_markup_renderer', 'unicode'),
313 314 ('gravatar_url', 'rhodecode_gravatar_url', 'unicode'),
314 315 ('clone_uri_tmpl', 'rhodecode_clone_uri_tmpl', 'unicode'),
315 316 ('support_url', 'rhodecode_support_url', 'unicode'),
316 317 ('show_revision_number', 'rhodecode_show_revision_number', 'bool'),
317 318 ('show_sha_length', 'rhodecode_show_sha_length', 'int'),
318 319 ]
319 320 for setting, form_key, type_ in settings:
320 321 sett = SettingsModel().create_or_update_setting(
321 322 setting, form_result[form_key], type_)
322 323 Session().add(sett)
323 324
324 325 Session().commit()
325 326 SettingsModel().invalidate_settings_cache()
326 327 h.flash(_('Updated visualisation settings'), category='success')
327 328 except Exception:
328 329 log.exception("Exception updating visualization settings")
329 330 h.flash(_('Error occurred during updating '
330 331 'visualisation settings'),
331 332 category='error')
332 333
333 334 return redirect(url('admin_settings_visual'))
334 335
335 336 @HasPermissionAllDecorator('hg.admin')
336 337 def settings_visual(self):
337 338 """GET /admin/settings/visual: All items in the collection"""
338 339 # url('admin_settings_visual')
339 340 c.active = 'visual'
340 341
341 342 return htmlfill.render(
342 343 render('admin/settings/settings.html'),
343 344 defaults=self._form_defaults(),
344 345 encoding="UTF-8",
345 346 force_defaults=False)
346 347
347 348 @HasPermissionAllDecorator('hg.admin')
348 349 @auth.CSRFRequired()
349 350 def settings_issuetracker_test(self):
350 351 if request.is_xhr:
351 352 return h.urlify_commit_message(
352 353 request.POST.get('test_text', ''),
353 354 'repo_group/test_repo1')
354 355 else:
355 356 raise HTTPBadRequest()
356 357
357 358 @HasPermissionAllDecorator('hg.admin')
358 359 @auth.CSRFRequired()
359 360 def settings_issuetracker_delete(self):
360 361 uid = request.POST.get('uid')
361 362 IssueTrackerSettingsModel().delete_entries(uid)
362 363 h.flash(_('Removed issue tracker entry'), category='success')
363 364 return redirect(url('admin_settings_issuetracker'))
364 365
365 366 @HasPermissionAllDecorator('hg.admin')
366 367 def settings_issuetracker(self):
367 368 """GET /admin/settings/issue-tracker: All items in the collection"""
368 369 # url('admin_settings_issuetracker')
369 370 c.active = 'issuetracker'
370 371 defaults = SettingsModel().get_all_settings()
371 372
372 373 entry_key = 'rhodecode_issuetracker_pat_'
373 374
374 375 c.issuetracker_entries = {}
375 376 for k, v in defaults.items():
376 377 if k.startswith(entry_key):
377 378 uid = k[len(entry_key):]
378 379 c.issuetracker_entries[uid] = None
379 380
380 381 for uid in c.issuetracker_entries:
381 382 c.issuetracker_entries[uid] = AttributeDict({
382 383 'pat': defaults.get('rhodecode_issuetracker_pat_' + uid),
383 384 'url': defaults.get('rhodecode_issuetracker_url_' + uid),
384 385 'pref': defaults.get('rhodecode_issuetracker_pref_' + uid),
385 386 'desc': defaults.get('rhodecode_issuetracker_desc_' + uid),
386 387 })
387 388
388 389 return render('admin/settings/settings.html')
389 390
390 391 @HasPermissionAllDecorator('hg.admin')
391 392 @auth.CSRFRequired()
392 393 def settings_issuetracker_save(self):
393 394 settings_model = IssueTrackerSettingsModel()
394 395
395 396 form = IssueTrackerPatternsForm()().to_python(request.POST)
396 397 for uid in form['delete_patterns']:
397 398 settings_model.delete_entries(uid)
398 399
399 400 for pattern in form['patterns']:
400 401 for setting, value, type_ in pattern:
401 402 sett = settings_model.create_or_update_setting(
402 403 setting, value, type_)
403 404 Session().add(sett)
404 405
405 406 Session().commit()
406 407
407 408 SettingsModel().invalidate_settings_cache()
408 409 h.flash(_('Updated issue tracker entries'), category='success')
409 410 return redirect(url('admin_settings_issuetracker'))
410 411
411 412 @HasPermissionAllDecorator('hg.admin')
412 413 @auth.CSRFRequired()
413 414 def settings_email_update(self):
414 415 """POST /admin/settings/email: All items in the collection"""
415 416 # url('admin_settings_email')
416 417 c.active = 'email'
417 418
418 419 test_email = request.POST.get('test_email')
419 420
420 421 if not test_email:
421 422 h.flash(_('Please enter email address'), category='error')
422 423 return redirect(url('admin_settings_email'))
423 424
424 425 email_kwargs = {
425 426 'date': datetime.datetime.now(),
426 427 'user': c.rhodecode_user,
427 428 'rhodecode_version': c.rhodecode_version
428 429 }
429 430
430 431 (subject, headers, email_body,
431 432 email_body_plaintext) = EmailNotificationModel().render_email(
432 433 EmailNotificationModel.TYPE_EMAIL_TEST, **email_kwargs)
433 434
434 435 recipients = [test_email] if test_email else None
435 436
436 437 run_task(tasks.send_email, recipients, subject,
437 438 email_body_plaintext, email_body)
438 439
439 440 h.flash(_('Send email task created'), category='success')
440 441 return redirect(url('admin_settings_email'))
441 442
442 443 @HasPermissionAllDecorator('hg.admin')
443 444 def settings_email(self):
444 445 """GET /admin/settings/email: All items in the collection"""
445 446 # url('admin_settings_email')
446 447 c.active = 'email'
447 448 c.rhodecode_ini = rhodecode.CONFIG
448 449
449 450 return htmlfill.render(
450 451 render('admin/settings/settings.html'),
451 452 defaults=self._form_defaults(),
452 453 encoding="UTF-8",
453 454 force_defaults=False)
454 455
455 456 @HasPermissionAllDecorator('hg.admin')
456 457 @auth.CSRFRequired()
457 458 def settings_hooks_update(self):
458 459 """POST or DELETE /admin/settings/hooks: All items in the collection"""
459 460 # url('admin_settings_hooks')
460 461 c.active = 'hooks'
461 462 if c.visual.allow_custom_hooks_settings:
462 463 ui_key = request.POST.get('new_hook_ui_key')
463 464 ui_value = request.POST.get('new_hook_ui_value')
464 465
465 466 hook_id = request.POST.get('hook_id')
466 467 new_hook = False
467 468
468 469 model = SettingsModel()
469 470 try:
470 471 if ui_value and ui_key:
471 472 model.create_or_update_hook(ui_key, ui_value)
472 473 h.flash(_('Added new hook'), category='success')
473 474 new_hook = True
474 475 elif hook_id:
475 476 RhodeCodeUi.delete(hook_id)
476 477 Session().commit()
477 478
478 479 # check for edits
479 480 update = False
480 481 _d = request.POST.dict_of_lists()
481 482 for k, v in zip(_d.get('hook_ui_key', []),
482 483 _d.get('hook_ui_value_new', [])):
483 484 model.create_or_update_hook(k, v)
484 485 update = True
485 486
486 487 if update and not new_hook:
487 488 h.flash(_('Updated hooks'), category='success')
488 489 Session().commit()
489 490 except Exception:
490 491 log.exception("Exception during hook creation")
491 492 h.flash(_('Error occurred during hook creation'),
492 493 category='error')
493 494
494 495 return redirect(url('admin_settings_hooks'))
495 496
496 497 @HasPermissionAllDecorator('hg.admin')
497 498 def settings_hooks(self):
498 499 """GET /admin/settings/hooks: All items in the collection"""
499 500 # url('admin_settings_hooks')
500 501 c.active = 'hooks'
501 502
502 503 model = SettingsModel()
503 504 c.hooks = model.get_builtin_hooks()
504 505 c.custom_hooks = model.get_custom_hooks()
505 506
506 507 return htmlfill.render(
507 508 render('admin/settings/settings.html'),
508 509 defaults=self._form_defaults(),
509 510 encoding="UTF-8",
510 511 force_defaults=False)
511 512
512 513 @HasPermissionAllDecorator('hg.admin')
513 514 def settings_search(self):
514 515 """GET /admin/settings/search: All items in the collection"""
515 516 # url('admin_settings_search')
516 517 c.active = 'search'
517 518
518 519 from rhodecode.lib.index import searcher_from_config
519 520 searcher = searcher_from_config(config)
520 521 c.statistics = searcher.statistics()
521 522
522 523 return render('admin/settings/settings.html')
523 524
524 525 @HasPermissionAllDecorator('hg.admin')
525 526 def settings_system(self):
526 527 """GET /admin/settings/system: All items in the collection"""
527 528 # url('admin_settings_system')
528 529 snapshot = str2bool(request.GET.get('snapshot'))
529 530 c.active = 'system'
530 531
531 532 defaults = self._form_defaults()
532 533 c.rhodecode_ini = rhodecode.CONFIG
533 534 c.rhodecode_update_url = defaults.get('rhodecode_update_url')
534 535 server_info = ScmModel().get_server_info(request.environ)
535 536 for key, val in server_info.iteritems():
536 537 setattr(c, key, val)
537 538
538 539 if c.disk['percent'] > 90:
539 540 h.flash(h.literal(_(
540 541 'Critical: your disk space is very low <b>%s%%</b> used' %
541 542 c.disk['percent'])), 'error')
542 543 elif c.disk['percent'] > 70:
543 544 h.flash(h.literal(_(
544 545 'Warning: your disk space is running low <b>%s%%</b> used' %
545 546 c.disk['percent'])), 'warning')
546 547
547 548 try:
548 549 c.uptime_age = h._age(
549 550 h.time_to_datetime(c.boot_time), False, show_suffix=False)
550 551 except TypeError:
551 552 c.uptime_age = c.boot_time
552 553
553 554 try:
554 555 c.system_memory = '%s/%s, %s%% (%s%%) used%s' % (
555 556 h.format_byte_size_binary(c.memory['used']),
556 557 h.format_byte_size_binary(c.memory['total']),
557 558 c.memory['percent2'],
558 559 c.memory['percent'],
559 560 ' %s' % c.memory['error'] if 'error' in c.memory else '')
560 561 except TypeError:
561 562 c.system_memory = 'NOT AVAILABLE'
562 563
563 564 rhodecode_ini_safe = rhodecode.CONFIG.copy()
564 565 blacklist = [
565 566 'rhodecode_license_key',
566 567 'routes.map',
567 568 'pylons.h',
568 569 'pylons.app_globals',
569 570 'pylons.environ_config',
570 571 'sqlalchemy.db1.url',
571 572 ('app_conf', 'sqlalchemy.db1.url')
572 573 ]
573 574 for k in blacklist:
574 575 if isinstance(k, tuple):
575 576 section, key = k
576 577 if section in rhodecode_ini_safe:
577 578 rhodecode_ini_safe[section].pop(key, None)
578 579 else:
579 580 rhodecode_ini_safe.pop(k, None)
580 581
581 582 c.rhodecode_ini_safe = rhodecode_ini_safe
582 583
583 584 # TODO: marcink, figure out how to allow only selected users to do this
584 585 c.allowed_to_snapshot = False
585 586
586 587 if snapshot:
587 588 if c.allowed_to_snapshot:
588 589 return render('admin/settings/settings_system_snapshot.html')
589 590 else:
590 591 h.flash('You are not allowed to do this', category='warning')
591 592
592 593 return htmlfill.render(
593 594 render('admin/settings/settings.html'),
594 595 defaults=defaults,
595 596 encoding="UTF-8",
596 597 force_defaults=False)
597 598
598 599 @staticmethod
599 600 def get_update_data(update_url):
600 601 """Return the JSON update data."""
601 602 ver = rhodecode.__version__
602 603 log.debug('Checking for upgrade on `%s` server', update_url)
603 604 opener = urllib2.build_opener()
604 605 opener.addheaders = [('User-agent', 'RhodeCode-SCM/%s' % ver)]
605 606 response = opener.open(update_url)
606 607 response_data = response.read()
607 608 data = json.loads(response_data)
608 609
609 610 return data
610 611
611 612 @HasPermissionAllDecorator('hg.admin')
612 613 def settings_system_update(self):
613 614 """GET /admin/settings/system/updates: All items in the collection"""
614 615 # url('admin_settings_system_update')
615 616 defaults = self._form_defaults()
616 617 update_url = defaults.get('rhodecode_update_url', '')
617 618
618 619 _err = lambda s: '<div style="color:#ff8888; padding:4px 0px">%s</div>' % (s)
619 620 try:
620 621 data = self.get_update_data(update_url)
621 622 except urllib2.URLError as e:
622 623 log.exception("Exception contacting upgrade server")
623 624 return _err('Failed to contact upgrade server: %r' % e)
624 625 except ValueError as e:
625 626 log.exception("Bad data sent from update server")
626 627 return _err('Bad data sent from update server')
627 628
628 629 latest = data['versions'][0]
629 630
630 631 c.update_url = update_url
631 632 c.latest_data = latest
632 633 c.latest_ver = latest['version']
633 634 c.cur_ver = rhodecode.__version__
634 635 c.should_upgrade = False
635 636
636 637 if (packaging.version.Version(c.latest_ver) >
637 638 packaging.version.Version(c.cur_ver)):
638 639 c.should_upgrade = True
639 640 c.important_notices = latest['general']
640 641
641 642 return render('admin/settings/settings_system_update.html')
642 643
643 644 @HasPermissionAllDecorator('hg.admin')
644 645 def settings_supervisor(self):
645 646 c.rhodecode_ini = rhodecode.CONFIG
646 647 c.active = 'supervisor'
647 648
648 649 c.supervisor_procs = OrderedDict([
649 650 (SUPERVISOR_MASTER, {}),
650 651 ])
651 652
652 653 c.log_size = 10240
653 654 supervisor = SupervisorModel()
654 655
655 656 _connection = supervisor.get_connection(
656 657 c.rhodecode_ini.get('supervisor.uri'))
657 658 c.connection_error = None
658 659 try:
659 660 _connection.supervisor.getAllProcessInfo()
660 661 except Exception as e:
661 662 c.connection_error = str(e)
662 663 log.exception("Exception reading supervisor data")
663 664 return render('admin/settings/settings.html')
664 665
665 666 groupid = c.rhodecode_ini.get('supervisor.group_id')
666 667
667 668 # feed our group processes to the main
668 669 for proc in supervisor.get_group_processes(_connection, groupid):
669 670 c.supervisor_procs[proc['name']] = {}
670 671
671 672 for k in c.supervisor_procs.keys():
672 673 try:
673 674 # master process info
674 675 if k == SUPERVISOR_MASTER:
675 676 _data = supervisor.get_master_state(_connection)
676 677 _data['name'] = 'supervisor master'
677 678 _data['description'] = 'pid %s, id: %s, ver: %s' % (
678 679 _data['pid'], _data['id'], _data['ver'])
679 680 c.supervisor_procs[k] = _data
680 681 else:
681 682 procid = groupid + ":" + k
682 683 c.supervisor_procs[k] = supervisor.get_process_info(_connection, procid)
683 684 except Exception as e:
684 685 log.exception("Exception reading supervisor data")
685 686 c.supervisor_procs[k] = {'_rhodecode_error': str(e)}
686 687
687 688 return render('admin/settings/settings.html')
688 689
689 690 @HasPermissionAllDecorator('hg.admin')
690 691 def settings_supervisor_log(self, procid):
691 692 import rhodecode
692 693 c.rhodecode_ini = rhodecode.CONFIG
693 694 c.active = 'supervisor_tail'
694 695
695 696 supervisor = SupervisorModel()
696 697 _connection = supervisor.get_connection(c.rhodecode_ini.get('supervisor.uri'))
697 698 groupid = c.rhodecode_ini.get('supervisor.group_id')
698 699 procid = groupid + ":" + procid if procid != SUPERVISOR_MASTER else procid
699 700
700 701 c.log_size = 10240
701 702 offset = abs(safe_int(request.GET.get('offset', c.log_size))) * -1
702 703 c.log = supervisor.read_process_log(_connection, procid, offset, 0)
703 704
704 705 return render('admin/settings/settings.html')
705 706
706 707 @HasPermissionAllDecorator('hg.admin')
707 708 @auth.CSRFRequired()
708 709 def settings_labs_update(self):
709 710 """POST /admin/settings/labs: All items in the collection"""
710 711 # url('admin_settings/labs', method={'POST'})
711 712 c.active = 'labs'
712 713
713 714 application_form = LabsSettingsForm()()
714 715 try:
715 716 form_result = application_form.to_python(dict(request.POST))
716 717 except formencode.Invalid as errors:
717 718 h.flash(
718 719 _('Some form inputs contain invalid data.'),
719 720 category='error')
720 721 return htmlfill.render(
721 722 render('admin/settings/settings.html'),
722 723 defaults=errors.value,
723 724 errors=errors.error_dict or {},
724 725 prefix_error=False,
725 726 encoding='UTF-8',
726 727 force_defaults=False
727 728 )
728 729
729 730 try:
730 731 session = Session()
731 732 for setting in _LAB_SETTINGS:
732 733 setting_name = setting.key[len('rhodecode_'):]
733 734 sett = SettingsModel().create_or_update_setting(
734 735 setting_name, form_result[setting.key], setting.type)
735 736 session.add(sett)
736 737
737 738 except Exception:
738 739 log.exception('Exception while updating lab settings')
739 740 h.flash(_('Error occurred during updating labs settings'),
740 741 category='error')
741 742 else:
742 743 Session().commit()
743 744 SettingsModel().invalidate_settings_cache()
744 745 h.flash(_('Updated Labs settings'), category='success')
745 746 return redirect(url('admin_settings_labs'))
746 747
747 748 return htmlfill.render(
748 749 render('admin/settings/settings.html'),
749 750 defaults=self._form_defaults(),
750 751 encoding='UTF-8',
751 752 force_defaults=False)
752 753
753 754 @HasPermissionAllDecorator('hg.admin')
754 755 def settings_labs(self):
755 756 """GET /admin/settings/labs: All items in the collection"""
756 757 # url('admin_settings_labs')
757 758 if not c.labs_active:
758 759 redirect(url('admin_settings'))
759 760
760 761 c.active = 'labs'
761 762 c.lab_settings = _LAB_SETTINGS
762 763
763 764 return htmlfill.render(
764 765 render('admin/settings/settings.html'),
765 766 defaults=self._form_defaults(),
766 767 encoding='UTF-8',
767 768 force_defaults=False)
768 769
769 770 def _form_defaults(self):
770 771 defaults = SettingsModel().get_all_settings()
771 772 defaults.update(self._get_hg_ui_settings())
772 773 defaults.update({
773 774 'new_svn_branch': '',
774 775 'new_svn_tag': '',
775 776 })
776 777 return defaults
777 778
778 779
779 780 # :param key: name of the setting including the 'rhodecode_' prefix
780 781 # :param type: the RhodeCodeSetting type to use.
781 782 # :param group: the i18ned group in which we should dispaly this setting
782 783 # :param label: the i18ned label we should display for this setting
783 784 # :param help: the i18ned help we should dispaly for this setting
784 785 LabSetting = collections.namedtuple(
785 786 'LabSetting', ('key', 'type', 'group', 'label', 'help'))
786 787
787 788
788 789 # This list has to be kept in sync with the form
789 790 # rhodecode.model.forms.LabsSettingsForm.
790 791 _LAB_SETTINGS = [
791 792 LabSetting(
792 793 key='rhodecode_hg_use_rebase_for_merging',
793 794 type='bool',
794 795 group=lazy_ugettext('Mercurial server-side merge'),
795 796 label=lazy_ugettext('Use rebase instead of creating a merge commit when merging via web interface'),
796 797 help='' # Do not translate the empty string!
797 798 ),
798 799 LabSetting(
799 800 key='rhodecode_proxy_subversion_http_requests',
800 801 type='bool',
801 802 group=lazy_ugettext('Subversion HTTP Support'),
802 803 label=lazy_ugettext('Proxy subversion HTTP requests'),
803 804 help='' # Do not translate the empty string!
804 805 ),
805 806 LabSetting(
806 807 key='rhodecode_subversion_http_server_url',
807 808 type='str',
808 809 group=lazy_ugettext('Subversion HTTP Server URL'),
809 810 label='', # Do not translate the empty string!
810 811 help=lazy_ugettext('e.g. http://localhost:8080/')
811 812 ),
812 813 ]
813
814
815 NavListEntry = collections.namedtuple('NavListEntry', ['key', 'name', 'url'])
816
817
818 class NavEntry(object):
819
820 def __init__(self, key, name, view_name, pyramid=False):
821 self.key = key
822 self.name = name
823 self.view_name = view_name
824 self.pyramid = pyramid
825
826 def generate_url(self, request):
827 if self.pyramid:
828 if hasattr(request, 'route_path'):
829 return request.route_path(self.view_name)
830 else:
831 # TODO: johbo: Remove this after migrating to pyramid.
832 # We need the pyramid request here to generate URLs to pyramid
833 # views from within pylons views.
834 from pyramid.threadlocal import get_current_request
835 pyramid_request = get_current_request()
836 return pyramid_request.route_path(self.view_name)
837 else:
838 return url(self.view_name)
839
840
841 class NavigationRegistry(object):
842
843 _base_entries = [
844 NavEntry('global', lazy_ugettext('Global'), 'admin_settings_global'),
845 NavEntry('vcs', lazy_ugettext('VCS'), 'admin_settings_vcs'),
846 NavEntry('visual', lazy_ugettext('Visual'), 'admin_settings_visual'),
847 NavEntry('mapping', lazy_ugettext('Remap and Rescan'),
848 'admin_settings_mapping'),
849 NavEntry('issuetracker', lazy_ugettext('Issue Tracker'),
850 'admin_settings_issuetracker'),
851 NavEntry('email', lazy_ugettext('Email'), 'admin_settings_email'),
852 NavEntry('hooks', lazy_ugettext('Hooks'), 'admin_settings_hooks'),
853 NavEntry('search', lazy_ugettext('Full Text Search'),
854 'admin_settings_search'),
855 NavEntry('system', lazy_ugettext('System Info'),
856 'admin_settings_system'),
857 NavEntry('open_source', lazy_ugettext('Open Source Licenses'),
858 'admin_settings_open_source', pyramid=True),
859 # TODO: marcink: we disable supervisor now until the supervisor stats
860 # page is fixed in the nix configuration
861 # NavEntry('supervisor', lazy_ugettext('Supervisor'),
862 # 'admin_settings_supervisor'),
863 ]
864
865 def __init__(self):
866 self._registered_entries = collections.OrderedDict([
867 (item.key, item) for item in self.__class__._base_entries
868 ])
869
870 # Add the labs entry when it's activated.
871 labs_active = str2bool(
872 rhodecode.CONFIG.get('labs_settings_active', 'false'))
873 if labs_active:
874 self.add_entry(
875 NavEntry('labs', lazy_ugettext('Labs'), 'admin_settings_labs'))
876
877 def add_entry(self, entry):
878 self._registered_entries[entry.key] = entry
879
880 def get_navlist(self, request):
881 navlist = [NavListEntry(i.key, i.name, i.generate_url(request))
882 for i in self._registered_entries.values()]
883 return navlist
884
885 navigation = NavigationRegistry()
General Comments 0
You need to be logged in to leave comments. Login now