##// END OF EJS Templates
ux: fix bug where saving empty issue tracker form was causing...
dan -
r1000:dbebf42b default
parent child Browse files
Show More
@@ -1,796 +1,797 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.admin.navigation import navigation_list
41 41 from rhodecode.lib import auth
42 42 from rhodecode.lib import helpers as h
43 43 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
44 44 from rhodecode.lib.base import BaseController, render
45 45 from rhodecode.lib.celerylib import tasks, run_task
46 46 from rhodecode.lib.utils import repo2db_mapper
47 47 from rhodecode.lib.utils2 import (
48 48 str2bool, safe_unicode, AttributeDict, safe_int)
49 49 from rhodecode.lib.compat import OrderedDict
50 50 from rhodecode.lib.ext_json import json
51 51 from rhodecode.lib.utils import jsonify
52 52
53 53 from rhodecode.model.db import RhodeCodeUi, Repository
54 54 from rhodecode.model.forms import ApplicationSettingsForm, \
55 55 ApplicationUiSettingsForm, ApplicationVisualisationForm, \
56 56 LabsSettingsForm, IssueTrackerPatternsForm
57 57
58 58 from rhodecode.model.scm import ScmModel
59 59 from rhodecode.model.notification import EmailNotificationModel
60 60 from rhodecode.model.meta import Session
61 61 from rhodecode.model.settings import (
62 62 IssueTrackerSettingsModel, VcsSettingsModel, SettingNotFound,
63 63 SettingsModel)
64 64
65 65 from rhodecode.model.supervisor import SupervisorModel, SUPERVISOR_MASTER
66 66
67 67
68 68 log = logging.getLogger(__name__)
69 69
70 70
71 71 class SettingsController(BaseController):
72 72 """REST Controller styled on the Atom Publishing Protocol"""
73 73 # To properly map this controller, ensure your config/routing.py
74 74 # file has a resource setup:
75 75 # map.resource('setting', 'settings', controller='admin/settings',
76 76 # path_prefix='/admin', name_prefix='admin_')
77 77
78 78 @LoginRequired()
79 79 def __before__(self):
80 80 super(SettingsController, self).__before__()
81 81 c.labs_active = str2bool(
82 82 rhodecode.CONFIG.get('labs_settings_active', 'true'))
83 83 c.navlist = navigation_list(request)
84 84
85 85 def _get_hg_ui_settings(self):
86 86 ret = RhodeCodeUi.query().all()
87 87
88 88 if not ret:
89 89 raise Exception('Could not get application ui settings !')
90 90 settings = {}
91 91 for each in ret:
92 92 k = each.ui_key
93 93 v = each.ui_value
94 94 if k == '/':
95 95 k = 'root_path'
96 96
97 97 if k in ['push_ssl', 'publish']:
98 98 v = str2bool(v)
99 99
100 100 if k.find('.') != -1:
101 101 k = k.replace('.', '_')
102 102
103 103 if each.ui_section in ['hooks', 'extensions']:
104 104 v = each.ui_active
105 105
106 106 settings[each.ui_section + '_' + k] = v
107 107 return settings
108 108
109 109 @HasPermissionAllDecorator('hg.admin')
110 110 @auth.CSRFRequired()
111 111 @jsonify
112 112 def delete_svn_pattern(self):
113 113 if not request.is_xhr:
114 114 raise HTTPBadRequest()
115 115
116 116 delete_pattern_id = request.POST.get('delete_svn_pattern')
117 117 model = VcsSettingsModel()
118 118 try:
119 119 model.delete_global_svn_pattern(delete_pattern_id)
120 120 except SettingNotFound:
121 121 raise HTTPBadRequest()
122 122
123 123 Session().commit()
124 124 return True
125 125
126 126 @HasPermissionAllDecorator('hg.admin')
127 127 @auth.CSRFRequired()
128 128 def settings_vcs_update(self):
129 129 """POST /admin/settings: All items in the collection"""
130 130 # url('admin_settings_vcs')
131 131 c.active = 'vcs'
132 132
133 133 model = VcsSettingsModel()
134 134 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
135 135 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
136 136
137 137 application_form = ApplicationUiSettingsForm()()
138 138
139 139 try:
140 140 form_result = application_form.to_python(dict(request.POST))
141 141 except formencode.Invalid as errors:
142 142 h.flash(
143 143 _("Some form inputs contain invalid data."),
144 144 category='error')
145 145 return htmlfill.render(
146 146 render('admin/settings/settings.html'),
147 147 defaults=errors.value,
148 148 errors=errors.error_dict or {},
149 149 prefix_error=False,
150 150 encoding="UTF-8",
151 151 force_defaults=False
152 152 )
153 153
154 154 try:
155 155 if c.visual.allow_repo_location_change:
156 156 model.update_global_path_setting(
157 157 form_result['paths_root_path'])
158 158
159 159 model.update_global_ssl_setting(form_result['web_push_ssl'])
160 160 model.update_global_hook_settings(form_result)
161 161
162 162 model.create_or_update_global_svn_settings(form_result)
163 163 model.create_or_update_global_hg_settings(form_result)
164 164 model.create_or_update_global_pr_settings(form_result)
165 165 except Exception:
166 166 log.exception("Exception while updating settings")
167 167 h.flash(_('Error occurred during updating '
168 168 'application settings'), category='error')
169 169 else:
170 170 Session().commit()
171 171 h.flash(_('Updated VCS settings'), category='success')
172 172 return redirect(url('admin_settings_vcs'))
173 173
174 174 return htmlfill.render(
175 175 render('admin/settings/settings.html'),
176 176 defaults=self._form_defaults(),
177 177 encoding="UTF-8",
178 178 force_defaults=False)
179 179
180 180 @HasPermissionAllDecorator('hg.admin')
181 181 def settings_vcs(self):
182 182 """GET /admin/settings: All items in the collection"""
183 183 # url('admin_settings_vcs')
184 184 c.active = 'vcs'
185 185 model = VcsSettingsModel()
186 186 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
187 187 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
188 188
189 189 return htmlfill.render(
190 190 render('admin/settings/settings.html'),
191 191 defaults=self._form_defaults(),
192 192 encoding="UTF-8",
193 193 force_defaults=False)
194 194
195 195 @HasPermissionAllDecorator('hg.admin')
196 196 @auth.CSRFRequired()
197 197 def settings_mapping_update(self):
198 198 """POST /admin/settings/mapping: All items in the collection"""
199 199 # url('admin_settings_mapping')
200 200 c.active = 'mapping'
201 201 rm_obsolete = request.POST.get('destroy', False)
202 202 invalidate_cache = request.POST.get('invalidate', False)
203 203 log.debug(
204 204 'rescanning repo location with destroy obsolete=%s', rm_obsolete)
205 205
206 206 if invalidate_cache:
207 207 log.debug('invalidating all repositories cache')
208 208 for repo in Repository.get_all():
209 209 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
210 210
211 211 filesystem_repos = ScmModel().repo_scan()
212 212 added, removed = repo2db_mapper(filesystem_repos, rm_obsolete)
213 213 _repr = lambda l: ', '.join(map(safe_unicode, l)) or '-'
214 214 h.flash(_('Repositories successfully '
215 215 'rescanned added: %s ; removed: %s') %
216 216 (_repr(added), _repr(removed)),
217 217 category='success')
218 218 return redirect(url('admin_settings_mapping'))
219 219
220 220 @HasPermissionAllDecorator('hg.admin')
221 221 def settings_mapping(self):
222 222 """GET /admin/settings/mapping: All items in the collection"""
223 223 # url('admin_settings_mapping')
224 224 c.active = 'mapping'
225 225
226 226 return htmlfill.render(
227 227 render('admin/settings/settings.html'),
228 228 defaults=self._form_defaults(),
229 229 encoding="UTF-8",
230 230 force_defaults=False)
231 231
232 232 @HasPermissionAllDecorator('hg.admin')
233 233 @auth.CSRFRequired()
234 234 def settings_global_update(self):
235 235 """POST /admin/settings/global: All items in the collection"""
236 236 # url('admin_settings_global')
237 237 c.active = 'global'
238 238 application_form = ApplicationSettingsForm()()
239 239 try:
240 240 form_result = application_form.to_python(dict(request.POST))
241 241 except formencode.Invalid as errors:
242 242 return htmlfill.render(
243 243 render('admin/settings/settings.html'),
244 244 defaults=errors.value,
245 245 errors=errors.error_dict or {},
246 246 prefix_error=False,
247 247 encoding="UTF-8",
248 248 force_defaults=False)
249 249
250 250 try:
251 251 settings = [
252 252 ('title', 'rhodecode_title'),
253 253 ('realm', 'rhodecode_realm'),
254 254 ('pre_code', 'rhodecode_pre_code'),
255 255 ('post_code', 'rhodecode_post_code'),
256 256 ('captcha_public_key', 'rhodecode_captcha_public_key'),
257 257 ('captcha_private_key', 'rhodecode_captcha_private_key'),
258 258 ]
259 259 for setting, form_key in settings:
260 260 sett = SettingsModel().create_or_update_setting(
261 261 setting, form_result[form_key])
262 262 Session().add(sett)
263 263
264 264 Session().commit()
265 265 SettingsModel().invalidate_settings_cache()
266 266 h.flash(_('Updated application settings'), category='success')
267 267 except Exception:
268 268 log.exception("Exception while updating application settings")
269 269 h.flash(
270 270 _('Error occurred during updating application settings'),
271 271 category='error')
272 272
273 273 return redirect(url('admin_settings_global'))
274 274
275 275 @HasPermissionAllDecorator('hg.admin')
276 276 def settings_global(self):
277 277 """GET /admin/settings/global: All items in the collection"""
278 278 # url('admin_settings_global')
279 279 c.active = 'global'
280 280
281 281 return htmlfill.render(
282 282 render('admin/settings/settings.html'),
283 283 defaults=self._form_defaults(),
284 284 encoding="UTF-8",
285 285 force_defaults=False)
286 286
287 287 @HasPermissionAllDecorator('hg.admin')
288 288 @auth.CSRFRequired()
289 289 def settings_visual_update(self):
290 290 """POST /admin/settings/visual: All items in the collection"""
291 291 # url('admin_settings_visual')
292 292 c.active = 'visual'
293 293 application_form = ApplicationVisualisationForm()()
294 294 try:
295 295 form_result = application_form.to_python(dict(request.POST))
296 296 except formencode.Invalid as errors:
297 297 return htmlfill.render(
298 298 render('admin/settings/settings.html'),
299 299 defaults=errors.value,
300 300 errors=errors.error_dict or {},
301 301 prefix_error=False,
302 302 encoding="UTF-8",
303 303 force_defaults=False
304 304 )
305 305
306 306 try:
307 307 settings = [
308 308 ('show_public_icon', 'rhodecode_show_public_icon', 'bool'),
309 309 ('show_private_icon', 'rhodecode_show_private_icon', 'bool'),
310 310 ('stylify_metatags', 'rhodecode_stylify_metatags', 'bool'),
311 311 ('repository_fields', 'rhodecode_repository_fields', 'bool'),
312 312 ('dashboard_items', 'rhodecode_dashboard_items', 'int'),
313 313 ('admin_grid_items', 'rhodecode_admin_grid_items', 'int'),
314 314 ('show_version', 'rhodecode_show_version', 'bool'),
315 315 ('use_gravatar', 'rhodecode_use_gravatar', 'bool'),
316 316 ('markup_renderer', 'rhodecode_markup_renderer', 'unicode'),
317 317 ('gravatar_url', 'rhodecode_gravatar_url', 'unicode'),
318 318 ('clone_uri_tmpl', 'rhodecode_clone_uri_tmpl', 'unicode'),
319 319 ('support_url', 'rhodecode_support_url', 'unicode'),
320 320 ('show_revision_number', 'rhodecode_show_revision_number', 'bool'),
321 321 ('show_sha_length', 'rhodecode_show_sha_length', 'int'),
322 322 ]
323 323 for setting, form_key, type_ in settings:
324 324 sett = SettingsModel().create_or_update_setting(
325 325 setting, form_result[form_key], type_)
326 326 Session().add(sett)
327 327
328 328 Session().commit()
329 329 SettingsModel().invalidate_settings_cache()
330 330 h.flash(_('Updated visualisation settings'), category='success')
331 331 except Exception:
332 332 log.exception("Exception updating visualization settings")
333 333 h.flash(_('Error occurred during updating '
334 334 'visualisation settings'),
335 335 category='error')
336 336
337 337 return redirect(url('admin_settings_visual'))
338 338
339 339 @HasPermissionAllDecorator('hg.admin')
340 340 def settings_visual(self):
341 341 """GET /admin/settings/visual: All items in the collection"""
342 342 # url('admin_settings_visual')
343 343 c.active = 'visual'
344 344
345 345 return htmlfill.render(
346 346 render('admin/settings/settings.html'),
347 347 defaults=self._form_defaults(),
348 348 encoding="UTF-8",
349 349 force_defaults=False)
350 350
351 351 @HasPermissionAllDecorator('hg.admin')
352 352 @auth.CSRFRequired()
353 353 def settings_issuetracker_test(self):
354 354 if request.is_xhr:
355 355 return h.urlify_commit_message(
356 356 request.POST.get('test_text', ''),
357 357 'repo_group/test_repo1')
358 358 else:
359 359 raise HTTPBadRequest()
360 360
361 361 @HasPermissionAllDecorator('hg.admin')
362 362 @auth.CSRFRequired()
363 363 def settings_issuetracker_delete(self):
364 364 uid = request.POST.get('uid')
365 365 IssueTrackerSettingsModel().delete_entries(uid)
366 366 h.flash(_('Removed issue tracker entry'), category='success')
367 367 return redirect(url('admin_settings_issuetracker'))
368 368
369 369 @HasPermissionAllDecorator('hg.admin')
370 370 def settings_issuetracker(self):
371 371 """GET /admin/settings/issue-tracker: All items in the collection"""
372 372 # url('admin_settings_issuetracker')
373 373 c.active = 'issuetracker'
374 374 defaults = SettingsModel().get_all_settings()
375 375
376 376 entry_key = 'rhodecode_issuetracker_pat_'
377 377
378 378 c.issuetracker_entries = {}
379 379 for k, v in defaults.items():
380 380 if k.startswith(entry_key):
381 381 uid = k[len(entry_key):]
382 382 c.issuetracker_entries[uid] = None
383 383
384 384 for uid in c.issuetracker_entries:
385 385 c.issuetracker_entries[uid] = AttributeDict({
386 386 'pat': defaults.get('rhodecode_issuetracker_pat_' + uid),
387 387 'url': defaults.get('rhodecode_issuetracker_url_' + uid),
388 388 'pref': defaults.get('rhodecode_issuetracker_pref_' + uid),
389 389 'desc': defaults.get('rhodecode_issuetracker_desc_' + uid),
390 390 })
391 391
392 392 return render('admin/settings/settings.html')
393 393
394 394 @HasPermissionAllDecorator('hg.admin')
395 395 @auth.CSRFRequired()
396 396 def settings_issuetracker_save(self):
397 397 settings_model = IssueTrackerSettingsModel()
398 398
399 399 form = IssueTrackerPatternsForm()().to_python(request.POST)
400 for uid in form['delete_patterns']:
401 settings_model.delete_entries(uid)
400 if form:
401 for uid in form.get('delete_patterns', []):
402 settings_model.delete_entries(uid)
402 403
403 for pattern in form['patterns']:
404 for setting, value, type_ in pattern:
405 sett = settings_model.create_or_update_setting(
406 setting, value, type_)
407 Session().add(sett)
404 for pattern in form.get('patterns', []):
405 for setting, value, type_ in pattern:
406 sett = settings_model.create_or_update_setting(
407 setting, value, type_)
408 Session().add(sett)
408 409
409 Session().commit()
410 Session().commit()
410 411
411 SettingsModel().invalidate_settings_cache()
412 h.flash(_('Updated issue tracker entries'), category='success')
412 SettingsModel().invalidate_settings_cache()
413 h.flash(_('Updated issue tracker entries'), category='success')
413 414 return redirect(url('admin_settings_issuetracker'))
414 415
415 416 @HasPermissionAllDecorator('hg.admin')
416 417 @auth.CSRFRequired()
417 418 def settings_email_update(self):
418 419 """POST /admin/settings/email: All items in the collection"""
419 420 # url('admin_settings_email')
420 421 c.active = 'email'
421 422
422 423 test_email = request.POST.get('test_email')
423 424
424 425 if not test_email:
425 426 h.flash(_('Please enter email address'), category='error')
426 427 return redirect(url('admin_settings_email'))
427 428
428 429 email_kwargs = {
429 430 'date': datetime.datetime.now(),
430 431 'user': c.rhodecode_user,
431 432 'rhodecode_version': c.rhodecode_version
432 433 }
433 434
434 435 (subject, headers, email_body,
435 436 email_body_plaintext) = EmailNotificationModel().render_email(
436 437 EmailNotificationModel.TYPE_EMAIL_TEST, **email_kwargs)
437 438
438 439 recipients = [test_email] if test_email else None
439 440
440 441 run_task(tasks.send_email, recipients, subject,
441 442 email_body_plaintext, email_body)
442 443
443 444 h.flash(_('Send email task created'), category='success')
444 445 return redirect(url('admin_settings_email'))
445 446
446 447 @HasPermissionAllDecorator('hg.admin')
447 448 def settings_email(self):
448 449 """GET /admin/settings/email: All items in the collection"""
449 450 # url('admin_settings_email')
450 451 c.active = 'email'
451 452 c.rhodecode_ini = rhodecode.CONFIG
452 453
453 454 return htmlfill.render(
454 455 render('admin/settings/settings.html'),
455 456 defaults=self._form_defaults(),
456 457 encoding="UTF-8",
457 458 force_defaults=False)
458 459
459 460 @HasPermissionAllDecorator('hg.admin')
460 461 @auth.CSRFRequired()
461 462 def settings_hooks_update(self):
462 463 """POST or DELETE /admin/settings/hooks: All items in the collection"""
463 464 # url('admin_settings_hooks')
464 465 c.active = 'hooks'
465 466 if c.visual.allow_custom_hooks_settings:
466 467 ui_key = request.POST.get('new_hook_ui_key')
467 468 ui_value = request.POST.get('new_hook_ui_value')
468 469
469 470 hook_id = request.POST.get('hook_id')
470 471 new_hook = False
471 472
472 473 model = SettingsModel()
473 474 try:
474 475 if ui_value and ui_key:
475 476 model.create_or_update_hook(ui_key, ui_value)
476 477 h.flash(_('Added new hook'), category='success')
477 478 new_hook = True
478 479 elif hook_id:
479 480 RhodeCodeUi.delete(hook_id)
480 481 Session().commit()
481 482
482 483 # check for edits
483 484 update = False
484 485 _d = request.POST.dict_of_lists()
485 486 for k, v in zip(_d.get('hook_ui_key', []),
486 487 _d.get('hook_ui_value_new', [])):
487 488 model.create_or_update_hook(k, v)
488 489 update = True
489 490
490 491 if update and not new_hook:
491 492 h.flash(_('Updated hooks'), category='success')
492 493 Session().commit()
493 494 except Exception:
494 495 log.exception("Exception during hook creation")
495 496 h.flash(_('Error occurred during hook creation'),
496 497 category='error')
497 498
498 499 return redirect(url('admin_settings_hooks'))
499 500
500 501 @HasPermissionAllDecorator('hg.admin')
501 502 def settings_hooks(self):
502 503 """GET /admin/settings/hooks: All items in the collection"""
503 504 # url('admin_settings_hooks')
504 505 c.active = 'hooks'
505 506
506 507 model = SettingsModel()
507 508 c.hooks = model.get_builtin_hooks()
508 509 c.custom_hooks = model.get_custom_hooks()
509 510
510 511 return htmlfill.render(
511 512 render('admin/settings/settings.html'),
512 513 defaults=self._form_defaults(),
513 514 encoding="UTF-8",
514 515 force_defaults=False)
515 516
516 517 @HasPermissionAllDecorator('hg.admin')
517 518 def settings_search(self):
518 519 """GET /admin/settings/search: All items in the collection"""
519 520 # url('admin_settings_search')
520 521 c.active = 'search'
521 522
522 523 from rhodecode.lib.index import searcher_from_config
523 524 searcher = searcher_from_config(config)
524 525 c.statistics = searcher.statistics()
525 526
526 527 return render('admin/settings/settings.html')
527 528
528 529 @HasPermissionAllDecorator('hg.admin')
529 530 def settings_system(self):
530 531 """GET /admin/settings/system: All items in the collection"""
531 532 # url('admin_settings_system')
532 533 snapshot = str2bool(request.GET.get('snapshot'))
533 534 c.active = 'system'
534 535
535 536 defaults = self._form_defaults()
536 537 c.rhodecode_ini = rhodecode.CONFIG
537 538 c.rhodecode_update_url = defaults.get('rhodecode_update_url')
538 539 server_info = ScmModel().get_server_info(request.environ)
539 540 for key, val in server_info.iteritems():
540 541 setattr(c, key, val)
541 542
542 543 if c.disk['percent'] > 90:
543 544 h.flash(h.literal(_(
544 545 'Critical: your disk space is very low <b>%s%%</b> used' %
545 546 c.disk['percent'])), 'error')
546 547 elif c.disk['percent'] > 70:
547 548 h.flash(h.literal(_(
548 549 'Warning: your disk space is running low <b>%s%%</b> used' %
549 550 c.disk['percent'])), 'warning')
550 551
551 552 try:
552 553 c.uptime_age = h._age(
553 554 h.time_to_datetime(c.boot_time), False, show_suffix=False)
554 555 except TypeError:
555 556 c.uptime_age = c.boot_time
556 557
557 558 try:
558 559 c.system_memory = '%s/%s, %s%% (%s%%) used%s' % (
559 560 h.format_byte_size_binary(c.memory['used']),
560 561 h.format_byte_size_binary(c.memory['total']),
561 562 c.memory['percent2'],
562 563 c.memory['percent'],
563 564 ' %s' % c.memory['error'] if 'error' in c.memory else '')
564 565 except TypeError:
565 566 c.system_memory = 'NOT AVAILABLE'
566 567
567 568 rhodecode_ini_safe = rhodecode.CONFIG.copy()
568 569 blacklist = [
569 570 'rhodecode_license_key',
570 571 'routes.map',
571 572 'pylons.h',
572 573 'pylons.app_globals',
573 574 'pylons.environ_config',
574 575 'sqlalchemy.db1.url',
575 576 ('app_conf', 'sqlalchemy.db1.url')
576 577 ]
577 578 for k in blacklist:
578 579 if isinstance(k, tuple):
579 580 section, key = k
580 581 if section in rhodecode_ini_safe:
581 582 rhodecode_ini_safe[section].pop(key, None)
582 583 else:
583 584 rhodecode_ini_safe.pop(k, None)
584 585
585 586 c.rhodecode_ini_safe = rhodecode_ini_safe
586 587
587 588 # TODO: marcink, figure out how to allow only selected users to do this
588 589 c.allowed_to_snapshot = False
589 590
590 591 if snapshot:
591 592 if c.allowed_to_snapshot:
592 593 return render('admin/settings/settings_system_snapshot.html')
593 594 else:
594 595 h.flash('You are not allowed to do this', category='warning')
595 596
596 597 return htmlfill.render(
597 598 render('admin/settings/settings.html'),
598 599 defaults=defaults,
599 600 encoding="UTF-8",
600 601 force_defaults=False)
601 602
602 603 @staticmethod
603 604 def get_update_data(update_url):
604 605 """Return the JSON update data."""
605 606 ver = rhodecode.__version__
606 607 log.debug('Checking for upgrade on `%s` server', update_url)
607 608 opener = urllib2.build_opener()
608 609 opener.addheaders = [('User-agent', 'RhodeCode-SCM/%s' % ver)]
609 610 response = opener.open(update_url)
610 611 response_data = response.read()
611 612 data = json.loads(response_data)
612 613
613 614 return data
614 615
615 616 @HasPermissionAllDecorator('hg.admin')
616 617 def settings_system_update(self):
617 618 """GET /admin/settings/system/updates: All items in the collection"""
618 619 # url('admin_settings_system_update')
619 620 defaults = self._form_defaults()
620 621 update_url = defaults.get('rhodecode_update_url', '')
621 622
622 623 _err = lambda s: '<div style="color:#ff8888; padding:4px 0px">%s</div>' % (s)
623 624 try:
624 625 data = self.get_update_data(update_url)
625 626 except urllib2.URLError as e:
626 627 log.exception("Exception contacting upgrade server")
627 628 return _err('Failed to contact upgrade server: %r' % e)
628 629 except ValueError as e:
629 630 log.exception("Bad data sent from update server")
630 631 return _err('Bad data sent from update server')
631 632
632 633 latest = data['versions'][0]
633 634
634 635 c.update_url = update_url
635 636 c.latest_data = latest
636 637 c.latest_ver = latest['version']
637 638 c.cur_ver = rhodecode.__version__
638 639 c.should_upgrade = False
639 640
640 641 if (packaging.version.Version(c.latest_ver) >
641 642 packaging.version.Version(c.cur_ver)):
642 643 c.should_upgrade = True
643 644 c.important_notices = latest['general']
644 645
645 646 return render('admin/settings/settings_system_update.html')
646 647
647 648 @HasPermissionAllDecorator('hg.admin')
648 649 def settings_supervisor(self):
649 650 c.rhodecode_ini = rhodecode.CONFIG
650 651 c.active = 'supervisor'
651 652
652 653 c.supervisor_procs = OrderedDict([
653 654 (SUPERVISOR_MASTER, {}),
654 655 ])
655 656
656 657 c.log_size = 10240
657 658 supervisor = SupervisorModel()
658 659
659 660 _connection = supervisor.get_connection(
660 661 c.rhodecode_ini.get('supervisor.uri'))
661 662 c.connection_error = None
662 663 try:
663 664 _connection.supervisor.getAllProcessInfo()
664 665 except Exception as e:
665 666 c.connection_error = str(e)
666 667 log.exception("Exception reading supervisor data")
667 668 return render('admin/settings/settings.html')
668 669
669 670 groupid = c.rhodecode_ini.get('supervisor.group_id')
670 671
671 672 # feed our group processes to the main
672 673 for proc in supervisor.get_group_processes(_connection, groupid):
673 674 c.supervisor_procs[proc['name']] = {}
674 675
675 676 for k in c.supervisor_procs.keys():
676 677 try:
677 678 # master process info
678 679 if k == SUPERVISOR_MASTER:
679 680 _data = supervisor.get_master_state(_connection)
680 681 _data['name'] = 'supervisor master'
681 682 _data['description'] = 'pid %s, id: %s, ver: %s' % (
682 683 _data['pid'], _data['id'], _data['ver'])
683 684 c.supervisor_procs[k] = _data
684 685 else:
685 686 procid = groupid + ":" + k
686 687 c.supervisor_procs[k] = supervisor.get_process_info(_connection, procid)
687 688 except Exception as e:
688 689 log.exception("Exception reading supervisor data")
689 690 c.supervisor_procs[k] = {'_rhodecode_error': str(e)}
690 691
691 692 return render('admin/settings/settings.html')
692 693
693 694 @HasPermissionAllDecorator('hg.admin')
694 695 def settings_supervisor_log(self, procid):
695 696 import rhodecode
696 697 c.rhodecode_ini = rhodecode.CONFIG
697 698 c.active = 'supervisor_tail'
698 699
699 700 supervisor = SupervisorModel()
700 701 _connection = supervisor.get_connection(c.rhodecode_ini.get('supervisor.uri'))
701 702 groupid = c.rhodecode_ini.get('supervisor.group_id')
702 703 procid = groupid + ":" + procid if procid != SUPERVISOR_MASTER else procid
703 704
704 705 c.log_size = 10240
705 706 offset = abs(safe_int(request.GET.get('offset', c.log_size))) * -1
706 707 c.log = supervisor.read_process_log(_connection, procid, offset, 0)
707 708
708 709 return render('admin/settings/settings.html')
709 710
710 711 @HasPermissionAllDecorator('hg.admin')
711 712 @auth.CSRFRequired()
712 713 def settings_labs_update(self):
713 714 """POST /admin/settings/labs: All items in the collection"""
714 715 # url('admin_settings/labs', method={'POST'})
715 716 c.active = 'labs'
716 717
717 718 application_form = LabsSettingsForm()()
718 719 try:
719 720 form_result = application_form.to_python(dict(request.POST))
720 721 except formencode.Invalid as errors:
721 722 h.flash(
722 723 _('Some form inputs contain invalid data.'),
723 724 category='error')
724 725 return htmlfill.render(
725 726 render('admin/settings/settings.html'),
726 727 defaults=errors.value,
727 728 errors=errors.error_dict or {},
728 729 prefix_error=False,
729 730 encoding='UTF-8',
730 731 force_defaults=False
731 732 )
732 733
733 734 try:
734 735 session = Session()
735 736 for setting in _LAB_SETTINGS:
736 737 setting_name = setting.key[len('rhodecode_'):]
737 738 sett = SettingsModel().create_or_update_setting(
738 739 setting_name, form_result[setting.key], setting.type)
739 740 session.add(sett)
740 741
741 742 except Exception:
742 743 log.exception('Exception while updating lab settings')
743 744 h.flash(_('Error occurred during updating labs settings'),
744 745 category='error')
745 746 else:
746 747 Session().commit()
747 748 SettingsModel().invalidate_settings_cache()
748 749 h.flash(_('Updated Labs settings'), category='success')
749 750 return redirect(url('admin_settings_labs'))
750 751
751 752 return htmlfill.render(
752 753 render('admin/settings/settings.html'),
753 754 defaults=self._form_defaults(),
754 755 encoding='UTF-8',
755 756 force_defaults=False)
756 757
757 758 @HasPermissionAllDecorator('hg.admin')
758 759 def settings_labs(self):
759 760 """GET /admin/settings/labs: All items in the collection"""
760 761 # url('admin_settings_labs')
761 762 if not c.labs_active:
762 763 redirect(url('admin_settings'))
763 764
764 765 c.active = 'labs'
765 766 c.lab_settings = _LAB_SETTINGS
766 767
767 768 return htmlfill.render(
768 769 render('admin/settings/settings.html'),
769 770 defaults=self._form_defaults(),
770 771 encoding='UTF-8',
771 772 force_defaults=False)
772 773
773 774 def _form_defaults(self):
774 775 defaults = SettingsModel().get_all_settings()
775 776 defaults.update(self._get_hg_ui_settings())
776 777 defaults.update({
777 778 'new_svn_branch': '',
778 779 'new_svn_tag': '',
779 780 })
780 781 return defaults
781 782
782 783
783 784 # :param key: name of the setting including the 'rhodecode_' prefix
784 785 # :param type: the RhodeCodeSetting type to use.
785 786 # :param group: the i18ned group in which we should dispaly this setting
786 787 # :param label: the i18ned label we should display for this setting
787 788 # :param help: the i18ned help we should dispaly for this setting
788 789 LabSetting = collections.namedtuple(
789 790 'LabSetting', ('key', 'type', 'group', 'label', 'help'))
790 791
791 792
792 793 # This list has to be kept in sync with the form
793 794 # rhodecode.model.forms.LabsSettingsForm.
794 795 _LAB_SETTINGS = [
795 796
796 797 ]
@@ -1,611 +1,619 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 mock
22 22 import pytest
23 23
24 24 import rhodecode
25 25 from rhodecode.config.routing import ADMIN_PREFIX
26 26 from rhodecode.lib.utils2 import md5
27 27 from rhodecode.model.db import RhodeCodeUi
28 28 from rhodecode.model.meta import Session
29 29 from rhodecode.model.settings import SettingsModel, IssueTrackerSettingsModel
30 30 from rhodecode.tests import url, assert_session_flash
31 31 from rhodecode.tests.utils import AssertResponse
32 32
33 33
34 34 UPDATE_DATA_QUALNAME = (
35 35 'rhodecode.controllers.admin.settings.SettingsController.get_update_data')
36 36
37 37
38 38 @pytest.mark.usefixtures('autologin_user', 'app')
39 39 class TestAdminSettingsController:
40 40
41 41 @pytest.mark.parametrize('urlname', [
42 42 'admin_settings_vcs',
43 43 'admin_settings_mapping',
44 44 'admin_settings_global',
45 45 'admin_settings_visual',
46 46 'admin_settings_email',
47 47 'admin_settings_hooks',
48 48 'admin_settings_search',
49 49 'admin_settings_system',
50 50 ])
51 51 def test_simple_get(self, urlname, app):
52 52 app.get(url(urlname))
53 53
54 54 def test_create_custom_hook(self, csrf_token):
55 55 response = self.app.post(
56 56 url('admin_settings_hooks'),
57 57 params={
58 58 'new_hook_ui_key': 'test_hooks_1',
59 59 'new_hook_ui_value': 'cd /tmp',
60 60 'csrf_token': csrf_token})
61 61
62 62 response = response.follow()
63 63 response.mustcontain('test_hooks_1')
64 64 response.mustcontain('cd /tmp')
65 65
66 66 def test_create_custom_hook_delete(self, csrf_token):
67 67 response = self.app.post(
68 68 url('admin_settings_hooks'),
69 69 params={
70 70 'new_hook_ui_key': 'test_hooks_2',
71 71 'new_hook_ui_value': 'cd /tmp2',
72 72 'csrf_token': csrf_token})
73 73
74 74 response = response.follow()
75 75 response.mustcontain('test_hooks_2')
76 76 response.mustcontain('cd /tmp2')
77 77
78 78 hook_id = SettingsModel().get_ui_by_key('test_hooks_2').ui_id
79 79
80 80 # delete
81 81 self.app.post(
82 82 url('admin_settings_hooks'),
83 83 params={'hook_id': hook_id, 'csrf_token': csrf_token})
84 84 response = self.app.get(url('admin_settings_hooks'))
85 85 response.mustcontain(no=['test_hooks_2'])
86 86 response.mustcontain(no=['cd /tmp2'])
87 87
88 88 def test_system_update_new_version(self):
89 89 update_data = {
90 90 'versions': [
91 91 {
92 92 'version': '100.3.1415926535',
93 93 'general': 'The latest version we are ever going to ship'
94 94 },
95 95 {
96 96 'version': '0.0.0',
97 97 'general': 'The first version we ever shipped'
98 98 }
99 99 ]
100 100 }
101 101 with mock.patch(UPDATE_DATA_QUALNAME, return_value=update_data):
102 102 response = self.app.get(url('admin_settings_system_update'))
103 103 response.mustcontain('A <b>new version</b> is available')
104 104
105 105 def test_system_update_nothing_new(self):
106 106 update_data = {
107 107 'versions': [
108 108 {
109 109 'version': '0.0.0',
110 110 'general': 'The first version we ever shipped'
111 111 }
112 112 ]
113 113 }
114 114 with mock.patch(UPDATE_DATA_QUALNAME, return_value=update_data):
115 115 response = self.app.get(url('admin_settings_system_update'))
116 116 response.mustcontain(
117 117 'You already have the <b>latest</b> stable version.')
118 118
119 119 def test_system_update_bad_response(self):
120 120 with mock.patch(UPDATE_DATA_QUALNAME, side_effect=ValueError('foo')):
121 121 response = self.app.get(url('admin_settings_system_update'))
122 122 response.mustcontain(
123 123 'Bad data sent from update server')
124 124
125 125
126 126 @pytest.mark.usefixtures('autologin_user', 'app')
127 127 class TestAdminSettingsGlobal:
128 128
129 129 def test_pre_post_code_code_active(self, csrf_token):
130 130 pre_code = 'rc-pre-code-187652122'
131 131 post_code = 'rc-postcode-98165231'
132 132
133 133 response = self.post_and_verify_settings({
134 134 'rhodecode_pre_code': pre_code,
135 135 'rhodecode_post_code': post_code,
136 136 'csrf_token': csrf_token,
137 137 })
138 138
139 139 response = response.follow()
140 140 response.mustcontain(pre_code, post_code)
141 141
142 142 def test_pre_post_code_code_inactive(self, csrf_token):
143 143 pre_code = 'rc-pre-code-187652122'
144 144 post_code = 'rc-postcode-98165231'
145 145 response = self.post_and_verify_settings({
146 146 'rhodecode_pre_code': '',
147 147 'rhodecode_post_code': '',
148 148 'csrf_token': csrf_token,
149 149 })
150 150
151 151 response = response.follow()
152 152 response.mustcontain(no=[pre_code, post_code])
153 153
154 154 def test_captcha_activate(self, csrf_token):
155 155 self.post_and_verify_settings({
156 156 'rhodecode_captcha_private_key': '1234567890',
157 157 'rhodecode_captcha_public_key': '1234567890',
158 158 'csrf_token': csrf_token,
159 159 })
160 160
161 161 response = self.app.get(ADMIN_PREFIX + '/register')
162 162 response.mustcontain('captcha')
163 163
164 164 def test_captcha_deactivate(self, csrf_token):
165 165 self.post_and_verify_settings({
166 166 'rhodecode_captcha_private_key': '',
167 167 'rhodecode_captcha_public_key': '1234567890',
168 168 'csrf_token': csrf_token,
169 169 })
170 170
171 171 response = self.app.get(ADMIN_PREFIX + '/register')
172 172 response.mustcontain(no=['captcha'])
173 173
174 174 def test_title_change(self, csrf_token):
175 175 old_title = 'RhodeCode'
176 176 new_title = old_title + '_changed'
177 177
178 178 for new_title in ['Changed', 'Ε»Γ³Ε‚wik', old_title]:
179 179 response = self.post_and_verify_settings({
180 180 'rhodecode_title': new_title,
181 181 'csrf_token': csrf_token,
182 182 })
183 183
184 184 response = response.follow()
185 185 response.mustcontain(
186 186 """<div class="branding">- %s</div>""" % new_title)
187 187
188 188 def post_and_verify_settings(self, settings):
189 189 old_title = 'RhodeCode'
190 190 old_realm = 'RhodeCode authentication'
191 191 params = {
192 192 'rhodecode_title': old_title,
193 193 'rhodecode_realm': old_realm,
194 194 'rhodecode_pre_code': '',
195 195 'rhodecode_post_code': '',
196 196 'rhodecode_captcha_private_key': '',
197 197 'rhodecode_captcha_public_key': '',
198 198 }
199 199 params.update(settings)
200 200 response = self.app.post(url('admin_settings_global'), params=params)
201 201
202 202 assert_session_flash(response, 'Updated application settings')
203 203 app_settings = SettingsModel().get_all_settings()
204 204 del settings['csrf_token']
205 205 for key, value in settings.iteritems():
206 206 assert app_settings[key] == value.decode('utf-8')
207 207
208 208 return response
209 209
210 210
211 211 @pytest.mark.usefixtures('autologin_user', 'app')
212 212 class TestAdminSettingsVcs:
213 213
214 214 def test_contains_svn_default_patterns(self, app):
215 215 response = app.get(url('admin_settings_vcs'))
216 216 expected_patterns = [
217 217 '/trunk',
218 218 '/branches/*',
219 219 '/tags/*',
220 220 ]
221 221 for pattern in expected_patterns:
222 222 response.mustcontain(pattern)
223 223
224 224 def test_add_new_svn_branch_and_tag_pattern(
225 225 self, app, backend_svn, form_defaults, disable_sql_cache,
226 226 csrf_token):
227 227 form_defaults.update({
228 228 'new_svn_branch': '/exp/branches/*',
229 229 'new_svn_tag': '/important_tags/*',
230 230 'csrf_token': csrf_token,
231 231 })
232 232
233 233 response = app.post(
234 234 url('admin_settings_vcs'), params=form_defaults, status=302)
235 235 response = response.follow()
236 236
237 237 # Expect to find the new values on the page
238 238 response.mustcontain('/exp/branches/*')
239 239 response.mustcontain('/important_tags/*')
240 240
241 241 # Expect that those patterns are used to match branches and tags now
242 242 repo = backend_svn['svn-simple-layout'].scm_instance()
243 243 assert 'exp/branches/exp-sphinx-docs' in repo.branches
244 244 assert 'important_tags/v0.5' in repo.tags
245 245
246 246 def test_add_same_svn_value_twice_shows_an_error_message(
247 247 self, app, form_defaults, csrf_token, settings_util):
248 248 settings_util.create_rhodecode_ui('vcs_svn_branch', '/test')
249 249 settings_util.create_rhodecode_ui('vcs_svn_tag', '/test')
250 250
251 251 response = app.post(
252 252 url('admin_settings_vcs'),
253 253 params={
254 254 'paths_root_path': form_defaults['paths_root_path'],
255 255 'new_svn_branch': '/test',
256 256 'new_svn_tag': '/test',
257 257 'csrf_token': csrf_token,
258 258 },
259 259 status=200)
260 260
261 261 response.mustcontain("Pattern already exists")
262 262 response.mustcontain("Some form inputs contain invalid data.")
263 263
264 264 @pytest.mark.parametrize('section', [
265 265 'vcs_svn_branch',
266 266 'vcs_svn_tag',
267 267 ])
268 268 def test_delete_svn_patterns(
269 269 self, section, app, csrf_token, settings_util):
270 270 setting = settings_util.create_rhodecode_ui(
271 271 section, '/test_delete', cleanup=False)
272 272
273 273 app.post(
274 274 url('admin_settings_vcs'),
275 275 params={
276 276 '_method': 'delete',
277 277 'delete_svn_pattern': setting.ui_id,
278 278 'csrf_token': csrf_token},
279 279 headers={'X-REQUESTED-WITH': 'XMLHttpRequest'})
280 280
281 281 @pytest.mark.parametrize('section', [
282 282 'vcs_svn_branch',
283 283 'vcs_svn_tag',
284 284 ])
285 285 def test_delete_svn_patterns_raises_400_when_no_xhr(
286 286 self, section, app, csrf_token, settings_util):
287 287 setting = settings_util.create_rhodecode_ui(section, '/test_delete')
288 288
289 289 app.post(
290 290 url('admin_settings_vcs'),
291 291 params={
292 292 '_method': 'delete',
293 293 'delete_svn_pattern': setting.ui_id,
294 294 'csrf_token': csrf_token},
295 295 status=400)
296 296
297 297 def test_extensions_hgsubversion(self, app, form_defaults, csrf_token):
298 298 form_defaults.update({
299 299 'csrf_token': csrf_token,
300 300 'extensions_hgsubversion': 'True',
301 301 })
302 302 response = app.post(
303 303 url('admin_settings_vcs'),
304 304 params=form_defaults,
305 305 status=302)
306 306
307 307 response = response.follow()
308 308 extensions_input = (
309 309 '<input id="extensions_hgsubversion" '
310 310 'name="extensions_hgsubversion" type="checkbox" '
311 311 'value="True" checked="checked" />')
312 312 response.mustcontain(extensions_input)
313 313
314 314 def test_has_a_section_for_pull_request_settings(self, app):
315 315 response = app.get(url('admin_settings_vcs'))
316 316 response.mustcontain('Pull Request Settings')
317 317
318 318 def test_has_an_input_for_invalidation_of_inline_comments(
319 319 self, app):
320 320 response = app.get(url('admin_settings_vcs'))
321 321 assert_response = AssertResponse(response)
322 322 assert_response.one_element_exists(
323 323 '[name=rhodecode_use_outdated_comments]')
324 324
325 325 @pytest.mark.parametrize('new_value', [True, False])
326 326 def test_allows_to_change_invalidation_of_inline_comments(
327 327 self, app, form_defaults, csrf_token, new_value):
328 328 setting_key = 'use_outdated_comments'
329 329 setting = SettingsModel().create_or_update_setting(
330 330 setting_key, not new_value, 'bool')
331 331 Session().add(setting)
332 332 Session().commit()
333 333
334 334 form_defaults.update({
335 335 'csrf_token': csrf_token,
336 336 'rhodecode_use_outdated_comments': str(new_value),
337 337 })
338 338 response = app.post(
339 339 url('admin_settings_vcs'),
340 340 params=form_defaults,
341 341 status=302)
342 342 response = response.follow()
343 343 setting = SettingsModel().get_setting_by_name(setting_key)
344 344 assert setting.app_settings_value is new_value
345 345
346 346 def test_has_a_section_for_labs_settings_if_enabled(self, app):
347 347 with mock.patch.dict(
348 348 rhodecode.CONFIG, {'labs_settings_active': 'true'}):
349 349 response = self.app.get(url('admin_settings_vcs'))
350 350 response.mustcontain('Labs Settings')
351 351
352 352 def test_has_not_a_section_for_labs_settings_if_disables(self, app):
353 353 with mock.patch.dict(
354 354 rhodecode.CONFIG, {'labs_settings_active': 'false'}):
355 355 response = self.app.get(url('admin_settings_vcs'))
356 356 response.mustcontain(no='Labs Settings')
357 357
358 358 @pytest.mark.parametrize('new_value', [True, False])
359 359 def test_allows_to_change_hg_rebase_merge_strategy(
360 360 self, app, form_defaults, csrf_token, new_value):
361 361 setting_key = 'hg_use_rebase_for_merging'
362 362
363 363 form_defaults.update({
364 364 'csrf_token': csrf_token,
365 365 'rhodecode_' + setting_key: str(new_value),
366 366 })
367 367
368 368 with mock.patch.dict(
369 369 rhodecode.CONFIG, {'labs_settings_active': 'true'}):
370 370 app.post(
371 371 url('admin_settings_vcs'),
372 372 params=form_defaults,
373 373 status=302)
374 374
375 375 setting = SettingsModel().get_setting_by_name(setting_key)
376 376 assert setting.app_settings_value is new_value
377 377
378 378 @pytest.fixture
379 379 def disable_sql_cache(self, request):
380 380 patcher = mock.patch(
381 381 'rhodecode.lib.caching_query.FromCache.process_query')
382 382 request.addfinalizer(patcher.stop)
383 383 patcher.start()
384 384
385 385 @pytest.fixture
386 386 def form_defaults(self):
387 387 from rhodecode.controllers.admin.settings import SettingsController
388 388 controller = SettingsController()
389 389 return controller._form_defaults()
390 390
391 391 # TODO: johbo: What we really want is to checkpoint before a test run and
392 392 # reset the session afterwards.
393 393 @pytest.fixture(scope='class', autouse=True)
394 394 def cleanup_settings(self, request, pylonsapp):
395 395 ui_id = RhodeCodeUi.ui_id
396 396 original_ids = list(
397 397 r.ui_id for r in RhodeCodeUi.query().values(ui_id))
398 398
399 399 @request.addfinalizer
400 400 def cleanup():
401 401 RhodeCodeUi.query().filter(
402 402 ui_id.notin_(original_ids)).delete(False)
403 403
404 404
405 405 @pytest.mark.usefixtures('autologin_user', 'app')
406 406 class TestLabsSettings(object):
407 407 def test_get_settings_page_disabled(self):
408 408 with mock.patch.dict(rhodecode.CONFIG,
409 409 {'labs_settings_active': 'false'}):
410 410 response = self.app.get(url('admin_settings_labs'), status=302)
411 411
412 412 assert response.location.endswith(url('admin_settings'))
413 413
414 414 def test_get_settings_page_enabled(self):
415 415 from rhodecode.controllers.admin import settings
416 416 lab_settings = [
417 417 settings.LabSetting(
418 418 key='rhodecode_bool',
419 419 type='bool',
420 420 group='bool group',
421 421 label='bool label',
422 422 help='bool help'
423 423 ),
424 424 settings.LabSetting(
425 425 key='rhodecode_text',
426 426 type='unicode',
427 427 group='text group',
428 428 label='text label',
429 429 help='text help'
430 430 ),
431 431 ]
432 432 with mock.patch.dict(rhodecode.CONFIG,
433 433 {'labs_settings_active': 'true'}):
434 434 with mock.patch.object(settings, '_LAB_SETTINGS', lab_settings):
435 435 response = self.app.get(url('admin_settings_labs'))
436 436
437 437 assert '<label>bool group:</label>' in response
438 438 assert '<label for="rhodecode_bool">bool label</label>' in response
439 439 assert '<p class="help-block">bool help</p>' in response
440 440 assert 'name="rhodecode_bool" type="checkbox"' in response
441 441
442 442 assert '<label>text group:</label>' in response
443 443 assert '<label for="rhodecode_text">text label</label>' in response
444 444 assert '<p class="help-block">text help</p>' in response
445 445 assert 'name="rhodecode_text" size="60" type="text"' in response
446 446
447 447
448 448 @pytest.mark.usefixtures('app')
449 449 class TestOpenSourceLicenses(object):
450 450
451 451 def _get_url(self):
452 452 return ADMIN_PREFIX + '/settings/open_source'
453 453
454 454 def test_records_are_displayed(self, autologin_user):
455 455 sample_licenses = {
456 456 "python2.7-pytest-2.7.1": {
457 457 "UNKNOWN": None
458 458 },
459 459 "python2.7-Markdown-2.6.2": {
460 460 "BSD-3-Clause": "http://spdx.org/licenses/BSD-3-Clause"
461 461 }
462 462 }
463 463 read_licenses_patch = mock.patch(
464 464 'rhodecode.admin.views.read_opensource_licenses',
465 465 return_value=sample_licenses)
466 466 with read_licenses_patch:
467 467 response = self.app.get(self._get_url(), status=200)
468 468
469 469 assert_response = AssertResponse(response)
470 470 assert_response.element_contains(
471 471 '.panel-heading', 'Licenses of Third Party Packages')
472 472 for name in sample_licenses:
473 473 response.mustcontain(name)
474 474 for license in sample_licenses[name]:
475 475 assert_response.element_contains('.panel-body', license)
476 476
477 477 def test_records_can_be_read(self, autologin_user):
478 478 response = self.app.get(self._get_url(), status=200)
479 479 assert_response = AssertResponse(response)
480 480 assert_response.element_contains(
481 481 '.panel-heading', 'Licenses of Third Party Packages')
482 482
483 483 def test_forbidden_when_normal_user(self, autologin_regular_user):
484 484 self.app.get(self._get_url(), status=403)
485 485
486 486
487 487 @pytest.mark.usefixtures("app")
488 488 class TestAdminSettingsIssueTracker:
489 489 RC_PREFIX = 'rhodecode_'
490 490 SHORT_PATTERN_KEY = 'issuetracker_pat_'
491 491 PATTERN_KEY = RC_PREFIX + SHORT_PATTERN_KEY
492 492
493 493 def test_issuetracker_index(self, autologin_user):
494 494 response = self.app.get(url('admin_settings_issuetracker'))
495 495 assert response.status_code == 200
496 496
497 def test_add_empty_issuetracker_pattern(
498 self, request, autologin_user, csrf_token):
499 post_url = url('admin_settings_issuetracker_save')
500 post_data = {
501 'csrf_token': csrf_token
502 }
503 self.app.post(post_url, post_data, status=302)
504
497 505 def test_add_issuetracker_pattern(
498 506 self, request, autologin_user, csrf_token):
499 507 pattern = 'issuetracker_pat'
500 508 another_pattern = pattern+'1'
501 509 post_url = url('admin_settings_issuetracker_save')
502 510 post_data = {
503 511 'new_pattern_pattern_0': pattern,
504 512 'new_pattern_url_0': 'url',
505 513 'new_pattern_prefix_0': 'prefix',
506 514 'new_pattern_description_0': 'description',
507 515 'new_pattern_pattern_1': another_pattern,
508 516 'new_pattern_url_1': 'url1',
509 517 'new_pattern_prefix_1': 'prefix1',
510 518 'new_pattern_description_1': 'description1',
511 519 'csrf_token': csrf_token
512 520 }
513 521 self.app.post(post_url, post_data, status=302)
514 522 settings = SettingsModel().get_all_settings()
515 523 self.uid = md5(pattern)
516 524 assert settings[self.PATTERN_KEY+self.uid] == pattern
517 525 self.another_uid = md5(another_pattern)
518 526 assert settings[self.PATTERN_KEY+self.another_uid] == another_pattern
519 527
520 528 @request.addfinalizer
521 529 def cleanup():
522 530 defaults = SettingsModel().get_all_settings()
523 531
524 532 entries = [name for name in defaults if (
525 533 (self.uid in name) or (self.another_uid) in name)]
526 534 start = len(self.RC_PREFIX)
527 535 for del_key in entries:
528 536 # TODO: anderson: get_by_name needs name without prefix
529 537 entry = SettingsModel().get_setting_by_name(del_key[start:])
530 538 Session().delete(entry)
531 539
532 540 Session().commit()
533 541
534 542 def test_edit_issuetracker_pattern(
535 543 self, autologin_user, backend, csrf_token, request):
536 544 old_pattern = 'issuetracker_pat'
537 545 old_uid = md5(old_pattern)
538 546 pattern = 'issuetracker_pat_new'
539 547 self.new_uid = md5(pattern)
540 548
541 549 SettingsModel().create_or_update_setting(
542 550 self.SHORT_PATTERN_KEY+old_uid, old_pattern, 'unicode')
543 551
544 552 post_url = url('admin_settings_issuetracker_save')
545 553 post_data = {
546 554 'new_pattern_pattern_0': pattern,
547 555 'new_pattern_url_0': 'url',
548 556 'new_pattern_prefix_0': 'prefix',
549 557 'new_pattern_description_0': 'description',
550 558 'uid': old_uid,
551 559 'csrf_token': csrf_token
552 560 }
553 561 self.app.post(post_url, post_data, status=302)
554 562 settings = SettingsModel().get_all_settings()
555 563 assert settings[self.PATTERN_KEY+self.new_uid] == pattern
556 564 assert self.PATTERN_KEY+old_uid not in settings
557 565
558 566 @request.addfinalizer
559 567 def cleanup():
560 568 IssueTrackerSettingsModel().delete_entries(self.new_uid)
561 569
562 570 def test_replace_issuetracker_pattern_description(
563 571 self, autologin_user, csrf_token, request, settings_util):
564 572 prefix = 'issuetracker'
565 573 pattern = 'issuetracker_pat'
566 574 self.uid = md5(pattern)
567 575 pattern_key = '_'.join([prefix, 'pat', self.uid])
568 576 rc_pattern_key = '_'.join(['rhodecode', pattern_key])
569 577 desc_key = '_'.join([prefix, 'desc', self.uid])
570 578 rc_desc_key = '_'.join(['rhodecode', desc_key])
571 579 new_description = 'new_description'
572 580
573 581 settings_util.create_rhodecode_setting(
574 582 pattern_key, pattern, 'unicode', cleanup=False)
575 583 settings_util.create_rhodecode_setting(
576 584 desc_key, 'old description', 'unicode', cleanup=False)
577 585
578 586 post_url = url('admin_settings_issuetracker_save')
579 587 post_data = {
580 588 'new_pattern_pattern_0': pattern,
581 589 'new_pattern_url_0': 'url',
582 590 'new_pattern_prefix_0': 'prefix',
583 591 'new_pattern_description_0': new_description,
584 592 'uid': self.uid,
585 593 'csrf_token': csrf_token
586 594 }
587 595 self.app.post(post_url, post_data, status=302)
588 596 settings = SettingsModel().get_all_settings()
589 597 assert settings[rc_pattern_key] == pattern
590 598 assert settings[rc_desc_key] == new_description
591 599
592 600 @request.addfinalizer
593 601 def cleanup():
594 602 IssueTrackerSettingsModel().delete_entries(self.uid)
595 603
596 604 def test_delete_issuetracker_pattern(
597 605 self, autologin_user, backend, csrf_token, settings_util):
598 606 pattern = 'issuetracker_pat'
599 607 uid = md5(pattern)
600 608 settings_util.create_rhodecode_setting(
601 609 self.SHORT_PATTERN_KEY+uid, pattern, 'unicode', cleanup=False)
602 610
603 611 post_url = url('admin_issuetracker_delete')
604 612 post_data = {
605 613 '_method': 'delete',
606 614 'uid': uid,
607 615 'csrf_token': csrf_token
608 616 }
609 617 self.app.post(post_url, post_data, status=302)
610 618 settings = SettingsModel().get_all_settings()
611 619 assert 'rhodecode_%s%s' % (self.SHORT_PATTERN_KEY, uid) not in settings
General Comments 0
You need to be logged in to leave comments. Login now