##// END OF EJS Templates
git-lfs: settings for GIT repos to enable git-lfs and set store location...
marcink -
r1570:f2271c3d default
parent child Browse files
Show More
@@ -1,692 +1,694 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 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
29 29 import datetime
30 30 import formencode
31 31 from formencode import htmlfill
32 32 from pylons import request, tmpl_context as c, url, config
33 33 from pylons.controllers.util import redirect
34 34 from pylons.i18n.translation import _
35 35 from pyramid.threadlocal import get_current_registry
36 36 from webob.exc import HTTPBadRequest
37 37
38 38 import rhodecode
39 39 from rhodecode.apps.admin.navigation import navigation_list
40 40 from rhodecode.apps.svn_support.config_keys import generate_config
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.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 from rhodecode.model.repo_group import RepoGroupModel
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 def _get_hg_ui_settings(self):
85 def _get_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 if k in ['push_ssl', 'publish']:
97 if k in ['push_ssl', 'publish', 'enabled']:
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 # TODO: Replace with request.registry after migrating to pyramid.
138 138 pyramid_settings = get_current_registry().settings
139 139 c.svn_proxy_generate_config = pyramid_settings[generate_config]
140 140
141 141 application_form = ApplicationUiSettingsForm()()
142 142
143 143 try:
144 144 form_result = application_form.to_python(dict(request.POST))
145 145 except formencode.Invalid as errors:
146 146 h.flash(
147 147 _("Some form inputs contain invalid data."),
148 148 category='error')
149 149 return htmlfill.render(
150 150 render('admin/settings/settings.mako'),
151 151 defaults=errors.value,
152 152 errors=errors.error_dict or {},
153 153 prefix_error=False,
154 154 encoding="UTF-8",
155 155 force_defaults=False
156 156 )
157 157
158 158 try:
159 159 if c.visual.allow_repo_location_change:
160 160 model.update_global_path_setting(
161 161 form_result['paths_root_path'])
162 162
163 163 model.update_global_ssl_setting(form_result['web_push_ssl'])
164 164 model.update_global_hook_settings(form_result)
165 165
166 166 model.create_or_update_global_svn_settings(form_result)
167 167 model.create_or_update_global_hg_settings(form_result)
168 model.create_or_update_global_git_settings(form_result)
168 169 model.create_or_update_global_pr_settings(form_result)
169 170 except Exception:
170 171 log.exception("Exception while updating settings")
171 172 h.flash(_('Error occurred during updating '
172 173 'application settings'), category='error')
173 174 else:
174 175 Session().commit()
175 176 h.flash(_('Updated VCS settings'), category='success')
176 177 return redirect(url('admin_settings_vcs'))
177 178
178 179 return htmlfill.render(
179 180 render('admin/settings/settings.mako'),
180 181 defaults=self._form_defaults(),
181 182 encoding="UTF-8",
182 183 force_defaults=False)
183 184
184 185 @HasPermissionAllDecorator('hg.admin')
185 186 def settings_vcs(self):
186 187 """GET /admin/settings: All items in the collection"""
187 188 # url('admin_settings_vcs')
188 189 c.active = 'vcs'
189 190 model = VcsSettingsModel()
190 191 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
191 192 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
192 193
193 194 # TODO: Replace with request.registry after migrating to pyramid.
194 195 pyramid_settings = get_current_registry().settings
195 196 c.svn_proxy_generate_config = pyramid_settings[generate_config]
196 197
197 198 return htmlfill.render(
198 199 render('admin/settings/settings.mako'),
199 200 defaults=self._form_defaults(),
200 201 encoding="UTF-8",
201 202 force_defaults=False)
202 203
203 204 @HasPermissionAllDecorator('hg.admin')
204 205 @auth.CSRFRequired()
205 206 def settings_mapping_update(self):
206 207 """POST /admin/settings/mapping: All items in the collection"""
207 208 # url('admin_settings_mapping')
208 209 c.active = 'mapping'
209 210 rm_obsolete = request.POST.get('destroy', False)
210 211 invalidate_cache = request.POST.get('invalidate', False)
211 212 log.debug(
212 213 'rescanning repo location with destroy obsolete=%s', rm_obsolete)
213 214
214 215 if invalidate_cache:
215 216 log.debug('invalidating all repositories cache')
216 217 for repo in Repository.get_all():
217 218 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
218 219
219 220 filesystem_repos = ScmModel().repo_scan()
220 221 added, removed = repo2db_mapper(filesystem_repos, rm_obsolete)
221 222 _repr = lambda l: ', '.join(map(safe_unicode, l)) or '-'
222 223 h.flash(_('Repositories successfully '
223 224 'rescanned added: %s ; removed: %s') %
224 225 (_repr(added), _repr(removed)),
225 226 category='success')
226 227 return redirect(url('admin_settings_mapping'))
227 228
228 229 @HasPermissionAllDecorator('hg.admin')
229 230 def settings_mapping(self):
230 231 """GET /admin/settings/mapping: All items in the collection"""
231 232 # url('admin_settings_mapping')
232 233 c.active = 'mapping'
233 234
234 235 return htmlfill.render(
235 236 render('admin/settings/settings.mako'),
236 237 defaults=self._form_defaults(),
237 238 encoding="UTF-8",
238 239 force_defaults=False)
239 240
240 241 @HasPermissionAllDecorator('hg.admin')
241 242 @auth.CSRFRequired()
242 243 def settings_global_update(self):
243 244 """POST /admin/settings/global: All items in the collection"""
244 245 # url('admin_settings_global')
245 246 c.active = 'global'
246 247 c.personal_repo_group_default_pattern = RepoGroupModel()\
247 248 .get_personal_group_name_pattern()
248 249 application_form = ApplicationSettingsForm()()
249 250 try:
250 251 form_result = application_form.to_python(dict(request.POST))
251 252 except formencode.Invalid as errors:
252 253 return htmlfill.render(
253 254 render('admin/settings/settings.mako'),
254 255 defaults=errors.value,
255 256 errors=errors.error_dict or {},
256 257 prefix_error=False,
257 258 encoding="UTF-8",
258 259 force_defaults=False)
259 260
260 261 try:
261 262 settings = [
262 263 ('title', 'rhodecode_title', 'unicode'),
263 264 ('realm', 'rhodecode_realm', 'unicode'),
264 265 ('pre_code', 'rhodecode_pre_code', 'unicode'),
265 266 ('post_code', 'rhodecode_post_code', 'unicode'),
266 267 ('captcha_public_key', 'rhodecode_captcha_public_key', 'unicode'),
267 268 ('captcha_private_key', 'rhodecode_captcha_private_key', 'unicode'),
268 269 ('create_personal_repo_group', 'rhodecode_create_personal_repo_group', 'bool'),
269 270 ('personal_repo_group_pattern', 'rhodecode_personal_repo_group_pattern', 'unicode'),
270 271 ]
271 272 for setting, form_key, type_ in settings:
272 273 sett = SettingsModel().create_or_update_setting(
273 274 setting, form_result[form_key], type_)
274 275 Session().add(sett)
275 276
276 277 Session().commit()
277 278 SettingsModel().invalidate_settings_cache()
278 279 h.flash(_('Updated application settings'), category='success')
279 280 except Exception:
280 281 log.exception("Exception while updating application settings")
281 282 h.flash(
282 283 _('Error occurred during updating application settings'),
283 284 category='error')
284 285
285 286 return redirect(url('admin_settings_global'))
286 287
287 288 @HasPermissionAllDecorator('hg.admin')
288 289 def settings_global(self):
289 290 """GET /admin/settings/global: All items in the collection"""
290 291 # url('admin_settings_global')
291 292 c.active = 'global'
292 293 c.personal_repo_group_default_pattern = RepoGroupModel()\
293 294 .get_personal_group_name_pattern()
294 295
295 296 return htmlfill.render(
296 297 render('admin/settings/settings.mako'),
297 298 defaults=self._form_defaults(),
298 299 encoding="UTF-8",
299 300 force_defaults=False)
300 301
301 302 @HasPermissionAllDecorator('hg.admin')
302 303 @auth.CSRFRequired()
303 304 def settings_visual_update(self):
304 305 """POST /admin/settings/visual: All items in the collection"""
305 306 # url('admin_settings_visual')
306 307 c.active = 'visual'
307 308 application_form = ApplicationVisualisationForm()()
308 309 try:
309 310 form_result = application_form.to_python(dict(request.POST))
310 311 except formencode.Invalid as errors:
311 312 return htmlfill.render(
312 313 render('admin/settings/settings.mako'),
313 314 defaults=errors.value,
314 315 errors=errors.error_dict or {},
315 316 prefix_error=False,
316 317 encoding="UTF-8",
317 318 force_defaults=False
318 319 )
319 320
320 321 try:
321 322 settings = [
322 323 ('show_public_icon', 'rhodecode_show_public_icon', 'bool'),
323 324 ('show_private_icon', 'rhodecode_show_private_icon', 'bool'),
324 325 ('stylify_metatags', 'rhodecode_stylify_metatags', 'bool'),
325 326 ('repository_fields', 'rhodecode_repository_fields', 'bool'),
326 327 ('dashboard_items', 'rhodecode_dashboard_items', 'int'),
327 328 ('admin_grid_items', 'rhodecode_admin_grid_items', 'int'),
328 329 ('show_version', 'rhodecode_show_version', 'bool'),
329 330 ('use_gravatar', 'rhodecode_use_gravatar', 'bool'),
330 331 ('markup_renderer', 'rhodecode_markup_renderer', 'unicode'),
331 332 ('gravatar_url', 'rhodecode_gravatar_url', 'unicode'),
332 333 ('clone_uri_tmpl', 'rhodecode_clone_uri_tmpl', 'unicode'),
333 334 ('support_url', 'rhodecode_support_url', 'unicode'),
334 335 ('show_revision_number', 'rhodecode_show_revision_number', 'bool'),
335 336 ('show_sha_length', 'rhodecode_show_sha_length', 'int'),
336 337 ]
337 338 for setting, form_key, type_ in settings:
338 339 sett = SettingsModel().create_or_update_setting(
339 340 setting, form_result[form_key], type_)
340 341 Session().add(sett)
341 342
342 343 Session().commit()
343 344 SettingsModel().invalidate_settings_cache()
344 345 h.flash(_('Updated visualisation settings'), category='success')
345 346 except Exception:
346 347 log.exception("Exception updating visualization settings")
347 348 h.flash(_('Error occurred during updating '
348 349 'visualisation settings'),
349 350 category='error')
350 351
351 352 return redirect(url('admin_settings_visual'))
352 353
353 354 @HasPermissionAllDecorator('hg.admin')
354 355 def settings_visual(self):
355 356 """GET /admin/settings/visual: All items in the collection"""
356 357 # url('admin_settings_visual')
357 358 c.active = 'visual'
358 359
359 360 return htmlfill.render(
360 361 render('admin/settings/settings.mako'),
361 362 defaults=self._form_defaults(),
362 363 encoding="UTF-8",
363 364 force_defaults=False)
364 365
365 366 @HasPermissionAllDecorator('hg.admin')
366 367 @auth.CSRFRequired()
367 368 def settings_issuetracker_test(self):
368 369 if request.is_xhr:
369 370 return h.urlify_commit_message(
370 371 request.POST.get('test_text', ''),
371 372 'repo_group/test_repo1')
372 373 else:
373 374 raise HTTPBadRequest()
374 375
375 376 @HasPermissionAllDecorator('hg.admin')
376 377 @auth.CSRFRequired()
377 378 def settings_issuetracker_delete(self):
378 379 uid = request.POST.get('uid')
379 380 IssueTrackerSettingsModel().delete_entries(uid)
380 381 h.flash(_('Removed issue tracker entry'), category='success')
381 382 return redirect(url('admin_settings_issuetracker'))
382 383
383 384 @HasPermissionAllDecorator('hg.admin')
384 385 def settings_issuetracker(self):
385 386 """GET /admin/settings/issue-tracker: All items in the collection"""
386 387 # url('admin_settings_issuetracker')
387 388 c.active = 'issuetracker'
388 389 defaults = SettingsModel().get_all_settings()
389 390
390 391 entry_key = 'rhodecode_issuetracker_pat_'
391 392
392 393 c.issuetracker_entries = {}
393 394 for k, v in defaults.items():
394 395 if k.startswith(entry_key):
395 396 uid = k[len(entry_key):]
396 397 c.issuetracker_entries[uid] = None
397 398
398 399 for uid in c.issuetracker_entries:
399 400 c.issuetracker_entries[uid] = AttributeDict({
400 401 'pat': defaults.get('rhodecode_issuetracker_pat_' + uid),
401 402 'url': defaults.get('rhodecode_issuetracker_url_' + uid),
402 403 'pref': defaults.get('rhodecode_issuetracker_pref_' + uid),
403 404 'desc': defaults.get('rhodecode_issuetracker_desc_' + uid),
404 405 })
405 406
406 407 return render('admin/settings/settings.mako')
407 408
408 409 @HasPermissionAllDecorator('hg.admin')
409 410 @auth.CSRFRequired()
410 411 def settings_issuetracker_save(self):
411 412 settings_model = IssueTrackerSettingsModel()
412 413
413 414 form = IssueTrackerPatternsForm()().to_python(request.POST)
414 415 if form:
415 416 for uid in form.get('delete_patterns', []):
416 417 settings_model.delete_entries(uid)
417 418
418 419 for pattern in form.get('patterns', []):
419 420 for setting, value, type_ in pattern:
420 421 sett = settings_model.create_or_update_setting(
421 422 setting, value, type_)
422 423 Session().add(sett)
423 424
424 425 Session().commit()
425 426
426 427 SettingsModel().invalidate_settings_cache()
427 428 h.flash(_('Updated issue tracker entries'), category='success')
428 429 return redirect(url('admin_settings_issuetracker'))
429 430
430 431 @HasPermissionAllDecorator('hg.admin')
431 432 @auth.CSRFRequired()
432 433 def settings_email_update(self):
433 434 """POST /admin/settings/email: All items in the collection"""
434 435 # url('admin_settings_email')
435 436 c.active = 'email'
436 437
437 438 test_email = request.POST.get('test_email')
438 439
439 440 if not test_email:
440 441 h.flash(_('Please enter email address'), category='error')
441 442 return redirect(url('admin_settings_email'))
442 443
443 444 email_kwargs = {
444 445 'date': datetime.datetime.now(),
445 446 'user': c.rhodecode_user,
446 447 'rhodecode_version': c.rhodecode_version
447 448 }
448 449
449 450 (subject, headers, email_body,
450 451 email_body_plaintext) = EmailNotificationModel().render_email(
451 452 EmailNotificationModel.TYPE_EMAIL_TEST, **email_kwargs)
452 453
453 454 recipients = [test_email] if test_email else None
454 455
455 456 run_task(tasks.send_email, recipients, subject,
456 457 email_body_plaintext, email_body)
457 458
458 459 h.flash(_('Send email task created'), category='success')
459 460 return redirect(url('admin_settings_email'))
460 461
461 462 @HasPermissionAllDecorator('hg.admin')
462 463 def settings_email(self):
463 464 """GET /admin/settings/email: All items in the collection"""
464 465 # url('admin_settings_email')
465 466 c.active = 'email'
466 467 c.rhodecode_ini = rhodecode.CONFIG
467 468
468 469 return htmlfill.render(
469 470 render('admin/settings/settings.mako'),
470 471 defaults=self._form_defaults(),
471 472 encoding="UTF-8",
472 473 force_defaults=False)
473 474
474 475 @HasPermissionAllDecorator('hg.admin')
475 476 @auth.CSRFRequired()
476 477 def settings_hooks_update(self):
477 478 """POST or DELETE /admin/settings/hooks: All items in the collection"""
478 479 # url('admin_settings_hooks')
479 480 c.active = 'hooks'
480 481 if c.visual.allow_custom_hooks_settings:
481 482 ui_key = request.POST.get('new_hook_ui_key')
482 483 ui_value = request.POST.get('new_hook_ui_value')
483 484
484 485 hook_id = request.POST.get('hook_id')
485 486 new_hook = False
486 487
487 488 model = SettingsModel()
488 489 try:
489 490 if ui_value and ui_key:
490 491 model.create_or_update_hook(ui_key, ui_value)
491 492 h.flash(_('Added new hook'), category='success')
492 493 new_hook = True
493 494 elif hook_id:
494 495 RhodeCodeUi.delete(hook_id)
495 496 Session().commit()
496 497
497 498 # check for edits
498 499 update = False
499 500 _d = request.POST.dict_of_lists()
500 501 for k, v in zip(_d.get('hook_ui_key', []),
501 502 _d.get('hook_ui_value_new', [])):
502 503 model.create_or_update_hook(k, v)
503 504 update = True
504 505
505 506 if update and not new_hook:
506 507 h.flash(_('Updated hooks'), category='success')
507 508 Session().commit()
508 509 except Exception:
509 510 log.exception("Exception during hook creation")
510 511 h.flash(_('Error occurred during hook creation'),
511 512 category='error')
512 513
513 514 return redirect(url('admin_settings_hooks'))
514 515
515 516 @HasPermissionAllDecorator('hg.admin')
516 517 def settings_hooks(self):
517 518 """GET /admin/settings/hooks: All items in the collection"""
518 519 # url('admin_settings_hooks')
519 520 c.active = 'hooks'
520 521
521 522 model = SettingsModel()
522 523 c.hooks = model.get_builtin_hooks()
523 524 c.custom_hooks = model.get_custom_hooks()
524 525
525 526 return htmlfill.render(
526 527 render('admin/settings/settings.mako'),
527 528 defaults=self._form_defaults(),
528 529 encoding="UTF-8",
529 530 force_defaults=False)
530 531
531 532 @HasPermissionAllDecorator('hg.admin')
532 533 def settings_search(self):
533 534 """GET /admin/settings/search: All items in the collection"""
534 535 # url('admin_settings_search')
535 536 c.active = 'search'
536 537
537 538 from rhodecode.lib.index import searcher_from_config
538 539 searcher = searcher_from_config(config)
539 540 c.statistics = searcher.statistics()
540 541
541 542 return render('admin/settings/settings.mako')
542 543
543 544 @HasPermissionAllDecorator('hg.admin')
544 545 def settings_supervisor(self):
545 546 c.rhodecode_ini = rhodecode.CONFIG
546 547 c.active = 'supervisor'
547 548
548 549 c.supervisor_procs = OrderedDict([
549 550 (SUPERVISOR_MASTER, {}),
550 551 ])
551 552
552 553 c.log_size = 10240
553 554 supervisor = SupervisorModel()
554 555
555 556 _connection = supervisor.get_connection(
556 557 c.rhodecode_ini.get('supervisor.uri'))
557 558 c.connection_error = None
558 559 try:
559 560 _connection.supervisor.getAllProcessInfo()
560 561 except Exception as e:
561 562 c.connection_error = str(e)
562 563 log.exception("Exception reading supervisor data")
563 564 return render('admin/settings/settings.mako')
564 565
565 566 groupid = c.rhodecode_ini.get('supervisor.group_id')
566 567
567 568 # feed our group processes to the main
568 569 for proc in supervisor.get_group_processes(_connection, groupid):
569 570 c.supervisor_procs[proc['name']] = {}
570 571
571 572 for k in c.supervisor_procs.keys():
572 573 try:
573 574 # master process info
574 575 if k == SUPERVISOR_MASTER:
575 576 _data = supervisor.get_master_state(_connection)
576 577 _data['name'] = 'supervisor master'
577 578 _data['description'] = 'pid %s, id: %s, ver: %s' % (
578 579 _data['pid'], _data['id'], _data['ver'])
579 580 c.supervisor_procs[k] = _data
580 581 else:
581 582 procid = groupid + ":" + k
582 583 c.supervisor_procs[k] = supervisor.get_process_info(_connection, procid)
583 584 except Exception as e:
584 585 log.exception("Exception reading supervisor data")
585 586 c.supervisor_procs[k] = {'_rhodecode_error': str(e)}
586 587
587 588 return render('admin/settings/settings.mako')
588 589
589 590 @HasPermissionAllDecorator('hg.admin')
590 591 def settings_supervisor_log(self, procid):
591 592 import rhodecode
592 593 c.rhodecode_ini = rhodecode.CONFIG
593 594 c.active = 'supervisor_tail'
594 595
595 596 supervisor = SupervisorModel()
596 597 _connection = supervisor.get_connection(c.rhodecode_ini.get('supervisor.uri'))
597 598 groupid = c.rhodecode_ini.get('supervisor.group_id')
598 599 procid = groupid + ":" + procid if procid != SUPERVISOR_MASTER else procid
599 600
600 601 c.log_size = 10240
601 602 offset = abs(safe_int(request.GET.get('offset', c.log_size))) * -1
602 603 c.log = supervisor.read_process_log(_connection, procid, offset, 0)
603 604
604 605 return render('admin/settings/settings.mako')
605 606
606 607 @HasPermissionAllDecorator('hg.admin')
607 608 @auth.CSRFRequired()
608 609 def settings_labs_update(self):
609 610 """POST /admin/settings/labs: All items in the collection"""
610 611 # url('admin_settings/labs', method={'POST'})
611 612 c.active = 'labs'
612 613
613 614 application_form = LabsSettingsForm()()
614 615 try:
615 616 form_result = application_form.to_python(dict(request.POST))
616 617 except formencode.Invalid as errors:
617 618 h.flash(
618 619 _('Some form inputs contain invalid data.'),
619 620 category='error')
620 621 return htmlfill.render(
621 622 render('admin/settings/settings.mako'),
622 623 defaults=errors.value,
623 624 errors=errors.error_dict or {},
624 625 prefix_error=False,
625 626 encoding='UTF-8',
626 627 force_defaults=False
627 628 )
628 629
629 630 try:
630 631 session = Session()
631 632 for setting in _LAB_SETTINGS:
632 633 setting_name = setting.key[len('rhodecode_'):]
633 634 sett = SettingsModel().create_or_update_setting(
634 635 setting_name, form_result[setting.key], setting.type)
635 636 session.add(sett)
636 637
637 638 except Exception:
638 639 log.exception('Exception while updating lab settings')
639 640 h.flash(_('Error occurred during updating labs settings'),
640 641 category='error')
641 642 else:
642 643 Session().commit()
643 644 SettingsModel().invalidate_settings_cache()
644 645 h.flash(_('Updated Labs settings'), category='success')
645 646 return redirect(url('admin_settings_labs'))
646 647
647 648 return htmlfill.render(
648 649 render('admin/settings/settings.mako'),
649 650 defaults=self._form_defaults(),
650 651 encoding='UTF-8',
651 652 force_defaults=False)
652 653
653 654 @HasPermissionAllDecorator('hg.admin')
654 655 def settings_labs(self):
655 656 """GET /admin/settings/labs: All items in the collection"""
656 657 # url('admin_settings_labs')
657 658 if not c.labs_active:
658 659 redirect(url('admin_settings'))
659 660
660 661 c.active = 'labs'
661 662 c.lab_settings = _LAB_SETTINGS
662 663
663 664 return htmlfill.render(
664 665 render('admin/settings/settings.mako'),
665 666 defaults=self._form_defaults(),
666 667 encoding='UTF-8',
667 668 force_defaults=False)
668 669
669 670 def _form_defaults(self):
670 671 defaults = SettingsModel().get_all_settings()
671 defaults.update(self._get_hg_ui_settings())
672 defaults.update(self._get_ui_settings())
673
672 674 defaults.update({
673 675 'new_svn_branch': '',
674 676 'new_svn_tag': '',
675 677 })
676 678 return defaults
677 679
678 680
679 681 # :param key: name of the setting including the 'rhodecode_' prefix
680 682 # :param type: the RhodeCodeSetting type to use.
681 683 # :param group: the i18ned group in which we should dispaly this setting
682 684 # :param label: the i18ned label we should display for this setting
683 685 # :param help: the i18ned help we should dispaly for this setting
684 686 LabSetting = collections.namedtuple(
685 687 'LabSetting', ('key', 'type', 'group', 'label', 'help'))
686 688
687 689
688 690 # This list has to be kept in sync with the form
689 691 # rhodecode.model.forms.LabsSettingsForm.
690 692 _LAB_SETTINGS = [
691 693
692 694 ]
@@ -1,553 +1,561 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 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 this is forms validation classes
23 23 http://formencode.org/module-formencode.validators.html
24 24 for list off all availible validators
25 25
26 26 we can create our own validators
27 27
28 28 The table below outlines the options which can be used in a schema in addition to the validators themselves
29 29 pre_validators [] These validators will be applied before the schema
30 30 chained_validators [] These validators will be applied after the schema
31 31 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
32 32 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
33 33 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
34 34 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
35 35
36 36
37 37 <name> = formencode.validators.<name of validator>
38 38 <name> must equal form name
39 39 list=[1,2,3,4,5]
40 40 for SELECT use formencode.All(OneOf(list), Int())
41 41
42 42 """
43 43
44 44 import deform
45 45 import logging
46 46 import formencode
47 47
48 48 from pkg_resources import resource_filename
49 49 from formencode import All, Pipe
50 50
51 51 from pylons.i18n.translation import _
52 52
53 53 from rhodecode import BACKENDS
54 54 from rhodecode.lib import helpers
55 55 from rhodecode.model import validators as v
56 56
57 57 log = logging.getLogger(__name__)
58 58
59 59
60 60 deform_templates = resource_filename('deform', 'templates')
61 61 rhodecode_templates = resource_filename('rhodecode', 'templates/forms')
62 62 search_path = (rhodecode_templates, deform_templates)
63 63
64 64
65 65 class RhodecodeFormZPTRendererFactory(deform.ZPTRendererFactory):
66 66 """ Subclass of ZPTRendererFactory to add rhodecode context variables """
67 67 def __call__(self, template_name, **kw):
68 68 kw['h'] = helpers
69 69 return self.load(template_name)(**kw)
70 70
71 71
72 72 form_renderer = RhodecodeFormZPTRendererFactory(search_path)
73 73 deform.Form.set_default_renderer(form_renderer)
74 74
75 75
76 76 def LoginForm():
77 77 class _LoginForm(formencode.Schema):
78 78 allow_extra_fields = True
79 79 filter_extra_fields = True
80 80 username = v.UnicodeString(
81 81 strip=True,
82 82 min=1,
83 83 not_empty=True,
84 84 messages={
85 85 'empty': _(u'Please enter a login'),
86 86 'tooShort': _(u'Enter a value %(min)i characters long or more')
87 87 }
88 88 )
89 89
90 90 password = v.UnicodeString(
91 91 strip=False,
92 92 min=3,
93 93 not_empty=True,
94 94 messages={
95 95 'empty': _(u'Please enter a password'),
96 96 'tooShort': _(u'Enter %(min)i characters or more')}
97 97 )
98 98
99 99 remember = v.StringBoolean(if_missing=False)
100 100
101 101 chained_validators = [v.ValidAuth()]
102 102 return _LoginForm
103 103
104 104
105 105 def UserForm(edit=False, available_languages=[], old_data={}):
106 106 class _UserForm(formencode.Schema):
107 107 allow_extra_fields = True
108 108 filter_extra_fields = True
109 109 username = All(v.UnicodeString(strip=True, min=1, not_empty=True),
110 110 v.ValidUsername(edit, old_data))
111 111 if edit:
112 112 new_password = All(
113 113 v.ValidPassword(),
114 114 v.UnicodeString(strip=False, min=6, not_empty=False)
115 115 )
116 116 password_confirmation = All(
117 117 v.ValidPassword(),
118 118 v.UnicodeString(strip=False, min=6, not_empty=False),
119 119 )
120 120 admin = v.StringBoolean(if_missing=False)
121 121 else:
122 122 password = All(
123 123 v.ValidPassword(),
124 124 v.UnicodeString(strip=False, min=6, not_empty=True)
125 125 )
126 126 password_confirmation = All(
127 127 v.ValidPassword(),
128 128 v.UnicodeString(strip=False, min=6, not_empty=False)
129 129 )
130 130
131 131 password_change = v.StringBoolean(if_missing=False)
132 132 create_repo_group = v.StringBoolean(if_missing=False)
133 133
134 134 active = v.StringBoolean(if_missing=False)
135 135 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
136 136 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
137 137 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
138 138 extern_name = v.UnicodeString(strip=True)
139 139 extern_type = v.UnicodeString(strip=True)
140 140 language = v.OneOf(available_languages, hideList=False,
141 141 testValueList=True, if_missing=None)
142 142 chained_validators = [v.ValidPasswordsMatch()]
143 143 return _UserForm
144 144
145 145
146 146 def UserGroupForm(edit=False, old_data=None, allow_disabled=False):
147 147 old_data = old_data or {}
148 148
149 149 class _UserGroupForm(formencode.Schema):
150 150 allow_extra_fields = True
151 151 filter_extra_fields = True
152 152
153 153 users_group_name = All(
154 154 v.UnicodeString(strip=True, min=1, not_empty=True),
155 155 v.ValidUserGroup(edit, old_data)
156 156 )
157 157 user_group_description = v.UnicodeString(strip=True, min=1,
158 158 not_empty=False)
159 159
160 160 users_group_active = v.StringBoolean(if_missing=False)
161 161
162 162 if edit:
163 163 # this is user group owner
164 164 user = All(
165 165 v.UnicodeString(not_empty=True),
166 166 v.ValidRepoUser(allow_disabled))
167 167 return _UserGroupForm
168 168
169 169
170 170 def RepoGroupForm(edit=False, old_data=None, available_groups=None,
171 171 can_create_in_root=False, allow_disabled=False):
172 172 old_data = old_data or {}
173 173 available_groups = available_groups or []
174 174
175 175 class _RepoGroupForm(formencode.Schema):
176 176 allow_extra_fields = True
177 177 filter_extra_fields = False
178 178
179 179 group_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
180 180 v.SlugifyName(),)
181 181 group_description = v.UnicodeString(strip=True, min=1,
182 182 not_empty=False)
183 183 group_copy_permissions = v.StringBoolean(if_missing=False)
184 184
185 185 group_parent_id = v.OneOf(available_groups, hideList=False,
186 186 testValueList=True, not_empty=True)
187 187 enable_locking = v.StringBoolean(if_missing=False)
188 188 chained_validators = [
189 189 v.ValidRepoGroup(edit, old_data, can_create_in_root)]
190 190
191 191 if edit:
192 192 # this is repo group owner
193 193 user = All(
194 194 v.UnicodeString(not_empty=True),
195 195 v.ValidRepoUser(allow_disabled))
196 196
197 197 return _RepoGroupForm
198 198
199 199
200 200 def RegisterForm(edit=False, old_data={}):
201 201 class _RegisterForm(formencode.Schema):
202 202 allow_extra_fields = True
203 203 filter_extra_fields = True
204 204 username = All(
205 205 v.ValidUsername(edit, old_data),
206 206 v.UnicodeString(strip=True, min=1, not_empty=True)
207 207 )
208 208 password = All(
209 209 v.ValidPassword(),
210 210 v.UnicodeString(strip=False, min=6, not_empty=True)
211 211 )
212 212 password_confirmation = All(
213 213 v.ValidPassword(),
214 214 v.UnicodeString(strip=False, min=6, not_empty=True)
215 215 )
216 216 active = v.StringBoolean(if_missing=False)
217 217 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
218 218 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
219 219 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
220 220
221 221 chained_validators = [v.ValidPasswordsMatch()]
222 222
223 223 return _RegisterForm
224 224
225 225
226 226 def PasswordResetForm():
227 227 class _PasswordResetForm(formencode.Schema):
228 228 allow_extra_fields = True
229 229 filter_extra_fields = True
230 230 email = All(v.ValidSystemEmail(), v.Email(not_empty=True))
231 231 return _PasswordResetForm
232 232
233 233
234 234 def RepoForm(edit=False, old_data=None, repo_groups=None, landing_revs=None,
235 235 allow_disabled=False):
236 236 old_data = old_data or {}
237 237 repo_groups = repo_groups or []
238 238 landing_revs = landing_revs or []
239 239 supported_backends = BACKENDS.keys()
240 240
241 241 class _RepoForm(formencode.Schema):
242 242 allow_extra_fields = True
243 243 filter_extra_fields = False
244 244 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
245 245 v.SlugifyName())
246 246 repo_group = All(v.CanWriteGroup(old_data),
247 247 v.OneOf(repo_groups, hideList=True))
248 248 repo_type = v.OneOf(supported_backends, required=False,
249 249 if_missing=old_data.get('repo_type'))
250 250 repo_description = v.UnicodeString(strip=True, min=1, not_empty=False)
251 251 repo_private = v.StringBoolean(if_missing=False)
252 252 repo_landing_rev = v.OneOf(landing_revs, hideList=True)
253 253 repo_copy_permissions = v.StringBoolean(if_missing=False)
254 254 clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False))
255 255
256 256 repo_enable_statistics = v.StringBoolean(if_missing=False)
257 257 repo_enable_downloads = v.StringBoolean(if_missing=False)
258 258 repo_enable_locking = v.StringBoolean(if_missing=False)
259 259
260 260 if edit:
261 261 # this is repo owner
262 262 user = All(
263 263 v.UnicodeString(not_empty=True),
264 264 v.ValidRepoUser(allow_disabled))
265 265 clone_uri_change = v.UnicodeString(
266 266 not_empty=False, if_missing=v.Missing)
267 267
268 268 chained_validators = [v.ValidCloneUri(),
269 269 v.ValidRepoName(edit, old_data)]
270 270 return _RepoForm
271 271
272 272
273 273 def RepoPermsForm():
274 274 class _RepoPermsForm(formencode.Schema):
275 275 allow_extra_fields = True
276 276 filter_extra_fields = False
277 277 chained_validators = [v.ValidPerms(type_='repo')]
278 278 return _RepoPermsForm
279 279
280 280
281 281 def RepoGroupPermsForm(valid_recursive_choices):
282 282 class _RepoGroupPermsForm(formencode.Schema):
283 283 allow_extra_fields = True
284 284 filter_extra_fields = False
285 285 recursive = v.OneOf(valid_recursive_choices)
286 286 chained_validators = [v.ValidPerms(type_='repo_group')]
287 287 return _RepoGroupPermsForm
288 288
289 289
290 290 def UserGroupPermsForm():
291 291 class _UserPermsForm(formencode.Schema):
292 292 allow_extra_fields = True
293 293 filter_extra_fields = False
294 294 chained_validators = [v.ValidPerms(type_='user_group')]
295 295 return _UserPermsForm
296 296
297 297
298 298 def RepoFieldForm():
299 299 class _RepoFieldForm(formencode.Schema):
300 300 filter_extra_fields = True
301 301 allow_extra_fields = True
302 302
303 303 new_field_key = All(v.FieldKey(),
304 304 v.UnicodeString(strip=True, min=3, not_empty=True))
305 305 new_field_value = v.UnicodeString(not_empty=False, if_missing=u'')
306 306 new_field_type = v.OneOf(['str', 'unicode', 'list', 'tuple'],
307 307 if_missing='str')
308 308 new_field_label = v.UnicodeString(not_empty=False)
309 309 new_field_desc = v.UnicodeString(not_empty=False)
310 310
311 311 return _RepoFieldForm
312 312
313 313
314 314 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
315 315 repo_groups=[], landing_revs=[]):
316 316 class _RepoForkForm(formencode.Schema):
317 317 allow_extra_fields = True
318 318 filter_extra_fields = False
319 319 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
320 320 v.SlugifyName())
321 321 repo_group = All(v.CanWriteGroup(),
322 322 v.OneOf(repo_groups, hideList=True))
323 323 repo_type = All(v.ValidForkType(old_data), v.OneOf(supported_backends))
324 324 description = v.UnicodeString(strip=True, min=1, not_empty=True)
325 325 private = v.StringBoolean(if_missing=False)
326 326 copy_permissions = v.StringBoolean(if_missing=False)
327 327 fork_parent_id = v.UnicodeString()
328 328 chained_validators = [v.ValidForkName(edit, old_data)]
329 329 landing_rev = v.OneOf(landing_revs, hideList=True)
330 330
331 331 return _RepoForkForm
332 332
333 333
334 334 def ApplicationSettingsForm():
335 335 class _ApplicationSettingsForm(formencode.Schema):
336 336 allow_extra_fields = True
337 337 filter_extra_fields = False
338 338 rhodecode_title = v.UnicodeString(strip=True, max=40, not_empty=False)
339 339 rhodecode_realm = v.UnicodeString(strip=True, min=1, not_empty=True)
340 340 rhodecode_pre_code = v.UnicodeString(strip=True, min=1, not_empty=False)
341 341 rhodecode_post_code = v.UnicodeString(strip=True, min=1, not_empty=False)
342 342 rhodecode_captcha_public_key = v.UnicodeString(strip=True, min=1, not_empty=False)
343 343 rhodecode_captcha_private_key = v.UnicodeString(strip=True, min=1, not_empty=False)
344 344 rhodecode_create_personal_repo_group = v.StringBoolean(if_missing=False)
345 345 rhodecode_personal_repo_group_pattern = v.UnicodeString(strip=True, min=1, not_empty=False)
346 346
347 347 return _ApplicationSettingsForm
348 348
349 349
350 350 def ApplicationVisualisationForm():
351 351 class _ApplicationVisualisationForm(formencode.Schema):
352 352 allow_extra_fields = True
353 353 filter_extra_fields = False
354 354 rhodecode_show_public_icon = v.StringBoolean(if_missing=False)
355 355 rhodecode_show_private_icon = v.StringBoolean(if_missing=False)
356 356 rhodecode_stylify_metatags = v.StringBoolean(if_missing=False)
357 357
358 358 rhodecode_repository_fields = v.StringBoolean(if_missing=False)
359 359 rhodecode_lightweight_journal = v.StringBoolean(if_missing=False)
360 360 rhodecode_dashboard_items = v.Int(min=5, not_empty=True)
361 361 rhodecode_admin_grid_items = v.Int(min=5, not_empty=True)
362 362 rhodecode_show_version = v.StringBoolean(if_missing=False)
363 363 rhodecode_use_gravatar = v.StringBoolean(if_missing=False)
364 364 rhodecode_markup_renderer = v.OneOf(['markdown', 'rst'])
365 365 rhodecode_gravatar_url = v.UnicodeString(min=3)
366 366 rhodecode_clone_uri_tmpl = v.UnicodeString(min=3)
367 367 rhodecode_support_url = v.UnicodeString()
368 368 rhodecode_show_revision_number = v.StringBoolean(if_missing=False)
369 369 rhodecode_show_sha_length = v.Int(min=4, not_empty=True)
370 370
371 371 return _ApplicationVisualisationForm
372 372
373 373
374 374 class _BaseVcsSettingsForm(formencode.Schema):
375 375 allow_extra_fields = True
376 376 filter_extra_fields = False
377 377 hooks_changegroup_repo_size = v.StringBoolean(if_missing=False)
378 378 hooks_changegroup_push_logger = v.StringBoolean(if_missing=False)
379 379 hooks_outgoing_pull_logger = v.StringBoolean(if_missing=False)
380 380
381 # PR/Code-review
382 rhodecode_pr_merge_enabled = v.StringBoolean(if_missing=False)
383 rhodecode_use_outdated_comments = v.StringBoolean(if_missing=False)
384
385 # hg
381 386 extensions_largefiles = v.StringBoolean(if_missing=False)
382 387 phases_publish = v.StringBoolean(if_missing=False)
383
384 rhodecode_pr_merge_enabled = v.StringBoolean(if_missing=False)
385 rhodecode_use_outdated_comments = v.StringBoolean(if_missing=False)
386 388 rhodecode_hg_use_rebase_for_merging = v.StringBoolean(if_missing=False)
387 389
390 # git
391 vcs_git_lfs_enabled = v.StringBoolean(if_missing=False)
392
393 # svn
388 394 vcs_svn_proxy_http_requests_enabled = v.StringBoolean(if_missing=False)
389 395 vcs_svn_proxy_http_server_url = v.UnicodeString(strip=True, if_missing=None)
390 396
391 397
392 398 def ApplicationUiSettingsForm():
393 399 class _ApplicationUiSettingsForm(_BaseVcsSettingsForm):
394 400 web_push_ssl = v.StringBoolean(if_missing=False)
395 401 paths_root_path = All(
396 402 v.ValidPath(),
397 403 v.UnicodeString(strip=True, min=1, not_empty=True)
398 404 )
399 405 largefiles_usercache = All(
400 406 v.ValidPath(),
401 v.UnicodeString(strip=True, min=2, not_empty=True)
402 )
407 v.UnicodeString(strip=True, min=2, not_empty=True))
408 vcs_git_lfs_store_location = All(
409 v.ValidPath(),
410 v.UnicodeString(strip=True, min=2, not_empty=True))
403 411 extensions_hgsubversion = v.StringBoolean(if_missing=False)
404 412 extensions_hggit = v.StringBoolean(if_missing=False)
405 413 new_svn_branch = v.ValidSvnPattern(section='vcs_svn_branch')
406 414 new_svn_tag = v.ValidSvnPattern(section='vcs_svn_tag')
407 415
408 416 return _ApplicationUiSettingsForm
409 417
410 418
411 419 def RepoVcsSettingsForm(repo_name):
412 420 class _RepoVcsSettingsForm(_BaseVcsSettingsForm):
413 421 inherit_global_settings = v.StringBoolean(if_missing=False)
414 422 new_svn_branch = v.ValidSvnPattern(
415 423 section='vcs_svn_branch', repo_name=repo_name)
416 424 new_svn_tag = v.ValidSvnPattern(
417 425 section='vcs_svn_tag', repo_name=repo_name)
418 426
419 427 return _RepoVcsSettingsForm
420 428
421 429
422 430 def LabsSettingsForm():
423 431 class _LabSettingsForm(formencode.Schema):
424 432 allow_extra_fields = True
425 433 filter_extra_fields = False
426 434
427 435 return _LabSettingsForm
428 436
429 437
430 438 def ApplicationPermissionsForm(
431 439 register_choices, password_reset_choices, extern_activate_choices):
432 440 class _DefaultPermissionsForm(formencode.Schema):
433 441 allow_extra_fields = True
434 442 filter_extra_fields = True
435 443
436 444 anonymous = v.StringBoolean(if_missing=False)
437 445 default_register = v.OneOf(register_choices)
438 446 default_register_message = v.UnicodeString()
439 447 default_password_reset = v.OneOf(password_reset_choices)
440 448 default_extern_activate = v.OneOf(extern_activate_choices)
441 449
442 450 return _DefaultPermissionsForm
443 451
444 452
445 453 def ObjectPermissionsForm(repo_perms_choices, group_perms_choices,
446 454 user_group_perms_choices):
447 455 class _ObjectPermissionsForm(formencode.Schema):
448 456 allow_extra_fields = True
449 457 filter_extra_fields = True
450 458 overwrite_default_repo = v.StringBoolean(if_missing=False)
451 459 overwrite_default_group = v.StringBoolean(if_missing=False)
452 460 overwrite_default_user_group = v.StringBoolean(if_missing=False)
453 461 default_repo_perm = v.OneOf(repo_perms_choices)
454 462 default_group_perm = v.OneOf(group_perms_choices)
455 463 default_user_group_perm = v.OneOf(user_group_perms_choices)
456 464
457 465 return _ObjectPermissionsForm
458 466
459 467
460 468 def UserPermissionsForm(create_choices, create_on_write_choices,
461 469 repo_group_create_choices, user_group_create_choices,
462 470 fork_choices, inherit_default_permissions_choices):
463 471 class _DefaultPermissionsForm(formencode.Schema):
464 472 allow_extra_fields = True
465 473 filter_extra_fields = True
466 474
467 475 anonymous = v.StringBoolean(if_missing=False)
468 476
469 477 default_repo_create = v.OneOf(create_choices)
470 478 default_repo_create_on_write = v.OneOf(create_on_write_choices)
471 479 default_user_group_create = v.OneOf(user_group_create_choices)
472 480 default_repo_group_create = v.OneOf(repo_group_create_choices)
473 481 default_fork_create = v.OneOf(fork_choices)
474 482 default_inherit_default_permissions = v.OneOf(inherit_default_permissions_choices)
475 483
476 484 return _DefaultPermissionsForm
477 485
478 486
479 487 def UserIndividualPermissionsForm():
480 488 class _DefaultPermissionsForm(formencode.Schema):
481 489 allow_extra_fields = True
482 490 filter_extra_fields = True
483 491
484 492 inherit_default_permissions = v.StringBoolean(if_missing=False)
485 493
486 494 return _DefaultPermissionsForm
487 495
488 496
489 497 def DefaultsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
490 498 class _DefaultsForm(formencode.Schema):
491 499 allow_extra_fields = True
492 500 filter_extra_fields = True
493 501 default_repo_type = v.OneOf(supported_backends)
494 502 default_repo_private = v.StringBoolean(if_missing=False)
495 503 default_repo_enable_statistics = v.StringBoolean(if_missing=False)
496 504 default_repo_enable_downloads = v.StringBoolean(if_missing=False)
497 505 default_repo_enable_locking = v.StringBoolean(if_missing=False)
498 506
499 507 return _DefaultsForm
500 508
501 509
502 510 def AuthSettingsForm():
503 511 class _AuthSettingsForm(formencode.Schema):
504 512 allow_extra_fields = True
505 513 filter_extra_fields = True
506 514 auth_plugins = All(v.ValidAuthPlugins(),
507 515 v.UniqueListFromString()(not_empty=True))
508 516
509 517 return _AuthSettingsForm
510 518
511 519
512 520 def UserExtraEmailForm():
513 521 class _UserExtraEmailForm(formencode.Schema):
514 522 email = All(v.UniqSystemEmail(), v.Email(not_empty=True))
515 523 return _UserExtraEmailForm
516 524
517 525
518 526 def UserExtraIpForm():
519 527 class _UserExtraIpForm(formencode.Schema):
520 528 ip = v.ValidIp()(not_empty=True)
521 529 return _UserExtraIpForm
522 530
523 531
524 532
525 533 def PullRequestForm(repo_id):
526 534 class ReviewerForm(formencode.Schema):
527 535 user_id = v.Int(not_empty=True)
528 536 reasons = All()
529 537
530 538 class _PullRequestForm(formencode.Schema):
531 539 allow_extra_fields = True
532 540 filter_extra_fields = True
533 541
534 542 user = v.UnicodeString(strip=True, required=True)
535 543 source_repo = v.UnicodeString(strip=True, required=True)
536 544 source_ref = v.UnicodeString(strip=True, required=True)
537 545 target_repo = v.UnicodeString(strip=True, required=True)
538 546 target_ref = v.UnicodeString(strip=True, required=True)
539 547 revisions = All(#v.NotReviewedRevisions(repo_id)(),
540 548 v.UniqueList()(not_empty=True))
541 549 review_members = formencode.ForEach(ReviewerForm())
542 550 pullrequest_title = v.UnicodeString(strip=True, required=True)
543 551 pullrequest_desc = v.UnicodeString(strip=True, required=False)
544 552
545 553 return _PullRequestForm
546 554
547 555
548 556 def IssueTrackerPatternsForm():
549 557 class _IssueTrackerPatternsForm(formencode.Schema):
550 558 allow_extra_fields = True
551 559 filter_extra_fields = False
552 560 chained_validators = [v.ValidPattern()]
553 561 return _IssueTrackerPatternsForm
@@ -1,737 +1,776 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 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.utils2 import (
28 28 Optional, AttributeDict, safe_str, remove_prefix, str2bool)
29 29 from rhodecode.lib.vcs.backends import base
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_PRETX_PUSH,
54 54 RhodeCodeUi.HOOK_PULL, 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 204 def invalidate_settings_cache(self):
205 205 namespace = 'rhodecode_settings'
206 206 cache_manager = caches.get_cache_manager('sql_cache_short', namespace)
207 207 caches.clear_cache_manager(cache_manager)
208 208
209 209 def get_all_settings(self, cache=False):
210 210 def _compute():
211 211 q = self._get_settings_query()
212 212 if not q:
213 213 raise Exception('Could not get application settings !')
214 214
215 215 settings = {
216 216 'rhodecode_' + result.app_settings_name: result.app_settings_value
217 217 for result in q
218 218 }
219 219 return settings
220 220
221 221 if cache:
222 222 log.debug('Fetching app settings using cache')
223 223 repo = self._get_repo(self.repo) if self.repo else None
224 224 namespace = 'rhodecode_settings'
225 225 cache_manager = caches.get_cache_manager(
226 226 'sql_cache_short', namespace)
227 227 _cache_key = (
228 228 "get_repo_{}_settings".format(repo.repo_id)
229 229 if repo else "get_app_settings")
230 230
231 231 return cache_manager.get(_cache_key, createfunc=_compute)
232 232
233 233 else:
234 234 return _compute()
235 235
236 236 def get_auth_settings(self):
237 237 q = self._get_settings_query()
238 238 q = q.filter(
239 239 self.SettingsDbModel.app_settings_name.startswith('auth_'))
240 240 rows = q.all()
241 241 auth_settings = {
242 242 row.app_settings_name: row.app_settings_value for row in rows}
243 243 return auth_settings
244 244
245 245 def get_auth_plugins(self):
246 246 auth_plugins = self.get_setting_by_name("auth_plugins")
247 247 return auth_plugins.app_settings_value
248 248
249 249 def get_default_repo_settings(self, strip_prefix=False):
250 250 q = self._get_settings_query()
251 251 q = q.filter(
252 252 self.SettingsDbModel.app_settings_name.startswith('default_'))
253 253 rows = q.all()
254 254
255 255 result = {}
256 256 for row in rows:
257 257 key = row.app_settings_name
258 258 if strip_prefix:
259 259 key = remove_prefix(key, prefix='default_')
260 260 result.update({key: row.app_settings_value})
261 261 return result
262 262
263 263 def get_repo(self):
264 264 repo = self._get_repo(self.repo)
265 265 if not repo:
266 266 raise Exception(
267 267 'Repository `{}` cannot be found inside the database'.format(
268 268 self.repo))
269 269 return repo
270 270
271 271 def _filter_by_repo(self, model, query):
272 272 if self.repo:
273 273 repo = self.get_repo()
274 274 query = query.filter(model.repository_id == repo.repo_id)
275 275 return query
276 276
277 277 def _get_hooks(self, query):
278 278 query = query.filter(self.UiDbModel.ui_section == self.HOOKS_SECTION)
279 279 query = self._filter_by_repo(RepoRhodeCodeUi, query)
280 280 return query.all()
281 281
282 282 def _get_settings_query(self):
283 283 q = self.SettingsDbModel.query()
284 284 return self._filter_by_repo(RepoRhodeCodeSetting, q)
285 285
286 286 def list_enabled_social_plugins(self, settings):
287 287 enabled = []
288 288 for plug in SOCIAL_PLUGINS_LIST:
289 289 if str2bool(settings.get('rhodecode_auth_{}_enabled'.format(plug)
290 290 )):
291 291 enabled.append(plug)
292 292 return enabled
293 293
294 294
295 295 def assert_repo_settings(func):
296 296 @wraps(func)
297 297 def _wrapper(self, *args, **kwargs):
298 298 if not self.repo_settings:
299 299 raise Exception('Repository is not specified')
300 300 return func(self, *args, **kwargs)
301 301 return _wrapper
302 302
303 303
304 304 class IssueTrackerSettingsModel(object):
305 305 INHERIT_SETTINGS = 'inherit_issue_tracker_settings'
306 306 SETTINGS_PREFIX = 'issuetracker_'
307 307
308 308 def __init__(self, sa=None, repo=None):
309 309 self.global_settings = SettingsModel(sa=sa)
310 310 self.repo_settings = SettingsModel(sa=sa, repo=repo) if repo else None
311 311
312 312 @property
313 313 def inherit_global_settings(self):
314 314 if not self.repo_settings:
315 315 return True
316 316 setting = self.repo_settings.get_setting_by_name(self.INHERIT_SETTINGS)
317 317 return setting.app_settings_value if setting else True
318 318
319 319 @inherit_global_settings.setter
320 320 def inherit_global_settings(self, value):
321 321 if self.repo_settings:
322 322 settings = self.repo_settings.create_or_update_setting(
323 323 self.INHERIT_SETTINGS, value, type_='bool')
324 324 Session().add(settings)
325 325
326 326 def _get_keyname(self, key, uid, prefix=''):
327 327 return '{0}{1}{2}_{3}'.format(
328 328 prefix, self.SETTINGS_PREFIX, key, uid)
329 329
330 330 def _make_dict_for_settings(self, qs):
331 331 prefix_match = self._get_keyname('pat', '', 'rhodecode_')
332 332
333 333 issuetracker_entries = {}
334 334 # create keys
335 335 for k, v in qs.items():
336 336 if k.startswith(prefix_match):
337 337 uid = k[len(prefix_match):]
338 338 issuetracker_entries[uid] = None
339 339
340 340 # populate
341 341 for uid in issuetracker_entries:
342 342 issuetracker_entries[uid] = AttributeDict({
343 343 'pat': qs.get(self._get_keyname('pat', uid, 'rhodecode_')),
344 344 'url': qs.get(self._get_keyname('url', uid, 'rhodecode_')),
345 345 'pref': qs.get(self._get_keyname('pref', uid, 'rhodecode_')),
346 346 'desc': qs.get(self._get_keyname('desc', uid, 'rhodecode_')),
347 347 })
348 348 return issuetracker_entries
349 349
350 350 def get_global_settings(self, cache=False):
351 351 """
352 352 Returns list of global issue tracker settings
353 353 """
354 354 defaults = self.global_settings.get_all_settings(cache=cache)
355 355 settings = self._make_dict_for_settings(defaults)
356 356 return settings
357 357
358 358 def get_repo_settings(self, cache=False):
359 359 """
360 360 Returns list of issue tracker settings per repository
361 361 """
362 362 if not self.repo_settings:
363 363 raise Exception('Repository is not specified')
364 364 all_settings = self.repo_settings.get_all_settings(cache=cache)
365 365 settings = self._make_dict_for_settings(all_settings)
366 366 return settings
367 367
368 368 def get_settings(self, cache=False):
369 369 if self.inherit_global_settings:
370 370 return self.get_global_settings(cache=cache)
371 371 else:
372 372 return self.get_repo_settings(cache=cache)
373 373
374 374 def delete_entries(self, uid):
375 375 if self.repo_settings:
376 376 all_patterns = self.get_repo_settings()
377 377 settings_model = self.repo_settings
378 378 else:
379 379 all_patterns = self.get_global_settings()
380 380 settings_model = self.global_settings
381 381 entries = all_patterns.get(uid)
382 382
383 383 for del_key in entries:
384 384 setting_name = self._get_keyname(del_key, uid)
385 385 entry = settings_model.get_setting_by_name(setting_name)
386 386 if entry:
387 387 Session().delete(entry)
388 388
389 389 Session().commit()
390 390
391 391 def create_or_update_setting(
392 392 self, name, val=Optional(''), type_=Optional('unicode')):
393 393 if self.repo_settings:
394 394 setting = self.repo_settings.create_or_update_setting(
395 395 name, val, type_)
396 396 else:
397 397 setting = self.global_settings.create_or_update_setting(
398 398 name, val, type_)
399 399 return setting
400 400
401 401
402 402 class VcsSettingsModel(object):
403 403
404 404 INHERIT_SETTINGS = 'inherit_vcs_settings'
405 405 GENERAL_SETTINGS = (
406 406 'use_outdated_comments',
407 407 'pr_merge_enabled',
408 408 'hg_use_rebase_for_merging')
409 409
410 410 HOOKS_SETTINGS = (
411 411 ('hooks', 'changegroup.repo_size'),
412 412 ('hooks', 'changegroup.push_logger'),
413 ('hooks', 'outgoing.pull_logger'))
413 ('hooks', 'outgoing.pull_logger'),)
414 414 HG_SETTINGS = (
415 415 ('extensions', 'largefiles'),
416 ('phases', 'publish'))
416 ('phases', 'publish'),)
417 GIT_SETTINGS = (
418 ('vcs_git_lfs', 'enabled'),)
419
417 420 GLOBAL_HG_SETTINGS = (
418 421 ('extensions', 'largefiles'),
419 422 ('largefiles', 'usercache'),
420 423 ('phases', 'publish'),
421 424 ('extensions', 'hgsubversion'))
425 GLOBAL_GIT_SETTINGS = (
426 ('vcs_git_lfs', 'enabled'),
427 ('vcs_git_lfs', 'store_location'))
422 428 GLOBAL_SVN_SETTINGS = (
423 429 ('vcs_svn_proxy', 'http_requests_enabled'),
424 430 ('vcs_svn_proxy', 'http_server_url'))
425 431
426 432 SVN_BRANCH_SECTION = 'vcs_svn_branch'
427 433 SVN_TAG_SECTION = 'vcs_svn_tag'
428 434 SSL_SETTING = ('web', 'push_ssl')
429 435 PATH_SETTING = ('paths', '/')
430 436
431 437 def __init__(self, sa=None, repo=None):
432 438 self.global_settings = SettingsModel(sa=sa)
433 439 self.repo_settings = SettingsModel(sa=sa, repo=repo) if repo else None
434 self._ui_settings = self.HG_SETTINGS + self.HOOKS_SETTINGS
440 self._ui_settings = (
441 self.HG_SETTINGS + self.GIT_SETTINGS + self.HOOKS_SETTINGS)
435 442 self._svn_sections = (self.SVN_BRANCH_SECTION, self.SVN_TAG_SECTION)
436 443
437 444 @property
438 445 @assert_repo_settings
439 446 def inherit_global_settings(self):
440 447 setting = self.repo_settings.get_setting_by_name(self.INHERIT_SETTINGS)
441 448 return setting.app_settings_value if setting else True
442 449
443 450 @inherit_global_settings.setter
444 451 @assert_repo_settings
445 452 def inherit_global_settings(self, value):
446 453 self.repo_settings.create_or_update_setting(
447 454 self.INHERIT_SETTINGS, value, type_='bool')
448 455
449 456 def get_global_svn_branch_patterns(self):
450 457 return self.global_settings.get_ui_by_section(self.SVN_BRANCH_SECTION)
451 458
452 459 @assert_repo_settings
453 460 def get_repo_svn_branch_patterns(self):
454 461 return self.repo_settings.get_ui_by_section(self.SVN_BRANCH_SECTION)
455 462
456 463 def get_global_svn_tag_patterns(self):
457 464 return self.global_settings.get_ui_by_section(self.SVN_TAG_SECTION)
458 465
459 466 @assert_repo_settings
460 467 def get_repo_svn_tag_patterns(self):
461 468 return self.repo_settings.get_ui_by_section(self.SVN_TAG_SECTION)
462 469
463 470 def get_global_settings(self):
464 471 return self._collect_all_settings(global_=True)
465 472
466 473 @assert_repo_settings
467 474 def get_repo_settings(self):
468 475 return self._collect_all_settings(global_=False)
469 476
470 477 @assert_repo_settings
471 478 def create_or_update_repo_settings(
472 479 self, data, inherit_global_settings=False):
473 480 from rhodecode.model.scm import ScmModel
474 481
475 482 self.inherit_global_settings = inherit_global_settings
476 483
477 484 repo = self.repo_settings.get_repo()
478 485 if not inherit_global_settings:
479 486 if repo.repo_type == 'svn':
480 487 self.create_repo_svn_settings(data)
481 488 else:
482 489 self.create_or_update_repo_hook_settings(data)
483 490 self.create_or_update_repo_pr_settings(data)
484 491
485 492 if repo.repo_type == 'hg':
486 493 self.create_or_update_repo_hg_settings(data)
487 494
495 if repo.repo_type == 'git':
496 self.create_or_update_repo_git_settings(data)
497
488 498 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
489 499
490 500 @assert_repo_settings
491 501 def create_or_update_repo_hook_settings(self, data):
492 502 for section, key in self.HOOKS_SETTINGS:
493 503 data_key = self._get_form_ui_key(section, key)
494 504 if data_key not in data:
495 505 raise ValueError(
496 506 'The given data does not contain {} key'.format(data_key))
497 507
498 508 active = data.get(data_key)
499 509 repo_setting = self.repo_settings.get_ui_by_section_and_key(
500 510 section, key)
501 511 if not repo_setting:
502 512 global_setting = self.global_settings.\
503 513 get_ui_by_section_and_key(section, key)
504 514 self.repo_settings.create_ui_section_value(
505 515 section, global_setting.ui_value, key=key, active=active)
506 516 else:
507 517 repo_setting.ui_active = active
508 518 Session().add(repo_setting)
509 519
510 520 def update_global_hook_settings(self, data):
511 521 for section, key in self.HOOKS_SETTINGS:
512 522 data_key = self._get_form_ui_key(section, key)
513 523 if data_key not in data:
514 524 raise ValueError(
515 525 'The given data does not contain {} key'.format(data_key))
516 526 active = data.get(data_key)
517 527 repo_setting = self.global_settings.get_ui_by_section_and_key(
518 528 section, key)
519 529 repo_setting.ui_active = active
520 530 Session().add(repo_setting)
521 531
522 532 @assert_repo_settings
523 533 def create_or_update_repo_pr_settings(self, data):
524 534 return self._create_or_update_general_settings(
525 535 self.repo_settings, data)
526 536
527 537 def create_or_update_global_pr_settings(self, data):
528 538 return self._create_or_update_general_settings(
529 539 self.global_settings, data)
530 540
531 541 @assert_repo_settings
532 542 def create_repo_svn_settings(self, data):
533 543 return self._create_svn_settings(self.repo_settings, data)
534 544
535 545 @assert_repo_settings
536 546 def create_or_update_repo_hg_settings(self, data):
537 largefiles, phases = self.HG_SETTINGS
538 largefiles_key, phases_key = self._get_settings_keys(
539 self.HG_SETTINGS, data)
547 largefiles, phases = \
548 self.HG_SETTINGS
549 largefiles_key, phases_key = \
550 self._get_settings_keys(self.HG_SETTINGS, data)
551
540 552 self._create_or_update_ui(
541 553 self.repo_settings, *largefiles, value='',
542 554 active=data[largefiles_key])
543 555 self._create_or_update_ui(
544 556 self.repo_settings, *phases, value=safe_str(data[phases_key]))
545 557
546 558 def create_or_update_global_hg_settings(self, data):
547 559 largefiles, largefiles_store, phases, hgsubversion \
548 560 = self.GLOBAL_HG_SETTINGS
549 561 largefiles_key, largefiles_store_key, phases_key, subversion_key \
550 562 = self._get_settings_keys(self.GLOBAL_HG_SETTINGS, data)
551
552 563 self._create_or_update_ui(
553 564 self.global_settings, *largefiles, value='',
554 565 active=data[largefiles_key])
555 566 self._create_or_update_ui(
556 567 self.global_settings, *largefiles_store,
557 568 value=data[largefiles_store_key])
558 569 self._create_or_update_ui(
559 570 self.global_settings, *phases, value=safe_str(data[phases_key]))
560 571 self._create_or_update_ui(
561 572 self.global_settings, *hgsubversion, active=data[subversion_key])
562 573
574 def create_or_update_repo_git_settings(self, data):
575 # NOTE(marcink): # comma make unpack work properly
576 lfs_enabled, \
577 = self.GIT_SETTINGS
578
579 lfs_enabled_key, \
580 = self._get_settings_keys(self.GIT_SETTINGS, data)
581
582 self._create_or_update_ui(
583 self.repo_settings, *lfs_enabled, value=data[lfs_enabled_key],
584 active=data[lfs_enabled_key])
585
586 def create_or_update_global_git_settings(self, data):
587 lfs_enabled, lfs_store_location \
588 = self.GLOBAL_GIT_SETTINGS
589 lfs_enabled_key, lfs_store_location_key \
590 = self._get_settings_keys(self.GLOBAL_GIT_SETTINGS, data)
591
592 self._create_or_update_ui(
593 self.global_settings, *lfs_enabled, value=data[lfs_enabled_key],
594 active=data[lfs_enabled_key])
595 self._create_or_update_ui(
596 self.global_settings, *lfs_store_location,
597 value=data[lfs_store_location_key])
598
563 599 def create_or_update_global_svn_settings(self, data):
564 600 # branch/tags patterns
565 601 self._create_svn_settings(self.global_settings, data)
566 602
567 603 http_requests_enabled, http_server_url = self.GLOBAL_SVN_SETTINGS
568 604 http_requests_enabled_key, http_server_url_key = self._get_settings_keys(
569 605 self.GLOBAL_SVN_SETTINGS, data)
570 606
571 607 self._create_or_update_ui(
572 608 self.global_settings, *http_requests_enabled,
573 609 value=safe_str(data[http_requests_enabled_key]))
574 610 self._create_or_update_ui(
575 611 self.global_settings, *http_server_url,
576 612 value=data[http_server_url_key])
577 613
578 614 def update_global_ssl_setting(self, value):
579 615 self._create_or_update_ui(
580 616 self.global_settings, *self.SSL_SETTING, value=value)
581 617
582 618 def update_global_path_setting(self, value):
583 619 self._create_or_update_ui(
584 620 self.global_settings, *self.PATH_SETTING, value=value)
585 621
586 622 @assert_repo_settings
587 623 def delete_repo_svn_pattern(self, id_):
588 624 self.repo_settings.delete_ui(id_)
589 625
590 626 def delete_global_svn_pattern(self, id_):
591 627 self.global_settings.delete_ui(id_)
592 628
593 629 @assert_repo_settings
594 630 def get_repo_ui_settings(self, section=None, key=None):
595 631 global_uis = self.global_settings.get_ui(section, key)
596 632 repo_uis = self.repo_settings.get_ui(section, key)
597 633 filtered_repo_uis = self._filter_ui_settings(repo_uis)
598 634 filtered_repo_uis_keys = [
599 635 (s.section, s.key) for s in filtered_repo_uis]
600 636
601 637 def _is_global_ui_filtered(ui):
602 638 return (
603 639 (ui.section, ui.key) in filtered_repo_uis_keys
604 640 or ui.section in self._svn_sections)
605 641
606 642 filtered_global_uis = [
607 643 ui for ui in global_uis if not _is_global_ui_filtered(ui)]
608 644
609 645 return filtered_global_uis + filtered_repo_uis
610 646
611 647 def get_global_ui_settings(self, section=None, key=None):
612 648 return self.global_settings.get_ui(section, key)
613 649
614 650 def get_ui_settings_as_config_obj(self, section=None, key=None):
615 651 config = base.Config()
616 652
617 653 ui_settings = self.get_ui_settings(section=section, key=key)
618 654
619 655 for entry in ui_settings:
620 656 config.set(entry.section, entry.key, entry.value)
621 657
622 658 return config
623 659
624 660 def get_ui_settings(self, section=None, key=None):
625 661 if not self.repo_settings or self.inherit_global_settings:
626 662 return self.get_global_ui_settings(section, key)
627 663 else:
628 664 return self.get_repo_ui_settings(section, key)
629 665
630 666 def get_svn_patterns(self, section=None):
631 667 if not self.repo_settings:
632 668 return self.get_global_ui_settings(section)
633 669 else:
634 670 return self.get_repo_ui_settings(section)
635 671
636 672 @assert_repo_settings
637 673 def get_repo_general_settings(self):
638 674 global_settings = self.global_settings.get_all_settings()
639 675 repo_settings = self.repo_settings.get_all_settings()
640 676 filtered_repo_settings = self._filter_general_settings(repo_settings)
641 677 global_settings.update(filtered_repo_settings)
642 678 return global_settings
643 679
644 680 def get_global_general_settings(self):
645 681 return self.global_settings.get_all_settings()
646 682
647 683 def get_general_settings(self):
648 684 if not self.repo_settings or self.inherit_global_settings:
649 685 return self.get_global_general_settings()
650 686 else:
651 687 return self.get_repo_general_settings()
652 688
653 689 def get_repos_location(self):
654 690 return self.global_settings.get_ui_by_key('/').ui_value
655 691
656 692 def _filter_ui_settings(self, settings):
657 693 filtered_settings = [
658 694 s for s in settings if self._should_keep_setting(s)]
659 695 return filtered_settings
660 696
661 697 def _should_keep_setting(self, setting):
662 698 keep = (
663 699 (setting.section, setting.key) in self._ui_settings or
664 700 setting.section in self._svn_sections)
665 701 return keep
666 702
667 703 def _filter_general_settings(self, settings):
668 704 keys = ['rhodecode_{}'.format(key) for key in self.GENERAL_SETTINGS]
669 705 return {
670 706 k: settings[k]
671 707 for k in settings if k in keys}
672 708
673 709 def _collect_all_settings(self, global_=False):
674 710 settings = self.global_settings if global_ else self.repo_settings
675 711 result = {}
676 712
677 713 for section, key in self._ui_settings:
678 714 ui = settings.get_ui_by_section_and_key(section, key)
679 715 result_key = self._get_form_ui_key(section, key)
716
680 717 if ui:
681 718 if section in ('hooks', 'extensions'):
682 719 result[result_key] = ui.ui_active
720 elif result_key in ['vcs_git_lfs_enabled']:
721 result[result_key] = ui.ui_active
683 722 else:
684 723 result[result_key] = ui.ui_value
685 724
686 725 for name in self.GENERAL_SETTINGS:
687 726 setting = settings.get_setting_by_name(name)
688 727 if setting:
689 728 result_key = 'rhodecode_{}'.format(name)
690 729 result[result_key] = setting.app_settings_value
691 730
692 731 return result
693 732
694 733 def _get_form_ui_key(self, section, key):
695 734 return '{section}_{key}'.format(
696 735 section=section, key=key.replace('.', '_'))
697 736
698 737 def _create_or_update_ui(
699 738 self, settings, section, key, value=None, active=None):
700 739 ui = settings.get_ui_by_section_and_key(section, key)
701 740 if not ui:
702 741 active = True if active is None else active
703 742 settings.create_ui_section_value(
704 743 section, value, key=key, active=active)
705 744 else:
706 745 if active is not None:
707 746 ui.ui_active = active
708 747 if value is not None:
709 748 ui.ui_value = value
710 749 Session().add(ui)
711 750
712 751 def _create_svn_settings(self, settings, data):
713 752 svn_settings = {
714 753 'new_svn_branch': self.SVN_BRANCH_SECTION,
715 754 'new_svn_tag': self.SVN_TAG_SECTION
716 755 }
717 756 for key in svn_settings:
718 757 if data.get(key):
719 758 settings.create_ui_section_value(svn_settings[key], data[key])
720 759
721 760 def _create_or_update_general_settings(self, settings, data):
722 761 for name in self.GENERAL_SETTINGS:
723 762 data_key = 'rhodecode_{}'.format(name)
724 763 if data_key not in data:
725 764 raise ValueError(
726 765 'The given data does not contain {} key'.format(data_key))
727 766 setting = settings.create_or_update_setting(
728 767 name, data[data_key], 'bool')
729 768 Session().add(setting)
730 769
731 770 def _get_settings_keys(self, settings, data):
732 771 data_keys = [self._get_form_ui_key(*s) for s in settings]
733 772 for data_key in data_keys:
734 773 if data_key not in data:
735 774 raise ValueError(
736 775 'The given data does not contain {} key'.format(data_key))
737 776 return data_keys
@@ -1,291 +1,324 b''
1 1 ## snippet for displaying vcs settings
2 2 ## usage:
3 3 ## <%namespace name="vcss" file="/base/vcssettings.mako"/>
4 4 ## ${vcss.vcs_settings_fields()}
5 5
6 6 <%def name="vcs_settings_fields(suffix='', svn_branch_patterns=None, svn_tag_patterns=None, repo_type=None, display_globals=False, allow_repo_location_change=False, **kwargs)">
7 7 % if display_globals:
8 8 <div class="panel panel-default">
9 9 <div class="panel-heading" id="general">
10 10 <h3 class="panel-title">${_('General')}</h3>
11 11 </div>
12 12 <div class="panel-body">
13 13 <div class="field">
14 14 <div class="checkbox">
15 15 ${h.checkbox('web_push_ssl' + suffix, 'True')}
16 16 <label for="web_push_ssl${suffix}">${_('Require SSL for vcs operations')}</label>
17 17 </div>
18 18 <div class="label">
19 19 <span class="help-block">${_('Activate to set RhodeCode to require SSL for pushing or pulling. If SSL certificate is missing it will return a HTTP Error 406: Not Acceptable.')}</span>
20 20 </div>
21 21 </div>
22 22 </div>
23 23 </div>
24 24 % endif
25 25
26 26 % if display_globals:
27 27 <div class="panel panel-default">
28 28 <div class="panel-heading">
29 29 <h3 class="panel-title">${_('Main Storage Location')}</h3>
30 30 </div>
31 31 <div class="panel-body">
32 32 <div class="field">
33 33 <div class="inputx locked_input">
34 34 %if allow_repo_location_change:
35 35 ${h.text('paths_root_path',size=59,readonly="readonly", class_="disabled")}
36 36 <span id="path_unlock" class="tooltip"
37 37 title="${h.tooltip(_('Click to unlock. You must restart RhodeCode in order to make this setting take effect.'))}">
38 38 <div class="btn btn-default lock_input_button"><i id="path_unlock_icon" class="icon-lock"></i></div>
39 39 </span>
40 40 %else:
41 41 ${_('Repository location change is disabled. You can enable this by changing the `allow_repo_location_change` inside .ini file.')}
42 42 ## form still requires this but we cannot internally change it anyway
43 43 ${h.hidden('paths_root_path',size=30,readonly="readonly", class_="disabled")}
44 44 %endif
45 45 </div>
46 46 </div>
47 47 <div class="label">
48 48 <span class="help-block">${_('Filesystem location where repositories should be stored. After changing this value a restart and rescan of the repository folder are required.')}</span>
49 49 </div>
50 50 </div>
51 51 </div>
52 52 % endif
53 53
54 54 % if display_globals or repo_type in ['git', 'hg']:
55 55 <div class="panel panel-default">
56 56 <div class="panel-heading" id="general">
57 57 <h3 class="panel-title">${_('Internal Hooks')}</h3>
58 58 </div>
59 59 <div class="panel-body">
60 60 <div class="field">
61 61 <div class="checkbox">
62 62 ${h.checkbox('hooks_changegroup_repo_size' + suffix, 'True', **kwargs)}
63 63 <label for="hooks_changegroup_repo_size${suffix}">${_('Show repository size after push')}</label>
64 64 </div>
65 65
66 66 <div class="label">
67 67 <span class="help-block">${_('Trigger a hook that calculates repository size after each push.')}</span>
68 68 </div>
69 69 <div class="checkbox">
70 70 ${h.checkbox('hooks_changegroup_push_logger' + suffix, 'True', **kwargs)}
71 71 <label for="hooks_changegroup_push_logger${suffix}">${_('Execute pre/post push hooks')}</label>
72 72 </div>
73 73 <div class="label">
74 74 <span class="help-block">${_('Execute Built in pre/post push hooks. This also executes rcextensions hooks.')}</span>
75 75 </div>
76 76 <div class="checkbox">
77 77 ${h.checkbox('hooks_outgoing_pull_logger' + suffix, 'True', **kwargs)}
78 78 <label for="hooks_outgoing_pull_logger${suffix}">${_('Execute pre/post pull hooks')}</label>
79 79 </div>
80 80 <div class="label">
81 81 <span class="help-block">${_('Execute Built in pre/post pull hooks. This also executes rcextensions hooks.')}</span>
82 82 </div>
83 83 </div>
84 84 </div>
85 85 </div>
86 86 % endif
87 87
88 88 % if display_globals or repo_type in ['hg']:
89 89 <div class="panel panel-default">
90 90 <div class="panel-heading">
91 91 <h3 class="panel-title">${_('Mercurial Settings')}</h3>
92 92 </div>
93 93 <div class="panel-body">
94 94 <div class="checkbox">
95 95 ${h.checkbox('extensions_largefiles' + suffix, 'True', **kwargs)}
96 96 <label for="extensions_largefiles${suffix}">${_('Enable largefiles extension')}</label>
97 97 </div>
98 98 <div class="label">
99 99 % if display_globals:
100 100 <span class="help-block">${_('Enable Largefiles extensions for all repositories.')}</span>
101 101 % else:
102 102 <span class="help-block">${_('Enable Largefiles extensions for this repository.')}</span>
103 103 % endif
104 104 </div>
105 105
106 106 % if display_globals:
107 107 <div class="field">
108 108 <div class="input">
109 109 ${h.text('largefiles_usercache' + suffix, size=59)}
110 110 </div>
111 111 </div>
112 112 <div class="label">
113 113 <span class="help-block">${_('Filesystem location where Mercurial largefile objects should be stored.')}</span>
114 114 </div>
115 115 % endif
116 116
117 117 <div class="checkbox">
118 118 ${h.checkbox('phases_publish' + suffix, 'True', **kwargs)}
119 119 <label for="phases_publish${suffix}">${_('Set repositories as publishing') if display_globals else _('Set repository as publishing')}</label>
120 120 </div>
121 121 <div class="label">
122 122 <span class="help-block">${_('When this is enabled all commits in the repository are seen as public commits by clients.')}</span>
123 123 </div>
124 124 % if display_globals:
125 125 <div class="checkbox">
126 126 ${h.checkbox('extensions_hgsubversion' + suffix,'True')}
127 127 <label for="extensions_hgsubversion${suffix}">${_('Enable hgsubversion extension')}</label>
128 128 </div>
129 129 <div class="label">
130 130 <span class="help-block">${_('Requires hgsubversion library to be installed. Allows cloning remote SVN repositories and migrates them to Mercurial type.')}</span>
131 131 </div>
132 132 % endif
133 133 </div>
134 134 </div>
135 135 ## LABS for HG
136 136 % if c.labs_active:
137 137 <div class="panel panel-danger">
138 138 <div class="panel-heading">
139 139 <h3 class="panel-title">${_('Mercurial Labs Settings')} (${_('These features are considered experimental and may not work as expected.')})</h3>
140 140 </div>
141 141 <div class="panel-body">
142 142
143 143 <div class="checkbox">
144 144 ${h.checkbox('rhodecode_hg_use_rebase_for_merging' + suffix, 'True', **kwargs)}
145 145 <label for="rhodecode_hg_use_rebase_for_merging${suffix}">${_('Use rebase as merge strategy')}</label>
146 146 </div>
147 147 <div class="label">
148 148 <span class="help-block">${_('Use rebase instead of creating a merge commit when merging via web interface.')}</span>
149 149 </div>
150 150
151 151 </div>
152 152 </div>
153 153 % endif
154 154
155 155 % endif
156 156
157 % if display_globals or repo_type in ['git']:
158 <div class="panel panel-default">
159 <div class="panel-heading">
160 <h3 class="panel-title">${_('Git Settings')}</h3>
161 </div>
162 <div class="panel-body">
163 <div class="checkbox">
164 ${h.checkbox('vcs_git_lfs_enabled' + suffix, 'True', **kwargs)}
165 <label for="vcs_git_lfs_enabled${suffix}">${_('Enable lfs extension')}</label>
166 </div>
167 <div class="label">
168 % if display_globals:
169 <span class="help-block">${_('Enable lfs extensions for all repositories.')}</span>
170 % else:
171 <span class="help-block">${_('Enable lfs extensions for this repository.')}</span>
172 % endif
173 </div>
174
175 % if display_globals:
176 <div class="field">
177 <div class="input">
178 ${h.text('vcs_git_lfs_store_location' + suffix, size=59)}
179 </div>
180 </div>
181 <div class="label">
182 <span class="help-block">${_('Filesystem location where Git lfs objects should be stored.')}</span>
183 </div>
184 % endif
185 </div>
186 </div>
187 % endif
188
189
157 190 % if display_globals:
158 191 <div class="panel panel-default">
159 192 <div class="panel-heading">
160 193 <h3 class="panel-title">${_('Global Subversion Settings')}</h3>
161 194 </div>
162 195 <div class="panel-body">
163 196 <div class="field">
164 197 <div class="checkbox">
165 198 ${h.checkbox('vcs_svn_proxy_http_requests_enabled' + suffix, 'True', **kwargs)}
166 199 <label for="vcs_svn_proxy_http_requests_enabled${suffix}">${_('Proxy subversion HTTP requests')}</label>
167 200 </div>
168 201 <div class="label">
169 202 <span class="help-block">
170 203 ${_('Subversion HTTP Support. Enables communication with SVN over HTTP protocol.')}
171 204 <a href="${h.url('enterprise_svn_setup')}" target="_blank">${_('SVN Protocol setup Documentation')}</a>.
172 205 </span>
173 206 </div>
174 207 </div>
175 208 <div class="field">
176 209 <div class="label">
177 210 <label for="vcs_svn_proxy_http_server_url">${_('Subversion HTTP Server URL')}</label><br/>
178 211 </div>
179 212 <div class="input">
180 213 ${h.text('vcs_svn_proxy_http_server_url',size=59)}
181 214 % if c.svn_proxy_generate_config:
182 215 <span class="buttons">
183 216 <button class="btn btn-primary" id="vcs_svn_generate_cfg">${_('Generate Apache Config')}</button>
184 217 </span>
185 218 % endif
186 219 </div>
187 220 </div>
188 221 </div>
189 222 </div>
190 223 % endif
191 224
192 225 % if display_globals or repo_type in ['svn']:
193 226 <div class="panel panel-default">
194 227 <div class="panel-heading">
195 228 <h3 class="panel-title">${_('Subversion Settings')}</h3>
196 229 </div>
197 230 <div class="panel-body">
198 231 <div class="field">
199 232 <div class="content" >
200 233 <label>${_('Repository patterns')}</label><br/>
201 234 </div>
202 235 </div>
203 236 <div class="label">
204 237 <span class="help-block">${_('Patterns for identifying SVN branches and tags. For recursive search, use "*". Eg.: "/branches/*"')}</span>
205 238 </div>
206 239
207 240 <div class="field branch_patterns">
208 241 <div class="input" >
209 242 <label>${_('Branches')}:</label><br/>
210 243 </div>
211 244 % if svn_branch_patterns:
212 245 % for branch in svn_branch_patterns:
213 246 <div class="input adjacent" id="${'id%s' % branch.ui_id}">
214 247 ${h.hidden('branch_ui_key' + suffix, branch.ui_key)}
215 248 ${h.text('branch_value_%d' % branch.ui_id + suffix, branch.ui_value, size=59, readonly="readonly", class_='disabled')}
216 249 % if kwargs.get('disabled') != 'disabled':
217 250 <span class="btn btn-x" onclick="ajaxDeletePattern(${branch.ui_id},'${'id%s' % branch.ui_id}')">
218 251 ${_('Delete')}
219 252 </span>
220 253 % endif
221 254 </div>
222 255 % endfor
223 256 %endif
224 257 </div>
225 258 % if kwargs.get('disabled') != 'disabled':
226 259 <div class="field branch_patterns">
227 260 <div class="input" >
228 261 ${h.text('new_svn_branch',size=59,placeholder='New branch pattern')}
229 262 </div>
230 263 </div>
231 264 % endif
232 265 <div class="field tag_patterns">
233 266 <div class="input" >
234 267 <label>${_('Tags')}:</label><br/>
235 268 </div>
236 269 % if svn_tag_patterns:
237 270 % for tag in svn_tag_patterns:
238 271 <div class="input" id="${'id%s' % tag.ui_id + suffix}">
239 272 ${h.hidden('tag_ui_key' + suffix, tag.ui_key)}
240 273 ${h.text('tag_ui_value_new_%d' % tag.ui_id + suffix, tag.ui_value, size=59, readonly="readonly", class_='disabled tag_input')}
241 274 % if kwargs.get('disabled') != 'disabled':
242 275 <span class="btn btn-x" onclick="ajaxDeletePattern(${tag.ui_id},'${'id%s' % tag.ui_id}')">
243 276 ${_('Delete')}
244 277 </span>
245 278 %endif
246 279 </div>
247 280 % endfor
248 281 % endif
249 282 </div>
250 283 % if kwargs.get('disabled') != 'disabled':
251 284 <div class="field tag_patterns">
252 285 <div class="input" >
253 286 ${h.text('new_svn_tag' + suffix, size=59, placeholder='New tag pattern')}
254 287 </div>
255 288 </div>
256 289 %endif
257 290 </div>
258 291 </div>
259 292 % else:
260 293 ${h.hidden('new_svn_branch' + suffix, '')}
261 294 ${h.hidden('new_svn_tag' + suffix, '')}
262 295 % endif
263 296
264 297
265 298
266 299
267 300 % if display_globals or repo_type in ['hg', 'git']:
268 301 <div class="panel panel-default">
269 302 <div class="panel-heading">
270 303 <h3 class="panel-title">${_('Pull Request Settings')}</h3>
271 304 </div>
272 305 <div class="panel-body">
273 306 <div class="checkbox">
274 307 ${h.checkbox('rhodecode_pr_merge_enabled' + suffix, 'True', **kwargs)}
275 308 <label for="rhodecode_pr_merge_enabled${suffix}">${_('Enable server-side merge for pull requests')}</label>
276 309 </div>
277 310 <div class="label">
278 311 <span class="help-block">${_('Note: when this feature is enabled, it only runs hooks defined in the rcextension package. Custom hooks added on the Admin -> Settings -> Hooks page will not be run when pull requests are automatically merged from the web interface.')}</span>
279 312 </div>
280 313 <div class="checkbox">
281 314 ${h.checkbox('rhodecode_use_outdated_comments' + suffix, 'True', **kwargs)}
282 315 <label for="rhodecode_use_outdated_comments${suffix}">${_('Invalidate and relocate inline comments during update')}</label>
283 316 </div>
284 317 <div class="label">
285 318 <span class="help-block">${_('During the update of a pull request, the position of inline comments will be updated and outdated inline comments will be hidden.')}</span>
286 319 </div>
287 320 </div>
288 321 </div>
289 322 % endif
290 323
291 324 </%def>
@@ -1,137 +1,142 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 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 pytest
22 22 import urlparse
23 import mock
24 import simplejson as json
23 25
26 from rhodecode.lib.vcs.backends.base import Config
24 27 from rhodecode.tests.lib.middleware import mock_scm_app
25 28 import rhodecode.lib.middleware.simplegit as simplegit
26 29
27 30
28 31 def get_environ(url, request_method):
29 32 """Construct a minimum WSGI environ based on the URL."""
30 33 parsed_url = urlparse.urlparse(url)
31 34 environ = {
32 35 'PATH_INFO': parsed_url.path,
33 36 'QUERY_STRING': parsed_url.query,
34 37 'REQUEST_METHOD': request_method,
35 38 }
36 39
37 40 return environ
38 41
39 42
40 43 @pytest.mark.parametrize(
41 44 'url, expected_action, request_method',
42 45 [
43 46 ('/foo/bar/info/refs?service=git-upload-pack', 'pull', 'GET'),
44 47 ('/foo/bar/info/refs?service=git-receive-pack', 'push', 'GET'),
45 48 ('/foo/bar/git-upload-pack', 'pull', 'GET'),
46 49 ('/foo/bar/git-receive-pack', 'push', 'GET'),
47 50 # Edge case: missing data for info/refs
48 51 ('/foo/info/refs?service=', 'pull', 'GET'),
49 52 ('/foo/info/refs', 'pull', 'GET'),
50 53 # Edge case: git command comes with service argument
51 54 ('/foo/git-upload-pack?service=git-receive-pack', 'pull', 'GET'),
52 55 ('/foo/git-receive-pack?service=git-upload-pack', 'push', 'GET'),
53 56 # Edge case: repo name conflicts with git commands
54 57 ('/git-receive-pack/git-upload-pack', 'pull', 'GET'),
55 58 ('/git-receive-pack/git-receive-pack', 'push', 'GET'),
56 59 ('/git-upload-pack/git-upload-pack', 'pull', 'GET'),
57 60 ('/git-upload-pack/git-receive-pack', 'push', 'GET'),
58 61 ('/foo/git-receive-pack', 'push', 'GET'),
59 62 # Edge case: not a smart protocol url
60 63 ('/foo/bar', 'pull', 'GET'),
61 64 # GIT LFS cases, batch
62 65 ('/foo/bar/info/lfs/objects/batch', 'push', 'GET'),
63 66 ('/foo/bar/info/lfs/objects/batch', 'pull', 'POST'),
64 67 # GIT LFS oid, dl/upl
65 68 ('/foo/bar/info/lfs/abcdeabcde', 'pull', 'GET'),
66 69 ('/foo/bar/info/lfs/abcdeabcde', 'push', 'PUT'),
67 70 ('/foo/bar/info/lfs/abcdeabcde', 'push', 'POST'),
68 71 # Edge case: repo name conflicts with git commands
69 72 ('/info/lfs/info/lfs/objects/batch', 'push', 'GET'),
70 73 ('/info/lfs/info/lfs/objects/batch', 'pull', 'POST'),
71 74
72 75 ])
73 76 def test_get_action(url, expected_action, request_method, pylonsapp):
74 77 app = simplegit.SimpleGit(application=None,
75 78 config={'auth_ret_code': '', 'base_path': ''},
76 79 registry=None)
77 80 assert expected_action == app._get_action(get_environ(url, request_method))
78 81
79 82
80 83 @pytest.mark.parametrize(
81 84 'url, expected_repo_name, request_method',
82 85 [
83 86 ('/foo/info/refs?service=git-upload-pack', 'foo', 'GET'),
84 87 ('/foo/bar/info/refs?service=git-receive-pack', 'foo/bar', 'GET'),
85 88 ('/foo/git-upload-pack', 'foo', 'GET'),
86 89 ('/foo/git-receive-pack', 'foo', 'GET'),
87 90 ('/foo/bar/git-upload-pack', 'foo/bar', 'GET'),
88 91 ('/foo/bar/git-receive-pack', 'foo/bar', 'GET'),
89 92
90 93 # GIT LFS cases, batch
91 94 ('/foo/bar/info/lfs/objects/batch', 'foo/bar', 'GET'),
92 95 ('/example-git/info/lfs/objects/batch', 'example-git', 'POST'),
93 96 # GIT LFS oid, dl/upl
94 97 ('/foo/info/lfs/abcdeabcde', 'foo', 'GET'),
95 98 ('/foo/bar/info/lfs/abcdeabcde', 'foo/bar', 'PUT'),
96 99 ('/my-git-repo/info/lfs/abcdeabcde', 'my-git-repo', 'POST'),
97 100 # Edge case: repo name conflicts with git commands
98 101 ('/info/lfs/info/lfs/objects/batch', 'info/lfs', 'GET'),
99 102 ('/info/lfs/info/lfs/objects/batch', 'info/lfs', 'POST'),
100 103
101 104 ])
102 105 def test_get_repository_name(url, expected_repo_name, request_method, pylonsapp):
103 106 app = simplegit.SimpleGit(application=None,
104 107 config={'auth_ret_code': '', 'base_path': ''},
105 108 registry=None)
106 109 assert expected_repo_name == app._get_repository_name(
107 110 get_environ(url, request_method))
108 111
109 112
110 def test_get_config(pylonsapp):
113 def test_get_config(pylonsapp, user_util):
114 repo = user_util.create_repo(repo_type='git')
111 115 app = simplegit.SimpleGit(application=None,
112 116 config={'auth_ret_code': '', 'base_path': ''},
113 117 registry=None)
114 118 extras = {'foo': 'FOO', 'bar': 'BAR'}
115 119
116 120 # We copy the extras as the method below will change the contents.
117 config = app._create_config(dict(extras), repo_name='test-repo')
121 git_config = app._create_config(dict(extras), repo_name=repo.repo_name)
122
118 123 expected_config = dict(extras)
119 124 expected_config.update({
120 125 'git_update_server_info': False,
121 'git_lfs_enabled': True,
122 'git_lfs_store_path': simplegit.default_lfs_store()
126 'git_lfs_enabled': False,
127 'git_lfs_store_path': git_config['git_lfs_store_path']
123 128 })
124 129
125 assert config == expected_config
130 assert git_config == expected_config
126 131
127 132
128 133 def test_create_wsgi_app_uses_scm_app_from_simplevcs(pylonsapp):
129 134 config = {
130 135 'auth_ret_code': '',
131 136 'base_path': '',
132 137 'vcs.scm_app_implementation':
133 138 'rhodecode.tests.lib.middleware.mock_scm_app',
134 139 }
135 140 app = simplegit.SimpleGit(application=None, config=config, registry=None)
136 141 wsgi_app = app._create_wsgi_app('/tmp/test', 'test_repo', {})
137 142 assert wsgi_app is mock_scm_app.mock_git_wsgi
@@ -1,116 +1,128 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 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 urlparse
22 22
23 23 import mock
24 24 import pytest
25 25 import simplejson as json
26 26
27 27 from rhodecode.lib.vcs.backends.base import Config
28 28 from rhodecode.tests.lib.middleware import mock_scm_app
29 29 import rhodecode.lib.middleware.simplehg as simplehg
30 30
31 31
32 32 def get_environ(url):
33 33 """Construct a minimum WSGI environ based on the URL."""
34 34 parsed_url = urlparse.urlparse(url)
35 35 environ = {
36 36 'PATH_INFO': parsed_url.path,
37 37 'QUERY_STRING': parsed_url.query,
38 38 }
39 39
40 40 return environ
41 41
42 42
43 43 @pytest.mark.parametrize(
44 44 'url, expected_action',
45 45 [
46 46 ('/foo/bar?cmd=unbundle&key=tip', 'push'),
47 47 ('/foo/bar?cmd=pushkey&key=tip', 'push'),
48 48 ('/foo/bar?cmd=listkeys&key=tip', 'pull'),
49 49 ('/foo/bar?cmd=changegroup&key=tip', 'pull'),
50 50 # Edge case: unknown argument: assume pull
51 51 ('/foo/bar?cmd=unknown&key=tip', 'pull'),
52 52 ('/foo/bar?cmd=&key=tip', 'pull'),
53 53 # Edge case: not cmd argument
54 54 ('/foo/bar?key=tip', 'pull'),
55 55 ])
56 56 def test_get_action(url, expected_action):
57 57 app = simplehg.SimpleHg(application=None,
58 58 config={'auth_ret_code': '', 'base_path': ''},
59 59 registry=None)
60 60 assert expected_action == app._get_action(get_environ(url))
61 61
62 62
63 63 @pytest.mark.parametrize(
64 64 'url, expected_repo_name',
65 65 [
66 66 ('/foo?cmd=unbundle&key=tip', 'foo'),
67 67 ('/foo/bar?cmd=pushkey&key=tip', 'foo/bar'),
68 68 ('/foo/bar/baz?cmd=listkeys&key=tip', 'foo/bar/baz'),
69 69 # Repos with trailing slashes.
70 70 ('/foo/?cmd=unbundle&key=tip', 'foo'),
71 71 ('/foo/bar/?cmd=pushkey&key=tip', 'foo/bar'),
72 72 ('/foo/bar/baz/?cmd=listkeys&key=tip', 'foo/bar/baz'),
73 73 ])
74 74 def test_get_repository_name(url, expected_repo_name):
75 75 app = simplehg.SimpleHg(application=None,
76 76 config={'auth_ret_code': '', 'base_path': ''},
77 77 registry=None)
78 78 assert expected_repo_name == app._get_repository_name(get_environ(url))
79 79
80 80
81 def test_get_config():
81 def test_get_config(pylonsapp, user_util):
82 repo = user_util.create_repo(repo_type='git')
82 83 app = simplehg.SimpleHg(application=None,
83 84 config={'auth_ret_code': '', 'base_path': ''},
84 85 registry=None)
85 86 extras = {'foo': 'FOO', 'bar': 'BAR'}
86 87
87 mock_config = Config()
88 mock_config.set('a1', 'b1', 'c1')
89 mock_config.set('a2', 'b2', 'c2')
90 # We mock the call to make_db_config, otherwise we need to wait for the
91 # pylonsaspp
92 with mock.patch('rhodecode.lib.utils.make_db_config',
93 return_value=mock_config) as make_db_config_mock:
94 hg_config = app._create_config(extras, repo_name='test-repo')
88 hg_config = app._create_config(extras, repo_name=repo.repo_name)
89
90 config = simplehg.utils.make_db_config(repo=repo.repo_name)
91 config.set('rhodecode', 'RC_SCM_DATA', json.dumps(extras))
92 hg_config_org = config
95 93
96 make_db_config_mock.assert_called_once_with(repo='test-repo')
97 assert isinstance(hg_config, list)
98
99 # Remove the entries from the mock_config so to get only the extras
100 hg_config.remove(('a1', 'b1', 'c1'))
101 hg_config.remove(('a2', 'b2', 'c2'))
102
103 assert hg_config[0][:2] == ('rhodecode', 'RC_SCM_DATA')
104 assert json.loads(hg_config[0][-1]) == extras
94 expected_config = [
95 ('vcs_svn_tag', 'ff89f8c714d135d865f44b90e5413b88de19a55f', '/tags/*'),
96 ('web', 'push_ssl', 'False'),
97 ('web', 'allow_push', '*'),
98 ('web', 'allow_archive', 'gz zip bz2'),
99 ('web', 'baseurl', '/'),
100 ('vcs_git_lfs', 'store_location', hg_config_org.get('vcs_git_lfs', 'store_location')),
101 ('vcs_svn_branch', '9aac1a38c3b8a0cdc4ae0f960a5f83332bc4fa5e', '/branches/*'),
102 ('vcs_svn_branch', 'c7e6a611c87da06529fd0dd733308481d67c71a8', '/trunk'),
103 ('largefiles', 'usercache', hg_config_org.get('largefiles', 'usercache')),
104 ('hooks', 'preoutgoing.pre_pull', 'python:vcsserver.hooks.pre_pull'),
105 ('hooks', 'prechangegroup.pre_push', 'python:vcsserver.hooks.pre_push'),
106 ('hooks', 'outgoing.pull_logger', 'python:vcsserver.hooks.log_pull_action'),
107 ('hooks', 'pretxnchangegroup.pre_push', 'python:vcsserver.hooks.pre_push'),
108 ('hooks', 'changegroup.push_logger', 'python:vcsserver.hooks.log_push_action'),
109 ('hooks', 'changegroup.repo_size', 'python:vcsserver.hooks.repo_size'),
110 ('phases', 'publish', 'True'),
111 ('extensions', 'largefiles', ''),
112 ('paths', '/', hg_config_org.get('paths', '/')),
113 ('rhodecode', 'RC_SCM_DATA', '{"foo": "FOO", "bar": "BAR"}')
114 ]
115 for entry in expected_config:
116 assert entry in hg_config
105 117
106 118
107 119 def test_create_wsgi_app_uses_scm_app_from_simplevcs():
108 120 config = {
109 121 'auth_ret_code': '',
110 122 'base_path': '',
111 123 'vcs.scm_app_implementation':
112 124 'rhodecode.tests.lib.middleware.mock_scm_app',
113 125 }
114 126 app = simplehg.SimpleHg(application=None, config=config, registry=None)
115 127 wsgi_app = app._create_wsgi_app('/tmp/test', 'test_repo', {})
116 128 assert wsgi_app is mock_scm_app.mock_hg_wsgi
@@ -1,1034 +1,1061 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 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 from rhodecode.lib.utils2 import str2bool
25 25 from rhodecode.model.meta import Session
26 26 from rhodecode.model.settings import VcsSettingsModel, UiSetting
27 27
28 28
29 29 HOOKS_FORM_DATA = {
30 30 'hooks_changegroup_repo_size': True,
31 31 'hooks_changegroup_push_logger': True,
32 32 'hooks_outgoing_pull_logger': True
33 33 }
34 34
35 35 SVN_FORM_DATA = {
36 36 'new_svn_branch': 'test-branch',
37 37 'new_svn_tag': 'test-tag'
38 38 }
39 39
40 40 GENERAL_FORM_DATA = {
41 41 'rhodecode_pr_merge_enabled': True,
42 42 'rhodecode_use_outdated_comments': True,
43 43 'rhodecode_hg_use_rebase_for_merging': True,
44 44 }
45 45
46 46
47 47 class TestInheritGlobalSettingsProperty(object):
48 48 def test_get_raises_exception_when_repository_not_specified(self):
49 49 model = VcsSettingsModel()
50 50 with pytest.raises(Exception) as exc_info:
51 51 model.inherit_global_settings
52 52 assert exc_info.value.message == 'Repository is not specified'
53 53
54 54 def test_true_is_returned_when_value_is_not_found(self, repo_stub):
55 55 model = VcsSettingsModel(repo=repo_stub.repo_name)
56 56 assert model.inherit_global_settings is True
57 57
58 58 def test_value_is_returned(self, repo_stub, settings_util):
59 59 model = VcsSettingsModel(repo=repo_stub.repo_name)
60 60 settings_util.create_repo_rhodecode_setting(
61 61 repo_stub, VcsSettingsModel.INHERIT_SETTINGS, False, 'bool')
62 62 assert model.inherit_global_settings is False
63 63
64 64 def test_value_is_set(self, repo_stub):
65 65 model = VcsSettingsModel(repo=repo_stub.repo_name)
66 66 model.inherit_global_settings = False
67 67 setting = model.repo_settings.get_setting_by_name(
68 68 VcsSettingsModel.INHERIT_SETTINGS)
69 69 try:
70 70 assert setting.app_settings_type == 'bool'
71 71 assert setting.app_settings_value is False
72 72 finally:
73 73 Session().delete(setting)
74 74 Session().commit()
75 75
76 76 def test_set_raises_exception_when_repository_not_specified(self):
77 77 model = VcsSettingsModel()
78 78 with pytest.raises(Exception) as exc_info:
79 79 model.inherit_global_settings = False
80 80 assert exc_info.value.message == 'Repository is not specified'
81 81
82 82
83 83 class TestVcsSettingsModel(object):
84 84 def test_global_svn_branch_patterns(self):
85 85 model = VcsSettingsModel()
86 86 expected_result = {'test': 'test'}
87 87 with mock.patch.object(model, 'global_settings') as settings_mock:
88 88 get_settings = settings_mock.get_ui_by_section
89 89 get_settings.return_value = expected_result
90 90 settings_mock.return_value = expected_result
91 91 result = model.get_global_svn_branch_patterns()
92 92
93 93 get_settings.assert_called_once_with(model.SVN_BRANCH_SECTION)
94 94 assert expected_result == result
95 95
96 96 def test_repo_svn_branch_patterns(self):
97 97 model = VcsSettingsModel()
98 98 expected_result = {'test': 'test'}
99 99 with mock.patch.object(model, 'repo_settings') as settings_mock:
100 100 get_settings = settings_mock.get_ui_by_section
101 101 get_settings.return_value = expected_result
102 102 settings_mock.return_value = expected_result
103 103 result = model.get_repo_svn_branch_patterns()
104 104
105 105 get_settings.assert_called_once_with(model.SVN_BRANCH_SECTION)
106 106 assert expected_result == result
107 107
108 108 def test_repo_svn_branch_patterns_raises_exception_when_repo_is_not_set(
109 109 self):
110 110 model = VcsSettingsModel()
111 111 with pytest.raises(Exception) as exc_info:
112 112 model.get_repo_svn_branch_patterns()
113 113 assert exc_info.value.message == 'Repository is not specified'
114 114
115 115 def test_global_svn_tag_patterns(self):
116 116 model = VcsSettingsModel()
117 117 expected_result = {'test': 'test'}
118 118 with mock.patch.object(model, 'global_settings') as settings_mock:
119 119 get_settings = settings_mock.get_ui_by_section
120 120 get_settings.return_value = expected_result
121 121 settings_mock.return_value = expected_result
122 122 result = model.get_global_svn_tag_patterns()
123 123
124 124 get_settings.assert_called_once_with(model.SVN_TAG_SECTION)
125 125 assert expected_result == result
126 126
127 127 def test_repo_svn_tag_patterns(self):
128 128 model = VcsSettingsModel()
129 129 expected_result = {'test': 'test'}
130 130 with mock.patch.object(model, 'repo_settings') as settings_mock:
131 131 get_settings = settings_mock.get_ui_by_section
132 132 get_settings.return_value = expected_result
133 133 settings_mock.return_value = expected_result
134 134 result = model.get_repo_svn_tag_patterns()
135 135
136 136 get_settings.assert_called_once_with(model.SVN_TAG_SECTION)
137 137 assert expected_result == result
138 138
139 139 def test_repo_svn_tag_patterns_raises_exception_when_repo_is_not_set(self):
140 140 model = VcsSettingsModel()
141 141 with pytest.raises(Exception) as exc_info:
142 142 model.get_repo_svn_tag_patterns()
143 143 assert exc_info.value.message == 'Repository is not specified'
144 144
145 145 def test_get_global_settings(self):
146 146 expected_result = {'test': 'test'}
147 147 model = VcsSettingsModel()
148 148 with mock.patch.object(model, '_collect_all_settings') as collect_mock:
149 149 collect_mock.return_value = expected_result
150 150 result = model.get_global_settings()
151 151
152 152 collect_mock.assert_called_once_with(global_=True)
153 153 assert result == expected_result
154 154
155 155 def test_get_repo_settings(self, repo_stub):
156 156 model = VcsSettingsModel(repo=repo_stub.repo_name)
157 157 expected_result = {'test': 'test'}
158 158 with mock.patch.object(model, '_collect_all_settings') as collect_mock:
159 159 collect_mock.return_value = expected_result
160 160 result = model.get_repo_settings()
161 161
162 162 collect_mock.assert_called_once_with(global_=False)
163 163 assert result == expected_result
164 164
165 165 @pytest.mark.parametrize('settings, global_', [
166 166 ('global_settings', True),
167 167 ('repo_settings', False)
168 168 ])
169 169 def test_collect_all_settings(self, settings, global_):
170 170 model = VcsSettingsModel()
171 171 result_mock = self._mock_result()
172 172
173 173 settings_patch = mock.patch.object(model, settings)
174 174 with settings_patch as settings_mock:
175 175 settings_mock.get_ui_by_section_and_key.return_value = result_mock
176 176 settings_mock.get_setting_by_name.return_value = result_mock
177 177 result = model._collect_all_settings(global_=global_)
178 178
179 ui_settings = model.HG_SETTINGS + model.HOOKS_SETTINGS
179 ui_settings = model.HG_SETTINGS + model.GIT_SETTINGS + model.HOOKS_SETTINGS
180 180 self._assert_get_settings_calls(
181 181 settings_mock, ui_settings, model.GENERAL_SETTINGS)
182 182 self._assert_collect_all_settings_result(
183 183 ui_settings, model.GENERAL_SETTINGS, result)
184 184
185 185 @pytest.mark.parametrize('settings, global_', [
186 186 ('global_settings', True),
187 187 ('repo_settings', False)
188 188 ])
189 189 def test_collect_all_settings_without_empty_value(self, settings, global_):
190 190 model = VcsSettingsModel()
191 191
192 192 settings_patch = mock.patch.object(model, settings)
193 193 with settings_patch as settings_mock:
194 194 settings_mock.get_ui_by_section_and_key.return_value = None
195 195 settings_mock.get_setting_by_name.return_value = None
196 196 result = model._collect_all_settings(global_=global_)
197 197
198 198 assert result == {}
199 199
200 200 def _mock_result(self):
201 201 result_mock = mock.Mock()
202 202 result_mock.ui_value = 'ui_value'
203 203 result_mock.ui_active = True
204 204 result_mock.app_settings_value = 'setting_value'
205 205 return result_mock
206 206
207 207 def _assert_get_settings_calls(
208 208 self, settings_mock, ui_settings, general_settings):
209 209 assert (
210 210 settings_mock.get_ui_by_section_and_key.call_count ==
211 211 len(ui_settings))
212 212 assert (
213 213 settings_mock.get_setting_by_name.call_count ==
214 214 len(general_settings))
215 215
216 216 for section, key in ui_settings:
217 217 expected_call = mock.call(section, key)
218 218 assert (
219 219 expected_call in
220 220 settings_mock.get_ui_by_section_and_key.call_args_list)
221 221
222 222 for name in general_settings:
223 223 expected_call = mock.call(name)
224 224 assert (
225 225 expected_call in
226 226 settings_mock.get_setting_by_name.call_args_list)
227 227
228 228 def _assert_collect_all_settings_result(
229 229 self, ui_settings, general_settings, result):
230 230 expected_result = {}
231 231 for section, key in ui_settings:
232 232 key = '{}_{}'.format(section, key.replace('.', '_'))
233 value = True if section in ('extensions', 'hooks') else 'ui_value'
233
234 if section in ('extensions', 'hooks'):
235 value = True
236 elif key in ['vcs_git_lfs_enabled']:
237 value = True
238 else:
239 value = 'ui_value'
234 240 expected_result[key] = value
235 241
236 242 for name in general_settings:
237 243 key = 'rhodecode_' + name
238 244 expected_result[key] = 'setting_value'
239 245
240 246 assert expected_result == result
241 247
242 248
243 249 class TestCreateOrUpdateRepoHookSettings(object):
244 250 def test_create_when_no_repo_object_found(self, repo_stub):
245 251 model = VcsSettingsModel(repo=repo_stub.repo_name)
246 252
247 253 self._create_settings(model, HOOKS_FORM_DATA)
248 254
249 255 cleanup = []
250 256 try:
251 257 for section, key in model.HOOKS_SETTINGS:
252 258 ui = model.repo_settings.get_ui_by_section_and_key(
253 259 section, key)
254 260 assert ui.ui_active is True
255 261 cleanup.append(ui)
256 262 finally:
257 263 for ui in cleanup:
258 264 Session().delete(ui)
259 265 Session().commit()
260 266
261 267 def test_create_raises_exception_when_data_incomplete(self, repo_stub):
262 268 model = VcsSettingsModel(repo=repo_stub.repo_name)
263 269
264 270 deleted_key = 'hooks_changegroup_repo_size'
265 271 data = HOOKS_FORM_DATA.copy()
266 272 data.pop(deleted_key)
267 273
268 274 with pytest.raises(ValueError) as exc_info:
269 275 model.create_or_update_repo_hook_settings(data)
270 276 assert (
271 277 exc_info.value.message ==
272 278 'The given data does not contain {} key'.format(deleted_key))
273 279
274 280 def test_update_when_repo_object_found(self, repo_stub, settings_util):
275 281 model = VcsSettingsModel(repo=repo_stub.repo_name)
276 282 for section, key in model.HOOKS_SETTINGS:
277 283 settings_util.create_repo_rhodecode_ui(
278 284 repo_stub, section, None, key=key, active=False)
279 285 model.create_or_update_repo_hook_settings(HOOKS_FORM_DATA)
280 286 for section, key in model.HOOKS_SETTINGS:
281 287 ui = model.repo_settings.get_ui_by_section_and_key(section, key)
282 288 assert ui.ui_active is True
283 289
284 290 def _create_settings(self, model, data):
285 291 global_patch = mock.patch.object(model, 'global_settings')
286 292 global_setting = mock.Mock()
287 293 global_setting.ui_value = 'Test value'
288 294 with global_patch as global_mock:
289 295 global_mock.get_ui_by_section_and_key.return_value = global_setting
290 296 model.create_or_update_repo_hook_settings(HOOKS_FORM_DATA)
291 297
292 298
293 299 class TestUpdateGlobalHookSettings(object):
294 300 def test_update_raises_exception_when_data_incomplete(self):
295 301 model = VcsSettingsModel()
296 302
297 303 deleted_key = 'hooks_changegroup_repo_size'
298 304 data = HOOKS_FORM_DATA.copy()
299 305 data.pop(deleted_key)
300 306
301 307 with pytest.raises(ValueError) as exc_info:
302 308 model.update_global_hook_settings(data)
303 309 assert (
304 310 exc_info.value.message ==
305 311 'The given data does not contain {} key'.format(deleted_key))
306 312
307 313 def test_update_global_hook_settings(self, settings_util):
308 314 model = VcsSettingsModel()
309 315 setting_mock = mock.MagicMock()
310 316 setting_mock.ui_active = False
311 317 get_settings_patcher = mock.patch.object(
312 318 model.global_settings, 'get_ui_by_section_and_key',
313 319 return_value=setting_mock)
314 320 session_patcher = mock.patch('rhodecode.model.settings.Session')
315 321 with get_settings_patcher as get_settings_mock, session_patcher:
316 322 model.update_global_hook_settings(HOOKS_FORM_DATA)
317 323 assert setting_mock.ui_active is True
318 324 assert get_settings_mock.call_count == 3
319 325
320 326
321 327 class TestCreateOrUpdateRepoGeneralSettings(object):
322 328 def test_calls_create_or_update_general_settings(self, repo_stub):
323 329 model = VcsSettingsModel(repo=repo_stub.repo_name)
324 330 create_patch = mock.patch.object(
325 331 model, '_create_or_update_general_settings')
326 332 with create_patch as create_mock:
327 333 model.create_or_update_repo_pr_settings(GENERAL_FORM_DATA)
328 334 create_mock.assert_called_once_with(
329 335 model.repo_settings, GENERAL_FORM_DATA)
330 336
331 337 def test_raises_exception_when_repository_is_not_specified(self):
332 338 model = VcsSettingsModel()
333 339 with pytest.raises(Exception) as exc_info:
334 340 model.create_or_update_repo_pr_settings(GENERAL_FORM_DATA)
335 341 assert exc_info.value.message == 'Repository is not specified'
336 342
337 343
338 344 class TestCreateOrUpdatGlobalGeneralSettings(object):
339 345 def test_calls_create_or_update_general_settings(self):
340 346 model = VcsSettingsModel()
341 347 create_patch = mock.patch.object(
342 348 model, '_create_or_update_general_settings')
343 349 with create_patch as create_mock:
344 350 model.create_or_update_global_pr_settings(GENERAL_FORM_DATA)
345 351 create_mock.assert_called_once_with(
346 352 model.global_settings, GENERAL_FORM_DATA)
347 353
348 354
349 355 class TestCreateOrUpdateGeneralSettings(object):
350 356 def test_create_when_no_repo_settings_found(self, repo_stub):
351 357 model = VcsSettingsModel(repo=repo_stub.repo_name)
352 358 model._create_or_update_general_settings(
353 359 model.repo_settings, GENERAL_FORM_DATA)
354 360
355 361 cleanup = []
356 362 try:
357 363 for name in model.GENERAL_SETTINGS:
358 364 setting = model.repo_settings.get_setting_by_name(name)
359 365 assert setting.app_settings_value is True
360 366 cleanup.append(setting)
361 367 finally:
362 368 for setting in cleanup:
363 369 Session().delete(setting)
364 370 Session().commit()
365 371
366 372 def test_create_raises_exception_when_data_incomplete(self, repo_stub):
367 373 model = VcsSettingsModel(repo=repo_stub.repo_name)
368 374
369 375 deleted_key = 'rhodecode_pr_merge_enabled'
370 376 data = GENERAL_FORM_DATA.copy()
371 377 data.pop(deleted_key)
372 378
373 379 with pytest.raises(ValueError) as exc_info:
374 380 model._create_or_update_general_settings(model.repo_settings, data)
375 381 assert (
376 382 exc_info.value.message ==
377 383 'The given data does not contain {} key'.format(deleted_key))
378 384
379 385 def test_update_when_repo_setting_found(self, repo_stub, settings_util):
380 386 model = VcsSettingsModel(repo=repo_stub.repo_name)
381 387 for name in model.GENERAL_SETTINGS:
382 388 settings_util.create_repo_rhodecode_setting(
383 389 repo_stub, name, False, 'bool')
384 390
385 391 model._create_or_update_general_settings(
386 392 model.repo_settings, GENERAL_FORM_DATA)
387 393
388 394 for name in model.GENERAL_SETTINGS:
389 395 setting = model.repo_settings.get_setting_by_name(name)
390 396 assert setting.app_settings_value is True
391 397
392 398
393 399 class TestCreateRepoSvnSettings(object):
394 400 def test_calls_create_svn_settings(self, repo_stub):
395 401 model = VcsSettingsModel(repo=repo_stub.repo_name)
396 402 with mock.patch.object(model, '_create_svn_settings') as create_mock:
397 403 model.create_repo_svn_settings(SVN_FORM_DATA)
398 404 create_mock.assert_called_once_with(model.repo_settings, SVN_FORM_DATA)
399 405
400 406 def test_raises_exception_when_repository_is_not_specified(self):
401 407 model = VcsSettingsModel()
402 408 with pytest.raises(Exception) as exc_info:
403 409 model.create_repo_svn_settings(SVN_FORM_DATA)
404 410 assert exc_info.value.message == 'Repository is not specified'
405 411
406 412
407 413 class TestCreateSvnSettings(object):
408 414 def test_create(self, repo_stub):
409 415 model = VcsSettingsModel(repo=repo_stub.repo_name)
410 416 model._create_svn_settings(model.repo_settings, SVN_FORM_DATA)
411 417 Session().commit()
412 418
413 419 branch_ui = model.repo_settings.get_ui_by_section(
414 420 model.SVN_BRANCH_SECTION)
415 421 tag_ui = model.repo_settings.get_ui_by_section(
416 422 model.SVN_TAG_SECTION)
417 423
418 424 try:
419 425 assert len(branch_ui) == 1
420 426 assert len(tag_ui) == 1
421 427 finally:
422 428 Session().delete(branch_ui[0])
423 429 Session().delete(tag_ui[0])
424 430 Session().commit()
425 431
426 432 def test_create_tag(self, repo_stub):
427 433 model = VcsSettingsModel(repo=repo_stub.repo_name)
428 434 data = SVN_FORM_DATA.copy()
429 435 data.pop('new_svn_branch')
430 436 model._create_svn_settings(model.repo_settings, data)
431 437 Session().commit()
432 438
433 439 branch_ui = model.repo_settings.get_ui_by_section(
434 440 model.SVN_BRANCH_SECTION)
435 441 tag_ui = model.repo_settings.get_ui_by_section(
436 442 model.SVN_TAG_SECTION)
437 443
438 444 try:
439 445 assert len(branch_ui) == 0
440 446 assert len(tag_ui) == 1
441 447 finally:
442 448 Session().delete(tag_ui[0])
443 449 Session().commit()
444 450
445 451 def test_create_nothing_when_no_svn_settings_specified(self, repo_stub):
446 452 model = VcsSettingsModel(repo=repo_stub.repo_name)
447 453 model._create_svn_settings(model.repo_settings, {})
448 454 Session().commit()
449 455
450 456 branch_ui = model.repo_settings.get_ui_by_section(
451 457 model.SVN_BRANCH_SECTION)
452 458 tag_ui = model.repo_settings.get_ui_by_section(
453 459 model.SVN_TAG_SECTION)
454 460
455 461 assert len(branch_ui) == 0
456 462 assert len(tag_ui) == 0
457 463
458 464 def test_create_nothing_when_empty_settings_specified(self, repo_stub):
459 465 model = VcsSettingsModel(repo=repo_stub.repo_name)
460 466 data = {
461 467 'new_svn_branch': '',
462 468 'new_svn_tag': ''
463 469 }
464 470 model._create_svn_settings(model.repo_settings, data)
465 471 Session().commit()
466 472
467 473 branch_ui = model.repo_settings.get_ui_by_section(
468 474 model.SVN_BRANCH_SECTION)
469 475 tag_ui = model.repo_settings.get_ui_by_section(
470 476 model.SVN_TAG_SECTION)
471 477
472 478 assert len(branch_ui) == 0
473 479 assert len(tag_ui) == 0
474 480
475 481
476 482 class TestCreateOrUpdateUi(object):
477 483 def test_create(self, repo_stub):
478 484 model = VcsSettingsModel(repo=repo_stub.repo_name)
479 485 model._create_or_update_ui(
480 486 model.repo_settings, 'test-section', 'test-key', active=False,
481 487 value='False')
482 488 Session().commit()
483 489
484 490 created_ui = model.repo_settings.get_ui_by_section_and_key(
485 491 'test-section', 'test-key')
486 492
487 493 try:
488 494 assert created_ui.ui_active is False
489 495 assert str2bool(created_ui.ui_value) is False
490 496 finally:
491 497 Session().delete(created_ui)
492 498 Session().commit()
493 499
494 500 def test_update(self, repo_stub, settings_util):
495 501 model = VcsSettingsModel(repo=repo_stub.repo_name)
496 502
497 503 largefiles, phases = model.HG_SETTINGS
498 504 section = 'test-section'
499 505 key = 'test-key'
500 506 settings_util.create_repo_rhodecode_ui(
501 507 repo_stub, section, 'True', key=key, active=True)
502 508
503 509 model._create_or_update_ui(
504 510 model.repo_settings, section, key, active=False, value='False')
505 511 Session().commit()
506 512
507 513 created_ui = model.repo_settings.get_ui_by_section_and_key(
508 514 section, key)
509 515 assert created_ui.ui_active is False
510 516 assert str2bool(created_ui.ui_value) is False
511 517
512 518
513 519 class TestCreateOrUpdateRepoHgSettings(object):
514 520 FORM_DATA = {
515 521 'extensions_largefiles': False,
516 522 'phases_publish': False
517 523 }
518 524
519 525 def test_creates_repo_hg_settings_when_data_is_correct(self, repo_stub):
520 526 model = VcsSettingsModel(repo=repo_stub.repo_name)
521 527 with mock.patch.object(model, '_create_or_update_ui') as create_mock:
522 528 model.create_or_update_repo_hg_settings(self.FORM_DATA)
523 529 expected_calls = [
524 530 mock.call(model.repo_settings, 'extensions', 'largefiles',
525 531 active=False, value=''),
526 532 mock.call(model.repo_settings, 'phases', 'publish', value='False'),
527 533 ]
528 534 assert expected_calls == create_mock.call_args_list
529 535
530 536 @pytest.mark.parametrize('field_to_remove', FORM_DATA.keys())
531 537 def test_key_is_not_found(self, repo_stub, field_to_remove):
532 538 model = VcsSettingsModel(repo=repo_stub.repo_name)
533 539 data = self.FORM_DATA.copy()
534 540 data.pop(field_to_remove)
535 541 with pytest.raises(ValueError) as exc_info:
536 542 model.create_or_update_repo_hg_settings(data)
537 543 expected_message = 'The given data does not contain {} key'.format(
538 544 field_to_remove)
539 545 assert exc_info.value.message == expected_message
540 546
541 547 def test_create_raises_exception_when_repository_not_specified(self):
542 548 model = VcsSettingsModel()
543 549 with pytest.raises(Exception) as exc_info:
544 550 model.create_or_update_repo_hg_settings(self.FORM_DATA)
545 551 assert exc_info.value.message == 'Repository is not specified'
546 552
547 553
548 554 class TestUpdateGlobalSslSetting(object):
549 555 def test_updates_global_hg_settings(self):
550 556 model = VcsSettingsModel()
551 557 with mock.patch.object(model, '_create_or_update_ui') as create_mock:
552 558 model.update_global_ssl_setting('False')
553 559 create_mock.assert_called_once_with(
554 560 model.global_settings, 'web', 'push_ssl', value='False')
555 561
556 562
557 563 class TestUpdateGlobalPathSetting(object):
558 564 def test_updates_global_path_settings(self):
559 565 model = VcsSettingsModel()
560 566 with mock.patch.object(model, '_create_or_update_ui') as create_mock:
561 567 model.update_global_path_setting('False')
562 568 create_mock.assert_called_once_with(
563 569 model.global_settings, 'paths', '/', value='False')
564 570
565 571
566 572 class TestCreateOrUpdateGlobalHgSettings(object):
567 573 FORM_DATA = {
568 574 'extensions_largefiles': False,
569 575 'largefiles_usercache': '/example/largefiles-store',
570 576 'phases_publish': False,
571 577 'extensions_hgsubversion': False
572 578 }
573 579
574 580 def test_creates_repo_hg_settings_when_data_is_correct(self):
575 581 model = VcsSettingsModel()
576 582 with mock.patch.object(model, '_create_or_update_ui') as create_mock:
577 583 model.create_or_update_global_hg_settings(self.FORM_DATA)
578 584 expected_calls = [
579 585 mock.call(model.global_settings, 'extensions', 'largefiles',
580 586 active=False, value=''),
581 587 mock.call(model.global_settings, 'largefiles', 'usercache',
582 588 value='/example/largefiles-store'),
583 589 mock.call(model.global_settings, 'phases', 'publish',
584 590 value='False'),
585 591 mock.call(model.global_settings, 'extensions', 'hgsubversion',
586 592 active=False)
587 593 ]
588 594 assert expected_calls == create_mock.call_args_list
589 595
590 596 @pytest.mark.parametrize('field_to_remove', FORM_DATA.keys())
591 597 def test_key_is_not_found(self, repo_stub, field_to_remove):
592 598 model = VcsSettingsModel(repo=repo_stub.repo_name)
593 599 data = self.FORM_DATA.copy()
594 600 data.pop(field_to_remove)
595 601 with pytest.raises(Exception) as exc_info:
596 602 model.create_or_update_global_hg_settings(data)
597 603 expected_message = 'The given data does not contain {} key'.format(
598 604 field_to_remove)
599 605 assert exc_info.value.message == expected_message
600 606
601 607
608 class TestCreateOrUpdateGlobalGitSettings(object):
609 FORM_DATA = {
610 'vcs_git_lfs_enabled': False,
611 'vcs_git_lfs_store_location': '/example/lfs-store',
612 }
613
614 def test_creates_repo_hg_settings_when_data_is_correct(self):
615 model = VcsSettingsModel()
616 with mock.patch.object(model, '_create_or_update_ui') as create_mock:
617 model.create_or_update_global_git_settings(self.FORM_DATA)
618 expected_calls = [
619 mock.call(model.global_settings, 'vcs_git_lfs', 'enabled',
620 active=False, value=False),
621 mock.call(model.global_settings, 'vcs_git_lfs', 'store_location',
622 value='/example/lfs-store'),
623 ]
624 assert expected_calls == create_mock.call_args_list
625
626
602 627 class TestDeleteRepoSvnPattern(object):
603 628 def test_success_when_repo_is_set(self, backend_svn):
604 629 repo_name = backend_svn.repo_name
605 630 model = VcsSettingsModel(repo=repo_name)
606 631 delete_ui_patch = mock.patch.object(model.repo_settings, 'delete_ui')
607 632 with delete_ui_patch as delete_ui_mock:
608 633 model.delete_repo_svn_pattern(123)
609 634 delete_ui_mock.assert_called_once_with(123)
610 635
611 636 def test_raises_exception_when_repository_is_not_specified(self):
612 637 model = VcsSettingsModel()
613 638 with pytest.raises(Exception) as exc_info:
614 639 model.delete_repo_svn_pattern(123)
615 640 assert exc_info.value.message == 'Repository is not specified'
616 641
617 642
618 643 class TestDeleteGlobalSvnPattern(object):
619 644 def test_delete_global_svn_pattern_calls_delete_ui(self):
620 645 model = VcsSettingsModel()
621 646 delete_ui_patch = mock.patch.object(model.global_settings, 'delete_ui')
622 647 with delete_ui_patch as delete_ui_mock:
623 648 model.delete_global_svn_pattern(123)
624 649 delete_ui_mock.assert_called_once_with(123)
625 650
626 651
627 652 class TestFilterUiSettings(object):
628 653 def test_settings_are_filtered(self):
629 654 model = VcsSettingsModel()
630 655 repo_settings = [
631 656 UiSetting('extensions', 'largefiles', '', True),
632 657 UiSetting('phases', 'publish', 'True', True),
633 658 UiSetting('hooks', 'changegroup.repo_size', 'hook', True),
634 659 UiSetting('hooks', 'changegroup.push_logger', 'hook', True),
635 660 UiSetting('hooks', 'outgoing.pull_logger', 'hook', True),
636 661 UiSetting(
637 662 'vcs_svn_branch', '84223c972204fa545ca1b22dac7bef5b68d7442d',
638 663 'test_branch', True),
639 664 UiSetting(
640 665 'vcs_svn_tag', '84229c972204fa545ca1b22dac7bef5b68d7442d',
641 666 'test_tag', True),
642 667 ]
643 668 non_repo_settings = [
644 669 UiSetting('largefiles', 'usercache', '/example/largefiles-store', True),
645 670 UiSetting('test', 'outgoing.pull_logger', 'hook', True),
646 671 UiSetting('hooks', 'test2', 'hook', True),
647 672 UiSetting(
648 673 'vcs_svn_repo', '84229c972204fa545ca1b22dac7bef5b68d7442d',
649 674 'test_tag', True),
650 675 ]
651 676 settings = repo_settings + non_repo_settings
652 677 filtered_settings = model._filter_ui_settings(settings)
653 678 assert sorted(filtered_settings) == sorted(repo_settings)
654 679
655 680
656 681 class TestFilterGeneralSettings(object):
657 682 def test_settings_are_filtered(self):
658 683 model = VcsSettingsModel()
659 684 settings = {
660 685 'rhodecode_abcde': 'value1',
661 686 'rhodecode_vwxyz': 'value2',
662 687 }
663 688 general_settings = {
664 689 'rhodecode_{}'.format(key): 'value'
665 690 for key in VcsSettingsModel.GENERAL_SETTINGS
666 691 }
667 692 settings.update(general_settings)
668 693
669 694 filtered_settings = model._filter_general_settings(general_settings)
670 695 assert sorted(filtered_settings) == sorted(general_settings)
671 696
672 697
673 698 class TestGetRepoUiSettings(object):
674 699 def test_global_uis_are_returned_when_no_repo_uis_found(
675 700 self, repo_stub):
676 701 model = VcsSettingsModel(repo=repo_stub.repo_name)
677 702 result = model.get_repo_ui_settings()
678 703 svn_sections = (
679 704 VcsSettingsModel.SVN_TAG_SECTION,
680 705 VcsSettingsModel.SVN_BRANCH_SECTION)
681 706 expected_result = [
682 707 s for s in model.global_settings.get_ui()
683 708 if s.section not in svn_sections]
684 709 assert sorted(result) == sorted(expected_result)
685 710
686 711 def test_repo_uis_are_overriding_global_uis(
687 712 self, repo_stub, settings_util):
688 713 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
689 714 settings_util.create_repo_rhodecode_ui(
690 715 repo_stub, section, 'repo', key=key, active=False)
691 716 model = VcsSettingsModel(repo=repo_stub.repo_name)
692 717 result = model.get_repo_ui_settings()
693 718 for setting in result:
694 719 locator = (setting.section, setting.key)
695 720 if locator in VcsSettingsModel.HOOKS_SETTINGS:
696 721 assert setting.value == 'repo'
697 722
698 723 assert setting.active is False
699 724
700 725 def test_global_svn_patterns_are_not_in_list(
701 726 self, repo_stub, settings_util):
702 727 svn_sections = (
703 728 VcsSettingsModel.SVN_TAG_SECTION,
704 729 VcsSettingsModel.SVN_BRANCH_SECTION)
705 730 for section in svn_sections:
706 731 settings_util.create_rhodecode_ui(
707 732 section, 'repo', key='deadbeef' + section, active=False)
708 733 model = VcsSettingsModel(repo=repo_stub.repo_name)
709 734 result = model.get_repo_ui_settings()
710 735 for setting in result:
711 736 assert setting.section not in svn_sections
712 737
713 738 def test_repo_uis_filtered_by_section_are_returned(
714 739 self, repo_stub, settings_util):
715 740 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
716 741 settings_util.create_repo_rhodecode_ui(
717 742 repo_stub, section, 'repo', key=key, active=False)
718 743 model = VcsSettingsModel(repo=repo_stub.repo_name)
719 744 section, key = VcsSettingsModel.HOOKS_SETTINGS[0]
720 745 result = model.get_repo_ui_settings(section=section)
721 746 for setting in result:
722 747 assert setting.section == section
723 748
724 749 def test_repo_uis_filtered_by_key_are_returned(
725 750 self, repo_stub, settings_util):
726 751 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
727 752 settings_util.create_repo_rhodecode_ui(
728 753 repo_stub, section, 'repo', key=key, active=False)
729 754 model = VcsSettingsModel(repo=repo_stub.repo_name)
730 755 section, key = VcsSettingsModel.HOOKS_SETTINGS[0]
731 756 result = model.get_repo_ui_settings(key=key)
732 757 for setting in result:
733 758 assert setting.key == key
734 759
735 760 def test_raises_exception_when_repository_is_not_specified(self):
736 761 model = VcsSettingsModel()
737 762 with pytest.raises(Exception) as exc_info:
738 763 model.get_repo_ui_settings()
739 764 assert exc_info.value.message == 'Repository is not specified'
740 765
741 766
742 767 class TestGetRepoGeneralSettings(object):
743 768 def test_global_settings_are_returned_when_no_repo_settings_found(
744 769 self, repo_stub):
745 770 model = VcsSettingsModel(repo=repo_stub.repo_name)
746 771 result = model.get_repo_general_settings()
747 772 expected_result = model.global_settings.get_all_settings()
748 773 assert sorted(result) == sorted(expected_result)
749 774
750 775 def test_repo_uis_are_overriding_global_uis(
751 776 self, repo_stub, settings_util):
752 777 for key in VcsSettingsModel.GENERAL_SETTINGS:
753 778 settings_util.create_repo_rhodecode_setting(
754 779 repo_stub, key, 'abcde', type_='unicode')
755 780 model = VcsSettingsModel(repo=repo_stub.repo_name)
756 781 result = model.get_repo_ui_settings()
757 782 for key in result:
758 783 if key in VcsSettingsModel.GENERAL_SETTINGS:
759 784 assert result[key] == 'abcde'
760 785
761 786 def test_raises_exception_when_repository_is_not_specified(self):
762 787 model = VcsSettingsModel()
763 788 with pytest.raises(Exception) as exc_info:
764 789 model.get_repo_general_settings()
765 790 assert exc_info.value.message == 'Repository is not specified'
766 791
767 792
768 793 class TestGetGlobalGeneralSettings(object):
769 794 def test_global_settings_are_returned(self, repo_stub):
770 795 model = VcsSettingsModel()
771 796 result = model.get_global_general_settings()
772 797 expected_result = model.global_settings.get_all_settings()
773 798 assert sorted(result) == sorted(expected_result)
774 799
775 800 def test_repo_uis_are_not_overriding_global_uis(
776 801 self, repo_stub, settings_util):
777 802 for key in VcsSettingsModel.GENERAL_SETTINGS:
778 803 settings_util.create_repo_rhodecode_setting(
779 804 repo_stub, key, 'abcde', type_='unicode')
780 805 model = VcsSettingsModel(repo=repo_stub.repo_name)
781 806 result = model.get_global_general_settings()
782 807 expected_result = model.global_settings.get_all_settings()
783 808 assert sorted(result) == sorted(expected_result)
784 809
785 810
786 811 class TestGetGlobalUiSettings(object):
787 812 def test_global_uis_are_returned(self, repo_stub):
788 813 model = VcsSettingsModel()
789 814 result = model.get_global_ui_settings()
790 815 expected_result = model.global_settings.get_ui()
791 816 assert sorted(result) == sorted(expected_result)
792 817
793 818 def test_repo_uis_are_not_overriding_global_uis(
794 819 self, repo_stub, settings_util):
795 820 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
796 821 settings_util.create_repo_rhodecode_ui(
797 822 repo_stub, section, 'repo', key=key, active=False)
798 823 model = VcsSettingsModel(repo=repo_stub.repo_name)
799 824 result = model.get_global_ui_settings()
800 825 expected_result = model.global_settings.get_ui()
801 826 assert sorted(result) == sorted(expected_result)
802 827
803 828 def test_ui_settings_filtered_by_section(
804 829 self, repo_stub, settings_util):
805 830 model = VcsSettingsModel(repo=repo_stub.repo_name)
806 831 section, key = VcsSettingsModel.HOOKS_SETTINGS[0]
807 832 result = model.get_global_ui_settings(section=section)
808 833 expected_result = model.global_settings.get_ui(section=section)
809 834 assert sorted(result) == sorted(expected_result)
810 835
811 836 def test_ui_settings_filtered_by_key(
812 837 self, repo_stub, settings_util):
813 838 model = VcsSettingsModel(repo=repo_stub.repo_name)
814 839 section, key = VcsSettingsModel.HOOKS_SETTINGS[0]
815 840 result = model.get_global_ui_settings(key=key)
816 841 expected_result = model.global_settings.get_ui(key=key)
817 842 assert sorted(result) == sorted(expected_result)
818 843
819 844
820 845 class TestGetGeneralSettings(object):
821 846 def test_global_settings_are_returned_when_inherited_is_true(
822 847 self, repo_stub, settings_util):
823 848 model = VcsSettingsModel(repo=repo_stub.repo_name)
824 849 model.inherit_global_settings = True
825 850 for key in VcsSettingsModel.GENERAL_SETTINGS:
826 851 settings_util.create_repo_rhodecode_setting(
827 852 repo_stub, key, 'abcde', type_='unicode')
828 853 result = model.get_general_settings()
829 854 expected_result = model.get_global_general_settings()
830 855 assert sorted(result) == sorted(expected_result)
831 856
832 857 def test_repo_settings_are_returned_when_inherited_is_false(
833 858 self, repo_stub, settings_util):
834 859 model = VcsSettingsModel(repo=repo_stub.repo_name)
835 860 model.inherit_global_settings = False
836 861 for key in VcsSettingsModel.GENERAL_SETTINGS:
837 862 settings_util.create_repo_rhodecode_setting(
838 863 repo_stub, key, 'abcde', type_='unicode')
839 864 result = model.get_general_settings()
840 865 expected_result = model.get_repo_general_settings()
841 866 assert sorted(result) == sorted(expected_result)
842 867
843 868 def test_global_settings_are_returned_when_no_repository_specified(self):
844 869 model = VcsSettingsModel()
845 870 result = model.get_general_settings()
846 871 expected_result = model.get_global_general_settings()
847 872 assert sorted(result) == sorted(expected_result)
848 873
849 874
850 875 class TestGetUiSettings(object):
851 876 def test_global_settings_are_returned_when_inherited_is_true(
852 877 self, repo_stub, settings_util):
853 878 model = VcsSettingsModel(repo=repo_stub.repo_name)
854 879 model.inherit_global_settings = True
855 880 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
856 881 settings_util.create_repo_rhodecode_ui(
857 882 repo_stub, section, 'repo', key=key, active=True)
858 883 result = model.get_ui_settings()
859 884 expected_result = model.get_global_ui_settings()
860 885 assert sorted(result) == sorted(expected_result)
861 886
862 887 def test_repo_settings_are_returned_when_inherited_is_false(
863 888 self, repo_stub, settings_util):
864 889 model = VcsSettingsModel(repo=repo_stub.repo_name)
865 890 model.inherit_global_settings = False
866 891 for section, key in VcsSettingsModel.HOOKS_SETTINGS:
867 892 settings_util.create_repo_rhodecode_ui(
868 893 repo_stub, section, 'repo', key=key, active=True)
869 894 result = model.get_ui_settings()
870 895 expected_result = model.get_repo_ui_settings()
871 896 assert sorted(result) == sorted(expected_result)
872 897
873 898 def test_repo_settings_filtered_by_section_and_key(self, repo_stub):
874 899 model = VcsSettingsModel(repo=repo_stub.repo_name)
875 900 model.inherit_global_settings = False
876 901 args = ('section', 'key')
877 902 with mock.patch.object(model, 'get_repo_ui_settings') as settings_mock:
878 903 model.get_ui_settings(*args)
879 904 settings_mock.assert_called_once_with(*args)
880 905
881 906 def test_global_settings_filtered_by_section_and_key(self):
882 907 model = VcsSettingsModel()
883 908 args = ('section', 'key')
884 909 with mock.patch.object(model, 'get_global_ui_settings') as (
885 910 settings_mock):
886 911 model.get_ui_settings(*args)
887 912 settings_mock.assert_called_once_with(*args)
888 913
889 914 def test_global_settings_are_returned_when_no_repository_specified(self):
890 915 model = VcsSettingsModel()
891 916 result = model.get_ui_settings()
892 917 expected_result = model.get_global_ui_settings()
893 918 assert sorted(result) == sorted(expected_result)
894 919
895 920
896 921 class TestGetSvnPatterns(object):
897 922 def test_repo_settings_filtered_by_section_and_key(self, repo_stub):
898 923 model = VcsSettingsModel(repo=repo_stub.repo_name)
899 924 args = ('section', )
900 925 with mock.patch.object(model, 'get_repo_ui_settings') as settings_mock:
901 926 model.get_svn_patterns(*args)
902 927 settings_mock.assert_called_once_with(*args)
903 928
904 929 def test_global_settings_filtered_by_section_and_key(self):
905 930 model = VcsSettingsModel()
906 931 args = ('section', )
907 932 with mock.patch.object(model, 'get_global_ui_settings') as (
908 933 settings_mock):
909 934 model.get_svn_patterns(*args)
910 935 settings_mock.assert_called_once_with(*args)
911 936
912 937
913 938 class TestGetReposLocation(object):
914 939 def test_returns_repos_location(self, repo_stub):
915 940 model = VcsSettingsModel()
916 941
917 942 result_mock = mock.Mock()
918 943 result_mock.ui_value = '/tmp'
919 944
920 945 with mock.patch.object(model, 'global_settings') as settings_mock:
921 946 settings_mock.get_ui_by_key.return_value = result_mock
922 947 result = model.get_repos_location()
923 948
924 949 settings_mock.get_ui_by_key.assert_called_once_with('/')
925 950 assert result == '/tmp'
926 951
927 952
928 953 class TestCreateOrUpdateRepoSettings(object):
929 954 FORM_DATA = {
930 955 'inherit_global_settings': False,
931 956 'hooks_changegroup_repo_size': False,
932 957 'hooks_changegroup_push_logger': False,
933 958 'hooks_outgoing_pull_logger': False,
934 959 'extensions_largefiles': False,
935 960 'largefiles_usercache': '/example/largefiles-store',
961 'vcs_git_lfs_enabled': False,
962 'vcs_git_lfs_store_location': '/',
936 963 'phases_publish': 'False',
937 964 'rhodecode_pr_merge_enabled': False,
938 965 'rhodecode_use_outdated_comments': False,
939 966 'new_svn_branch': '',
940 967 'new_svn_tag': ''
941 968 }
942 969
943 970 def test_get_raises_exception_when_repository_not_specified(self):
944 971 model = VcsSettingsModel()
945 972 with pytest.raises(Exception) as exc_info:
946 973 model.create_or_update_repo_settings(data=self.FORM_DATA)
947 974 assert exc_info.value.message == 'Repository is not specified'
948 975
949 976 def test_only_svn_settings_are_updated_when_type_is_svn(self, backend_svn):
950 977 repo = backend_svn.create_repo()
951 978 model = VcsSettingsModel(repo=repo)
952 979 with self._patch_model(model) as mocks:
953 980 model.create_or_update_repo_settings(
954 981 data=self.FORM_DATA, inherit_global_settings=False)
955 982 mocks['create_repo_svn_settings'].assert_called_once_with(
956 983 self.FORM_DATA)
957 984 non_called_methods = (
958 985 'create_or_update_repo_hook_settings',
959 986 'create_or_update_repo_pr_settings',
960 987 'create_or_update_repo_hg_settings')
961 988 for method in non_called_methods:
962 989 assert mocks[method].call_count == 0
963 990
964 991 def test_non_svn_settings_are_updated_when_type_is_hg(self, backend_hg):
965 992 repo = backend_hg.create_repo()
966 993 model = VcsSettingsModel(repo=repo)
967 994 with self._patch_model(model) as mocks:
968 995 model.create_or_update_repo_settings(
969 996 data=self.FORM_DATA, inherit_global_settings=False)
970 997
971 998 assert mocks['create_repo_svn_settings'].call_count == 0
972 999 called_methods = (
973 1000 'create_or_update_repo_hook_settings',
974 1001 'create_or_update_repo_pr_settings',
975 1002 'create_or_update_repo_hg_settings')
976 1003 for method in called_methods:
977 1004 mocks[method].assert_called_once_with(self.FORM_DATA)
978 1005
979 1006 def test_non_svn_and_hg_settings_are_updated_when_type_is_git(
980 1007 self, backend_git):
981 1008 repo = backend_git.create_repo()
982 1009 model = VcsSettingsModel(repo=repo)
983 1010 with self._patch_model(model) as mocks:
984 1011 model.create_or_update_repo_settings(
985 1012 data=self.FORM_DATA, inherit_global_settings=False)
986 1013
987 1014 assert mocks['create_repo_svn_settings'].call_count == 0
988 1015 called_methods = (
989 1016 'create_or_update_repo_hook_settings',
990 1017 'create_or_update_repo_pr_settings')
991 1018 non_called_methods = (
992 1019 'create_repo_svn_settings',
993 1020 'create_or_update_repo_hg_settings'
994 1021 )
995 1022 for method in called_methods:
996 1023 mocks[method].assert_called_once_with(self.FORM_DATA)
997 1024 for method in non_called_methods:
998 1025 assert mocks[method].call_count == 0
999 1026
1000 1027 def test_no_methods_are_called_when_settings_are_inherited(
1001 1028 self, backend):
1002 1029 repo = backend.create_repo()
1003 1030 model = VcsSettingsModel(repo=repo)
1004 1031 with self._patch_model(model) as mocks:
1005 1032 model.create_or_update_repo_settings(
1006 1033 data=self.FORM_DATA, inherit_global_settings=True)
1007 1034 for method_name in mocks:
1008 1035 assert mocks[method_name].call_count == 0
1009 1036
1010 1037 def test_cache_is_marked_for_invalidation(self, repo_stub):
1011 1038 model = VcsSettingsModel(repo=repo_stub)
1012 1039 invalidation_patcher = mock.patch(
1013 1040 'rhodecode.controllers.admin.repos.ScmModel.mark_for_invalidation')
1014 1041 with invalidation_patcher as invalidation_mock:
1015 1042 model.create_or_update_repo_settings(
1016 1043 data=self.FORM_DATA, inherit_global_settings=True)
1017 1044 invalidation_mock.assert_called_once_with(
1018 1045 repo_stub.repo_name, delete=True)
1019 1046
1020 1047 def test_inherit_flag_is_saved(self, repo_stub):
1021 1048 model = VcsSettingsModel(repo=repo_stub)
1022 1049 model.inherit_global_settings = True
1023 1050 with self._patch_model(model):
1024 1051 model.create_or_update_repo_settings(
1025 1052 data=self.FORM_DATA, inherit_global_settings=False)
1026 1053 assert model.inherit_global_settings is False
1027 1054
1028 1055 def _patch_model(self, model):
1029 1056 return mock.patch.multiple(
1030 1057 model,
1031 1058 create_repo_svn_settings=mock.DEFAULT,
1032 1059 create_or_update_repo_hook_settings=mock.DEFAULT,
1033 1060 create_or_update_repo_pr_settings=mock.DEFAULT,
1034 1061 create_or_update_repo_hg_settings=mock.DEFAULT)
General Comments 0
You need to be logged in to leave comments. Login now