##// END OF EJS Templates
Pass in old groups data to CanWriteToGroup validator for later skipping group checks....
marcink -
r3524:af96fb19 beta
parent child Browse files
Show More
@@ -1,536 +1,538 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.admin.repos
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Repositories controller for RhodeCode
7 7
8 8 :created_on: Apr 7, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27 import traceback
28 28 import formencode
29 29 from formencode import htmlfill
30 30
31 31 from webob.exc import HTTPInternalServerError, HTTPForbidden
32 32 from pylons import request, session, tmpl_context as c, url
33 33 from pylons.controllers.util import redirect
34 34 from pylons.i18n.translation import _
35 35 from sqlalchemy.exc import IntegrityError
36 36
37 37 import rhodecode
38 38 from rhodecode.lib import helpers as h
39 39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
40 40 HasPermissionAnyDecorator, HasRepoPermissionAllDecorator, NotAnonymous,\
41 41 HasPermissionAny, HasReposGroupPermissionAny
42 42 from rhodecode.lib.base import BaseRepoController, render
43 43 from rhodecode.lib.utils import invalidate_cache, action_logger, repo_name_slug
44 44 from rhodecode.lib.helpers import get_token
45 45 from rhodecode.model.meta import Session
46 46 from rhodecode.model.db import User, Repository, UserFollowing, RepoGroup,\
47 47 RhodeCodeSetting, RepositoryField
48 48 from rhodecode.model.forms import RepoForm, RepoFieldForm
49 49 from rhodecode.model.scm import ScmModel, GroupList
50 50 from rhodecode.model.repo import RepoModel
51 51 from rhodecode.lib.compat import json
52 52 from sqlalchemy.sql.expression import func
53 53
54 54 log = logging.getLogger(__name__)
55 55
56 56
57 57 class ReposController(BaseRepoController):
58 58 """
59 59 REST Controller styled on the Atom Publishing Protocol"""
60 60 # To properly map this controller, ensure your config/routing.py
61 61 # file has a resource setup:
62 62 # map.resource('repo', 'repos')
63 63
64 64 @LoginRequired()
65 65 def __before__(self):
66 66 c.admin_user = session.get('admin_user')
67 67 c.admin_username = session.get('admin_username')
68 68 super(ReposController, self).__before__()
69 69
70 70 def __load_defaults(self):
71 71 acl_groups = GroupList(RepoGroup.query().all(),
72 72 perm_set=['group.write', 'group.admin'])
73 73 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
74 74 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
75 75
76 76 repo_model = RepoModel()
77 77 c.users_array = repo_model.get_users_js()
78 78 c.users_groups_array = repo_model.get_users_groups_js()
79 79 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
80 80 c.landing_revs_choices = choices
81 81
82 82 def __load_data(self, repo_name=None):
83 83 """
84 84 Load defaults settings for edit, and update
85 85
86 86 :param repo_name:
87 87 """
88 88 self.__load_defaults()
89 89
90 90 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
91 91 repo = db_repo.scm_instance
92 92
93 93 if c.repo_info is None:
94 94 h.not_mapped_error(repo_name)
95 95 return redirect(url('repos'))
96 96
97 97 ##override defaults for exact repo info here git/hg etc
98 98 choices, c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info)
99 99 c.landing_revs_choices = choices
100 100
101 101 c.default_user_id = User.get_by_username('default').user_id
102 102 c.in_public_journal = UserFollowing.query()\
103 103 .filter(UserFollowing.user_id == c.default_user_id)\
104 104 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
105 105
106 106 if c.repo_info.stats:
107 107 # this is on what revision we ended up so we add +1 for count
108 108 last_rev = c.repo_info.stats.stat_on_revision + 1
109 109 else:
110 110 last_rev = 0
111 111 c.stats_revision = last_rev
112 112
113 113 c.repo_last_rev = repo.count() if repo.revisions else 0
114 114
115 115 if last_rev == 0 or c.repo_last_rev == 0:
116 116 c.stats_percentage = 0
117 117 else:
118 118 c.stats_percentage = '%.2f' % ((float((last_rev)) /
119 119 c.repo_last_rev) * 100)
120 120
121 121 c.repo_fields = RepositoryField.query()\
122 122 .filter(RepositoryField.repository == db_repo).all()
123 123
124 124 defaults = RepoModel()._get_defaults(repo_name)
125 125
126 126 c.repos_list = [('', _('--REMOVE FORK--'))]
127 127 c.repos_list += [(x.repo_id, x.repo_name) for x in
128 128 Repository.query().order_by(Repository.repo_name).all()
129 129 if x.repo_id != c.repo_info.repo_id]
130 130
131 131 defaults['id_fork_of'] = db_repo.fork.repo_id if db_repo.fork else ''
132 132 return defaults
133 133
134 134 @HasPermissionAllDecorator('hg.admin')
135 135 def index(self, format='html'):
136 136 """GET /repos: All items in the collection"""
137 137 # url('repos')
138 138
139 139 c.repos_list = Repository.query()\
140 140 .order_by(func.lower(Repository.repo_name))\
141 141 .all()
142 142
143 143 repos_data = RepoModel().get_repos_as_dict(repos_list=c.repos_list,
144 144 admin=True,
145 145 super_user_actions=True)
146 146 #json used to render the grid
147 147 c.data = json.dumps(repos_data)
148 148
149 149 return render('admin/repos/repos.html')
150 150
151 151 @NotAnonymous()
152 152 def create(self):
153 153 """
154 154 POST /repos: Create a new item"""
155 155 # url('repos')
156 156
157 157 self.__load_defaults()
158 158 form_result = {}
159 159 try:
160 160 form_result = RepoForm(repo_groups=c.repo_groups_choices,
161 161 landing_revs=c.landing_revs_choices)()\
162 162 .to_python(dict(request.POST))
163 163
164 164 new_repo = RepoModel().create(form_result,
165 165 self.rhodecode_user.user_id)
166 166 if form_result['clone_uri']:
167 167 h.flash(_('created repository %s from %s') \
168 168 % (form_result['repo_name'], form_result['clone_uri']),
169 169 category='success')
170 170 else:
171 171 h.flash(_('created repository %s') % form_result['repo_name'],
172 172 category='success')
173 173
174 174 if request.POST.get('user_created'):
175 175 # created by regular non admin user
176 176 action_logger(self.rhodecode_user, 'user_created_repo',
177 177 form_result['repo_name_full'], self.ip_addr,
178 178 self.sa)
179 179 else:
180 180 action_logger(self.rhodecode_user, 'admin_created_repo',
181 181 form_result['repo_name_full'], self.ip_addr,
182 182 self.sa)
183 183 Session().commit()
184 184 except formencode.Invalid, errors:
185 185 return htmlfill.render(
186 186 render('admin/repos/repo_add.html'),
187 187 defaults=errors.value,
188 188 errors=errors.error_dict or {},
189 189 prefix_error=False,
190 190 encoding="UTF-8")
191 191
192 192 except Exception:
193 193 log.error(traceback.format_exc())
194 194 msg = _('error occurred during creation of repository %s') \
195 195 % form_result.get('repo_name')
196 196 h.flash(msg, category='error')
197 197 if c.rhodecode_user.is_admin:
198 198 return redirect(url('repos'))
199 199 return redirect(url('home'))
200 200 #redirect to our new repo !
201 201 return redirect(url('summary_home', repo_name=new_repo.repo_name))
202 202
203 203 @HasPermissionAllDecorator('hg.admin')
204 204 def new(self, format='html'):
205 205 """
206 206 WARNING: this function is depracated see settings.create_repo !!
207 207
208 208 GET /repos/new: Form to create a new item
209 209 """
210 210
211 211 parent_group = request.GET.get('parent_group')
212 212 self.__load_defaults()
213 213 ## apply the defaults from defaults page
214 214 defaults = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
215 215 if parent_group:
216 216 defaults.update({'repo_group': parent_group})
217 217
218 218 return htmlfill.render(
219 219 render('admin/repos/repo_add.html'),
220 220 defaults=defaults,
221 221 errors={},
222 222 prefix_error=False,
223 223 encoding="UTF-8"
224 224 )
225 225
226 226 @HasPermissionAllDecorator('hg.admin')
227 227 def update(self, repo_name):
228 228 """
229 229 PUT /repos/repo_name: Update an existing item"""
230 230 # Forms posted to this method should contain a hidden field:
231 231 # <input type="hidden" name="_method" value="PUT" />
232 232 # Or using helpers:
233 233 # h.form(url('repo', repo_name=ID),
234 234 # method='put')
235 235 # url('repo', repo_name=ID)
236 236 self.__load_defaults()
237 237 repo_model = RepoModel()
238 238 changed_name = repo_name
239 239 #override the choices with extracted revisions !
240 240 choices, c.landing_revs = ScmModel().get_repo_landing_revs(repo_name)
241 241 c.landing_revs_choices = choices
242
243 _form = RepoForm(edit=True, old_data={'repo_name': repo_name},
242 repo = Repository.get_by_repo_name(repo_name)
243 _form = RepoForm(edit=True, old_data={'repo_name': repo_name,
244 'repo_group': repo.group.get_dict() \
245 if repo.group else {}},
244 246 repo_groups=c.repo_groups_choices,
245 247 landing_revs=c.landing_revs_choices)()
246 248 try:
247 249 form_result = _form.to_python(dict(request.POST))
248 250 repo = repo_model.update(repo_name, **form_result)
249 251 invalidate_cache('get_repo_cached_%s' % repo_name)
250 252 h.flash(_('Repository %s updated successfully') % repo_name,
251 253 category='success')
252 254 changed_name = repo.repo_name
253 255 action_logger(self.rhodecode_user, 'admin_updated_repo',
254 256 changed_name, self.ip_addr, self.sa)
255 257 Session().commit()
256 258 except formencode.Invalid, errors:
257 259 defaults = self.__load_data(repo_name)
258 260 defaults.update(errors.value)
259 261 return htmlfill.render(
260 262 render('admin/repos/repo_edit.html'),
261 263 defaults=defaults,
262 264 errors=errors.error_dict or {},
263 265 prefix_error=False,
264 266 encoding="UTF-8")
265 267
266 268 except Exception:
267 269 log.error(traceback.format_exc())
268 270 h.flash(_('error occurred during update of repository %s') \
269 271 % repo_name, category='error')
270 272 return redirect(url('edit_repo', repo_name=changed_name))
271 273
272 274 @HasPermissionAllDecorator('hg.admin')
273 275 def delete(self, repo_name):
274 276 """
275 277 DELETE /repos/repo_name: Delete an existing item"""
276 278 # Forms posted to this method should contain a hidden field:
277 279 # <input type="hidden" name="_method" value="DELETE" />
278 280 # Or using helpers:
279 281 # h.form(url('repo', repo_name=ID),
280 282 # method='delete')
281 283 # url('repo', repo_name=ID)
282 284
283 285 repo_model = RepoModel()
284 286 repo = repo_model.get_by_repo_name(repo_name)
285 287 if not repo:
286 288 h.not_mapped_error(repo_name)
287 289 return redirect(url('repos'))
288 290 try:
289 291 _forks = repo.forks.count()
290 292 if _forks and request.POST.get('forks'):
291 293 do = request.POST['forks']
292 294 if do == 'detach_forks':
293 295 for r in repo.forks:
294 296 log.debug('Detaching fork %s from repo %s' % (r, repo))
295 297 r.fork = None
296 298 Session().add(r)
297 299 h.flash(_('detached %s forks') % _forks, category='success')
298 300 elif do == 'delete_forks':
299 301 for r in repo.forks:
300 302 log.debug('Deleting fork %s of repo %s' % (r, repo))
301 303 repo_model.delete(r)
302 304 h.flash(_('deleted %s forks') % _forks, category='success')
303 305 action_logger(self.rhodecode_user, 'admin_deleted_repo',
304 306 repo_name, self.ip_addr, self.sa)
305 307 repo_model.delete(repo)
306 308 invalidate_cache('get_repo_cached_%s' % repo_name)
307 309 h.flash(_('deleted repository %s') % repo_name, category='success')
308 310 Session().commit()
309 311 except IntegrityError, e:
310 312 if e.message.find('repositories_fork_id_fkey') != -1:
311 313 log.error(traceback.format_exc())
312 314 h.flash(_('Cannot delete %s it still contains attached '
313 315 'forks') % repo_name,
314 316 category='warning')
315 317 else:
316 318 log.error(traceback.format_exc())
317 319 h.flash(_('An error occurred during '
318 320 'deletion of %s') % repo_name,
319 321 category='error')
320 322
321 323 except Exception, e:
322 324 log.error(traceback.format_exc())
323 325 h.flash(_('An error occurred during deletion of %s') % repo_name,
324 326 category='error')
325 327
326 328 return redirect(url('repos'))
327 329
328 330 @HasRepoPermissionAllDecorator('repository.admin')
329 331 def delete_perm_user(self, repo_name):
330 332 """
331 333 DELETE an existing repository permission user
332 334
333 335 :param repo_name:
334 336 """
335 337 try:
336 338 RepoModel().revoke_user_permission(repo=repo_name,
337 339 user=request.POST['user_id'])
338 340 Session().commit()
339 341 except Exception:
340 342 log.error(traceback.format_exc())
341 343 h.flash(_('An error occurred during deletion of repository user'),
342 344 category='error')
343 345 raise HTTPInternalServerError()
344 346
345 347 @HasRepoPermissionAllDecorator('repository.admin')
346 348 def delete_perm_users_group(self, repo_name):
347 349 """
348 350 DELETE an existing repository permission user group
349 351
350 352 :param repo_name:
351 353 """
352 354
353 355 try:
354 356 RepoModel().revoke_users_group_permission(
355 357 repo=repo_name, group_name=request.POST['users_group_id']
356 358 )
357 359 Session().commit()
358 360 except Exception:
359 361 log.error(traceback.format_exc())
360 362 h.flash(_('An error occurred during deletion of repository'
361 363 ' user groups'),
362 364 category='error')
363 365 raise HTTPInternalServerError()
364 366
365 367 @HasPermissionAllDecorator('hg.admin')
366 368 def repo_stats(self, repo_name):
367 369 """
368 370 DELETE an existing repository statistics
369 371
370 372 :param repo_name:
371 373 """
372 374
373 375 try:
374 376 RepoModel().delete_stats(repo_name)
375 377 Session().commit()
376 378 except Exception, e:
377 379 log.error(traceback.format_exc())
378 380 h.flash(_('An error occurred during deletion of repository stats'),
379 381 category='error')
380 382 return redirect(url('edit_repo', repo_name=repo_name))
381 383
382 384 @HasPermissionAllDecorator('hg.admin')
383 385 def repo_cache(self, repo_name):
384 386 """
385 387 INVALIDATE existing repository cache
386 388
387 389 :param repo_name:
388 390 """
389 391
390 392 try:
391 393 ScmModel().mark_for_invalidation(repo_name)
392 394 Session().commit()
393 395 except Exception, e:
394 396 log.error(traceback.format_exc())
395 397 h.flash(_('An error occurred during cache invalidation'),
396 398 category='error')
397 399 return redirect(url('edit_repo', repo_name=repo_name))
398 400
399 401 @HasPermissionAllDecorator('hg.admin')
400 402 def repo_locking(self, repo_name):
401 403 """
402 404 Unlock repository when it is locked !
403 405
404 406 :param repo_name:
405 407 """
406 408
407 409 try:
408 410 repo = Repository.get_by_repo_name(repo_name)
409 411 if request.POST.get('set_lock'):
410 412 Repository.lock(repo, c.rhodecode_user.user_id)
411 413 elif request.POST.get('set_unlock'):
412 414 Repository.unlock(repo)
413 415 except Exception, e:
414 416 log.error(traceback.format_exc())
415 417 h.flash(_('An error occurred during unlocking'),
416 418 category='error')
417 419 return redirect(url('edit_repo', repo_name=repo_name))
418 420
419 421 @HasPermissionAllDecorator('hg.admin')
420 422 def repo_public_journal(self, repo_name):
421 423 """
422 424 Set's this repository to be visible in public journal,
423 425 in other words assing default user to follow this repo
424 426
425 427 :param repo_name:
426 428 """
427 429
428 430 cur_token = request.POST.get('auth_token')
429 431 token = get_token()
430 432 if cur_token == token:
431 433 try:
432 434 repo_id = Repository.get_by_repo_name(repo_name).repo_id
433 435 user_id = User.get_by_username('default').user_id
434 436 self.scm_model.toggle_following_repo(repo_id, user_id)
435 437 h.flash(_('Updated repository visibility in public journal'),
436 438 category='success')
437 439 Session().commit()
438 440 except:
439 441 h.flash(_('An error occurred during setting this'
440 442 ' repository in public journal'),
441 443 category='error')
442 444
443 445 else:
444 446 h.flash(_('Token mismatch'), category='error')
445 447 return redirect(url('edit_repo', repo_name=repo_name))
446 448
447 449 @HasPermissionAllDecorator('hg.admin')
448 450 def repo_pull(self, repo_name):
449 451 """
450 452 Runs task to update given repository with remote changes,
451 453 ie. make pull on remote location
452 454
453 455 :param repo_name:
454 456 """
455 457 try:
456 458 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
457 459 h.flash(_('Pulled from remote location'), category='success')
458 460 except Exception, e:
459 461 h.flash(_('An error occurred during pull from remote location'),
460 462 category='error')
461 463
462 464 return redirect(url('edit_repo', repo_name=repo_name))
463 465
464 466 @HasPermissionAllDecorator('hg.admin')
465 467 def repo_as_fork(self, repo_name):
466 468 """
467 469 Mark given repository as a fork of another
468 470
469 471 :param repo_name:
470 472 """
471 473 try:
472 474 fork_id = request.POST.get('id_fork_of')
473 475 repo = ScmModel().mark_as_fork(repo_name, fork_id,
474 476 self.rhodecode_user.username)
475 477 fork = repo.fork.repo_name if repo.fork else _('Nothing')
476 478 Session().commit()
477 479 h.flash(_('Marked repo %s as fork of %s') % (repo_name, fork),
478 480 category='success')
479 481 except Exception, e:
480 482 log.error(traceback.format_exc())
481 483 h.flash(_('An error occurred during this operation'),
482 484 category='error')
483 485
484 486 return redirect(url('edit_repo', repo_name=repo_name))
485 487
486 488 @HasPermissionAllDecorator('hg.admin')
487 489 def show(self, repo_name, format='html'):
488 490 """GET /repos/repo_name: Show a specific item"""
489 491 # url('repo', repo_name=ID)
490 492
491 493 @HasPermissionAllDecorator('hg.admin')
492 494 def edit(self, repo_name, format='html'):
493 495 """GET /repos/repo_name/edit: Form to edit an existing item"""
494 496 # url('edit_repo', repo_name=ID)
495 497 defaults = self.__load_data(repo_name)
496 498
497 499 return htmlfill.render(
498 500 render('admin/repos/repo_edit.html'),
499 501 defaults=defaults,
500 502 encoding="UTF-8",
501 503 force_defaults=False
502 504 )
503 505
504 506 @HasPermissionAllDecorator('hg.admin')
505 507 def create_repo_field(self, repo_name):
506 508 try:
507 509 form_result = RepoFieldForm()().to_python(dict(request.POST))
508 510 new_field = RepositoryField()
509 511 new_field.repository = Repository.get_by_repo_name(repo_name)
510 512 new_field.field_key = form_result['new_field_key']
511 513 new_field.field_type = form_result['new_field_type'] # python type
512 514 new_field.field_value = form_result['new_field_value'] # set initial blank value
513 515 new_field.field_desc = form_result['new_field_desc']
514 516 new_field.field_label = form_result['new_field_label']
515 517 Session().add(new_field)
516 518 Session().commit()
517 519
518 520 except Exception, e:
519 521 log.error(traceback.format_exc())
520 522 msg = _('An error occurred during creation of field')
521 523 if isinstance(e, formencode.Invalid):
522 524 msg += ". " + e.msg
523 525 h.flash(msg, category='error')
524 526 return redirect(url('edit_repo', repo_name=repo_name))
525 527
526 528 @HasPermissionAllDecorator('hg.admin')
527 529 def delete_repo_field(self, repo_name, field_id):
528 530 field = RepositoryField.get_or_404(field_id)
529 531 try:
530 532 Session().delete(field)
531 533 Session().commit()
532 534 except Exception, e:
533 535 log.error(traceback.format_exc())
534 536 msg = _('An error occurred during removal of field')
535 537 h.flash(msg, category='error')
536 538 return redirect(url('edit_repo', repo_name=repo_name))
@@ -1,198 +1,200 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.settings
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Settings controller for rhodecode
7 7
8 8 :created_on: Jun 30, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27 import traceback
28 28 import formencode
29 29
30 30 from formencode import htmlfill
31 31
32 32 from pylons import tmpl_context as c, request, url
33 33 from pylons.controllers.util import redirect
34 34 from pylons.i18n.translation import _
35 35
36 36 import rhodecode.lib.helpers as h
37 37
38 38 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAllDecorator,\
39 39 HasRepoPermissionAnyDecorator
40 40 from rhodecode.lib.base import BaseRepoController, render
41 41 from rhodecode.lib.utils import invalidate_cache, action_logger
42 42
43 43 from rhodecode.model.forms import RepoSettingsForm
44 44 from rhodecode.model.repo import RepoModel
45 45 from rhodecode.model.db import RepoGroup, Repository, RepositoryField
46 46 from rhodecode.model.meta import Session
47 47 from rhodecode.model.scm import ScmModel, GroupList
48 48
49 49 log = logging.getLogger(__name__)
50 50
51 51
52 52 class SettingsController(BaseRepoController):
53 53
54 54 @LoginRequired()
55 55 def __before__(self):
56 56 super(SettingsController, self).__before__()
57 57
58 58 def __load_defaults(self):
59 59 acl_groups = GroupList(RepoGroup.query().all(),
60 60 perm_set=['group.write', 'group.admin'])
61 61 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
62 62 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
63 63
64 64 repo_model = RepoModel()
65 65 c.users_array = repo_model.get_users_js()
66 66 c.users_groups_array = repo_model.get_users_groups_js()
67 67 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
68 68 c.landing_revs_choices = choices
69 69
70 70 def __load_data(self, repo_name=None):
71 71 """
72 72 Load defaults settings for edit, and update
73 73
74 74 :param repo_name:
75 75 """
76 76 self.__load_defaults()
77 77
78 78 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
79 79
80 80 if c.repo_info is None:
81 81 h.not_mapped_error(repo_name)
82 82 return redirect(url('home'))
83 83
84 84 ##override defaults for exact repo info here git/hg etc
85 85 choices, c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info)
86 86 c.landing_revs_choices = choices
87 87 c.repo_fields = RepositoryField.query()\
88 88 .filter(RepositoryField.repository == db_repo).all()
89 89 defaults = RepoModel()._get_defaults(repo_name)
90 90
91 91 return defaults
92 92
93 93 @HasRepoPermissionAllDecorator('repository.admin')
94 94 def index(self, repo_name):
95 95 defaults = self.__load_data(repo_name)
96 96
97 97 return htmlfill.render(
98 98 render('settings/repo_settings.html'),
99 99 defaults=defaults,
100 100 encoding="UTF-8",
101 101 force_defaults=False
102 102 )
103 103
104 104 @HasRepoPermissionAllDecorator('repository.admin')
105 105 def update(self, repo_name):
106 106 self.__load_defaults()
107 107 repo_model = RepoModel()
108 108 changed_name = repo_name
109 109 #override the choices with extracted revisions !
110 110 choices, c.landing_revs = ScmModel().get_repo_landing_revs(repo_name)
111 111 c.landing_revs_choices = choices
112
112 repo = Repository.get_by_repo_name(repo_name)
113 113 _form = RepoSettingsForm(edit=True,
114 old_data={'repo_name': repo_name},
114 old_data={'repo_name': repo_name,
115 'repo_group': repo.group.get_dict() \
116 if repo.group else {}},
115 117 repo_groups=c.repo_groups_choices,
116 118 landing_revs=c.landing_revs_choices)()
117 119 try:
118 120 form_result = _form.to_python(dict(request.POST))
119 121 repo_model.update(repo_name, **form_result)
120 122 invalidate_cache('get_repo_cached_%s' % repo_name)
121 123 h.flash(_('Repository %s updated successfully') % repo_name,
122 124 category='success')
123 125 changed_name = form_result['repo_name_full']
124 126 action_logger(self.rhodecode_user, 'user_updated_repo',
125 127 changed_name, self.ip_addr, self.sa)
126 128 Session().commit()
127 129 except formencode.Invalid, errors:
128 130 defaults = self.__load_data(repo_name)
129 131 defaults.update(errors.value)
130 132 return htmlfill.render(
131 133 render('settings/repo_settings.html'),
132 134 defaults=errors.value,
133 135 errors=errors.error_dict or {},
134 136 prefix_error=False,
135 137 encoding="UTF-8")
136 138
137 139 except Exception:
138 140 log.error(traceback.format_exc())
139 141 h.flash(_('error occurred during update of repository %s') \
140 142 % repo_name, category='error')
141 143
142 144 return redirect(url('repo_settings_home', repo_name=changed_name))
143 145
144 146 @HasRepoPermissionAllDecorator('repository.admin')
145 147 def delete(self, repo_name):
146 148 """DELETE /repos/repo_name: Delete an existing item"""
147 149 # Forms posted to this method should contain a hidden field:
148 150 # <input type="hidden" name="_method" value="DELETE" />
149 151 # Or using helpers:
150 152 # h.form(url('repo_settings_delete', repo_name=ID),
151 153 # method='delete')
152 154 # url('repo_settings_delete', repo_name=ID)
153 155
154 156 repo_model = RepoModel()
155 157 repo = repo_model.get_by_repo_name(repo_name)
156 158 if not repo:
157 159 h.not_mapped_error(repo_name)
158 160 return redirect(url('home'))
159 161 try:
160 162 action_logger(self.rhodecode_user, 'user_deleted_repo',
161 163 repo_name, self.ip_addr, self.sa)
162 164 repo_model.delete(repo)
163 165 invalidate_cache('get_repo_cached_%s' % repo_name)
164 166 h.flash(_('deleted repository %s') % repo_name, category='success')
165 167 Session().commit()
166 168 except Exception:
167 169 log.error(traceback.format_exc())
168 170 h.flash(_('An error occurred during deletion of %s') % repo_name,
169 171 category='error')
170 172
171 173 return redirect(url('admin_settings_my_account', anchor='my'))
172 174
173 175 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
174 176 def toggle_locking(self, repo_name):
175 177 """
176 178 Toggle locking of repository by simple GET call to url
177 179
178 180 :param repo_name:
179 181 """
180 182
181 183 try:
182 184 repo = Repository.get_by_repo_name(repo_name)
183 185
184 186 if repo.enable_locking:
185 187 if repo.locked[0]:
186 188 Repository.unlock(repo)
187 189 action = _('unlocked')
188 190 else:
189 191 Repository.lock(repo, c.rhodecode_user.user_id)
190 192 action = _('locked')
191 193
192 194 h.flash(_('Repository has been %s') % action,
193 195 category='success')
194 196 except Exception, e:
195 197 log.error(traceback.format_exc())
196 198 h.flash(_('An error occurred during unlocking'),
197 199 category='error')
198 200 return redirect(url('summary_home', repo_name=repo_name))
@@ -1,401 +1,401 b''
1 1 """ this is forms validation classes
2 2 http://formencode.org/module-formencode.validators.html
3 3 for list off all availible validators
4 4
5 5 we can create our own validators
6 6
7 7 The table below outlines the options which can be used in a schema in addition to the validators themselves
8 8 pre_validators [] These validators will be applied before the schema
9 9 chained_validators [] These validators will be applied after the schema
10 10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
11 11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
12 12 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.
13 13 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
14 14
15 15
16 16 <name> = formencode.validators.<name of validator>
17 17 <name> must equal form name
18 18 list=[1,2,3,4,5]
19 19 for SELECT use formencode.All(OneOf(list), Int())
20 20
21 21 """
22 22 import logging
23 23
24 24 import formencode
25 25 from formencode import All
26 26
27 27 from pylons.i18n.translation import _
28 28
29 29 from rhodecode.model import validators as v
30 30 from rhodecode import BACKENDS
31 31
32 32 log = logging.getLogger(__name__)
33 33
34 34
35 35 class LoginForm(formencode.Schema):
36 36 allow_extra_fields = True
37 37 filter_extra_fields = True
38 38 username = v.UnicodeString(
39 39 strip=True,
40 40 min=1,
41 41 not_empty=True,
42 42 messages={
43 43 'empty': _(u'Please enter a login'),
44 44 'tooShort': _(u'Enter a value %(min)i characters long or more')}
45 45 )
46 46
47 47 password = v.UnicodeString(
48 48 strip=False,
49 49 min=3,
50 50 not_empty=True,
51 51 messages={
52 52 'empty': _(u'Please enter a password'),
53 53 'tooShort': _(u'Enter %(min)i characters or more')}
54 54 )
55 55
56 56 remember = v.StringBoolean(if_missing=False)
57 57
58 58 chained_validators = [v.ValidAuth()]
59 59
60 60
61 61 def UserForm(edit=False, old_data={}):
62 62 class _UserForm(formencode.Schema):
63 63 allow_extra_fields = True
64 64 filter_extra_fields = True
65 65 username = All(v.UnicodeString(strip=True, min=1, not_empty=True),
66 66 v.ValidUsername(edit, old_data))
67 67 if edit:
68 68 new_password = All(
69 69 v.ValidPassword(),
70 70 v.UnicodeString(strip=False, min=6, not_empty=False)
71 71 )
72 72 password_confirmation = All(
73 73 v.ValidPassword(),
74 74 v.UnicodeString(strip=False, min=6, not_empty=False),
75 75 )
76 76 admin = v.StringBoolean(if_missing=False)
77 77 else:
78 78 password = All(
79 79 v.ValidPassword(),
80 80 v.UnicodeString(strip=False, min=6, not_empty=True)
81 81 )
82 82 password_confirmation = All(
83 83 v.ValidPassword(),
84 84 v.UnicodeString(strip=False, min=6, not_empty=False)
85 85 )
86 86
87 87 active = v.StringBoolean(if_missing=False)
88 88 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
89 89 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
90 90 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
91 91
92 92 chained_validators = [v.ValidPasswordsMatch()]
93 93
94 94 return _UserForm
95 95
96 96
97 97 def UserGroupForm(edit=False, old_data={}, available_members=[]):
98 98 class _UserGroupForm(formencode.Schema):
99 99 allow_extra_fields = True
100 100 filter_extra_fields = True
101 101
102 102 users_group_name = All(
103 103 v.UnicodeString(strip=True, min=1, not_empty=True),
104 104 v.ValidUserGroup(edit, old_data)
105 105 )
106 106
107 107 users_group_active = v.StringBoolean(if_missing=False)
108 108
109 109 if edit:
110 110 users_group_members = v.OneOf(
111 111 available_members, hideList=False, testValueList=True,
112 112 if_missing=None, not_empty=False
113 113 )
114 114
115 115 return _UserGroupForm
116 116
117 117
118 118 def ReposGroupForm(edit=False, old_data={}, available_groups=[],
119 119 can_create_in_root=False):
120 120 class _ReposGroupForm(formencode.Schema):
121 121 allow_extra_fields = True
122 122 filter_extra_fields = False
123 123
124 124 group_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
125 125 v.SlugifyName())
126 126 group_description = v.UnicodeString(strip=True, min=1,
127 127 not_empty=False)
128 128 if edit:
129 129 #FIXME: do a special check that we cannot move a group to one of
130 130 #it's children
131 131 pass
132 132 group_parent_id = All(v.CanCreateGroup(can_create_in_root),
133 133 v.OneOf(available_groups, hideList=False,
134 134 testValueList=True,
135 135 if_missing=None, not_empty=True))
136 136 enable_locking = v.StringBoolean(if_missing=False)
137 137 recursive = v.StringBoolean(if_missing=False)
138 138 chained_validators = [v.ValidReposGroup(edit, old_data),
139 139 v.ValidPerms('group')]
140 140
141 141 return _ReposGroupForm
142 142
143 143
144 144 def RegisterForm(edit=False, old_data={}):
145 145 class _RegisterForm(formencode.Schema):
146 146 allow_extra_fields = True
147 147 filter_extra_fields = True
148 148 username = All(
149 149 v.ValidUsername(edit, old_data),
150 150 v.UnicodeString(strip=True, min=1, not_empty=True)
151 151 )
152 152 password = All(
153 153 v.ValidPassword(),
154 154 v.UnicodeString(strip=False, min=6, not_empty=True)
155 155 )
156 156 password_confirmation = All(
157 157 v.ValidPassword(),
158 158 v.UnicodeString(strip=False, min=6, not_empty=True)
159 159 )
160 160 active = v.StringBoolean(if_missing=False)
161 161 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
162 162 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
163 163 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
164 164
165 165 chained_validators = [v.ValidPasswordsMatch()]
166 166
167 167 return _RegisterForm
168 168
169 169
170 170 def PasswordResetForm():
171 171 class _PasswordResetForm(formencode.Schema):
172 172 allow_extra_fields = True
173 173 filter_extra_fields = True
174 174 email = All(v.ValidSystemEmail(), v.Email(not_empty=True))
175 175 return _PasswordResetForm
176 176
177 177
178 178 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
179 179 repo_groups=[], landing_revs=[]):
180 180 class _RepoForm(formencode.Schema):
181 181 allow_extra_fields = True
182 182 filter_extra_fields = False
183 183 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
184 184 v.SlugifyName())
185 repo_group = All(v.CanWriteGroup(),
185 repo_group = All(v.CanWriteGroup(old_data),
186 186 v.OneOf(repo_groups, hideList=True))
187 187 repo_type = v.OneOf(supported_backends)
188 188 repo_description = v.UnicodeString(strip=True, min=1, not_empty=False)
189 189 repo_private = v.StringBoolean(if_missing=False)
190 190 repo_landing_rev = v.OneOf(landing_revs, hideList=True)
191 191 clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False))
192 192
193 193 repo_enable_statistics = v.StringBoolean(if_missing=False)
194 194 repo_enable_downloads = v.StringBoolean(if_missing=False)
195 195 repo_enable_locking = v.StringBoolean(if_missing=False)
196 196
197 197 if edit:
198 198 #this is repo owner
199 199 user = All(v.UnicodeString(not_empty=True), v.ValidRepoUser())
200 200
201 201 chained_validators = [v.ValidCloneUri(),
202 202 v.ValidRepoName(edit, old_data),
203 203 v.ValidPerms()]
204 204 return _RepoForm
205 205
206 206
207 207 def RepoFieldForm():
208 208 class _RepoFieldForm(formencode.Schema):
209 209 filter_extra_fields = True
210 210 allow_extra_fields = True
211 211
212 212 new_field_key = All(v.FieldKey(),
213 213 v.UnicodeString(strip=True, min=3, not_empty=True))
214 214 new_field_value = v.UnicodeString(not_empty=False, if_missing='')
215 215 new_field_type = v.OneOf(['str', 'unicode', 'list', 'tuple'],
216 216 if_missing='str')
217 217 new_field_label = v.UnicodeString(not_empty=False)
218 218 new_field_desc = v.UnicodeString(not_empty=False)
219 219
220 220 return _RepoFieldForm
221 221
222 222
223 223 def RepoSettingsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
224 224 repo_groups=[], landing_revs=[]):
225 225 class _RepoForm(formencode.Schema):
226 226 allow_extra_fields = True
227 227 filter_extra_fields = False
228 228 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
229 229 v.SlugifyName())
230 repo_group = All(v.CanWriteGroup(),
230 repo_group = All(v.CanWriteGroup(old_data),
231 231 v.OneOf(repo_groups, hideList=True))
232 232 repo_description = v.UnicodeString(strip=True, min=1, not_empty=False)
233 233 repo_private = v.StringBoolean(if_missing=False)
234 234 repo_landing_rev = v.OneOf(landing_revs, hideList=True)
235 235 clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False))
236 236
237 237 chained_validators = [v.ValidCloneUri(),
238 238 v.ValidRepoName(edit, old_data),
239 239 v.ValidPerms(),
240 240 v.ValidSettings()]
241 241 return _RepoForm
242 242
243 243
244 244 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
245 245 repo_groups=[], landing_revs=[]):
246 246 class _RepoForkForm(formencode.Schema):
247 247 allow_extra_fields = True
248 248 filter_extra_fields = False
249 249 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
250 250 v.SlugifyName())
251 251 repo_group = All(v.CanWriteGroup(),
252 252 v.OneOf(repo_groups, hideList=True))
253 253 repo_type = All(v.ValidForkType(old_data), v.OneOf(supported_backends))
254 254 description = v.UnicodeString(strip=True, min=1, not_empty=True)
255 255 private = v.StringBoolean(if_missing=False)
256 256 copy_permissions = v.StringBoolean(if_missing=False)
257 257 update_after_clone = v.StringBoolean(if_missing=False)
258 258 fork_parent_id = v.UnicodeString()
259 259 chained_validators = [v.ValidForkName(edit, old_data)]
260 260 landing_rev = v.OneOf(landing_revs, hideList=True)
261 261
262 262 return _RepoForkForm
263 263
264 264
265 265 def ApplicationSettingsForm():
266 266 class _ApplicationSettingsForm(formencode.Schema):
267 267 allow_extra_fields = True
268 268 filter_extra_fields = False
269 269 rhodecode_title = v.UnicodeString(strip=True, min=1, not_empty=True)
270 270 rhodecode_realm = v.UnicodeString(strip=True, min=1, not_empty=True)
271 271 rhodecode_ga_code = v.UnicodeString(strip=True, min=1, not_empty=False)
272 272
273 273 return _ApplicationSettingsForm
274 274
275 275
276 276 def ApplicationVisualisationForm():
277 277 class _ApplicationVisualisationForm(formencode.Schema):
278 278 allow_extra_fields = True
279 279 filter_extra_fields = False
280 280 rhodecode_show_public_icon = v.StringBoolean(if_missing=False)
281 281 rhodecode_show_private_icon = v.StringBoolean(if_missing=False)
282 282 rhodecode_stylify_metatags = v.StringBoolean(if_missing=False)
283 283
284 284 rhodecode_lightweight_dashboard = v.StringBoolean(if_missing=False)
285 285 rhodecode_repository_fields = v.StringBoolean(if_missing=False)
286 286 rhodecode_lightweight_journal = v.StringBoolean(if_missing=False)
287 287
288 288 return _ApplicationVisualisationForm
289 289
290 290
291 291 def ApplicationUiSettingsForm():
292 292 class _ApplicationUiSettingsForm(formencode.Schema):
293 293 allow_extra_fields = True
294 294 filter_extra_fields = False
295 295 web_push_ssl = v.StringBoolean(if_missing=False)
296 296 paths_root_path = All(
297 297 v.ValidPath(),
298 298 v.UnicodeString(strip=True, min=1, not_empty=True)
299 299 )
300 300 hooks_changegroup_update = v.StringBoolean(if_missing=False)
301 301 hooks_changegroup_repo_size = v.StringBoolean(if_missing=False)
302 302 hooks_changegroup_push_logger = v.StringBoolean(if_missing=False)
303 303 hooks_outgoing_pull_logger = v.StringBoolean(if_missing=False)
304 304
305 305 extensions_largefiles = v.StringBoolean(if_missing=False)
306 306 extensions_hgsubversion = v.StringBoolean(if_missing=False)
307 307 extensions_hggit = v.StringBoolean(if_missing=False)
308 308
309 309 return _ApplicationUiSettingsForm
310 310
311 311
312 312 def DefaultPermissionsForm(repo_perms_choices, group_perms_choices,
313 313 register_choices, create_choices, fork_choices):
314 314 class _DefaultPermissionsForm(formencode.Schema):
315 315 allow_extra_fields = True
316 316 filter_extra_fields = True
317 317 overwrite_default_repo = v.StringBoolean(if_missing=False)
318 318 overwrite_default_group = v.StringBoolean(if_missing=False)
319 319 anonymous = v.StringBoolean(if_missing=False)
320 320 default_repo_perm = v.OneOf(repo_perms_choices)
321 321 default_group_perm = v.OneOf(group_perms_choices)
322 322 default_register = v.OneOf(register_choices)
323 323 default_create = v.OneOf(create_choices)
324 324 default_fork = v.OneOf(fork_choices)
325 325
326 326 return _DefaultPermissionsForm
327 327
328 328
329 329 def DefaultsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
330 330 class _DefaultsForm(formencode.Schema):
331 331 allow_extra_fields = True
332 332 filter_extra_fields = True
333 333 default_repo_type = v.OneOf(supported_backends)
334 334 default_repo_private = v.StringBoolean(if_missing=False)
335 335 default_repo_enable_statistics = v.StringBoolean(if_missing=False)
336 336 default_repo_enable_downloads = v.StringBoolean(if_missing=False)
337 337 default_repo_enable_locking = v.StringBoolean(if_missing=False)
338 338
339 339 return _DefaultsForm
340 340
341 341
342 342 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices,
343 343 tls_kind_choices):
344 344 class _LdapSettingsForm(formencode.Schema):
345 345 allow_extra_fields = True
346 346 filter_extra_fields = True
347 347 #pre_validators = [LdapLibValidator]
348 348 ldap_active = v.StringBoolean(if_missing=False)
349 349 ldap_host = v.UnicodeString(strip=True,)
350 350 ldap_port = v.Number(strip=True,)
351 351 ldap_tls_kind = v.OneOf(tls_kind_choices)
352 352 ldap_tls_reqcert = v.OneOf(tls_reqcert_choices)
353 353 ldap_dn_user = v.UnicodeString(strip=True,)
354 354 ldap_dn_pass = v.UnicodeString(strip=True,)
355 355 ldap_base_dn = v.UnicodeString(strip=True,)
356 356 ldap_filter = v.UnicodeString(strip=True,)
357 357 ldap_search_scope = v.OneOf(search_scope_choices)
358 358 ldap_attr_login = All(
359 359 v.AttrLoginValidator(),
360 360 v.UnicodeString(strip=True,)
361 361 )
362 362 ldap_attr_firstname = v.UnicodeString(strip=True,)
363 363 ldap_attr_lastname = v.UnicodeString(strip=True,)
364 364 ldap_attr_email = v.UnicodeString(strip=True,)
365 365
366 366 return _LdapSettingsForm
367 367
368 368
369 369 def UserExtraEmailForm():
370 370 class _UserExtraEmailForm(formencode.Schema):
371 371 email = All(v.UniqSystemEmail(), v.Email(not_empty=True))
372 372 return _UserExtraEmailForm
373 373
374 374
375 375 def UserExtraIpForm():
376 376 class _UserExtraIpForm(formencode.Schema):
377 377 ip = v.ValidIp()(not_empty=True)
378 378 return _UserExtraIpForm
379 379
380 380
381 381 def PullRequestForm(repo_id):
382 382 class _PullRequestForm(formencode.Schema):
383 383 allow_extra_fields = True
384 384 filter_extra_fields = True
385 385
386 386 user = v.UnicodeString(strip=True, required=True)
387 387 org_repo = v.UnicodeString(strip=True, required=True)
388 388 org_ref = v.UnicodeString(strip=True, required=True)
389 389 other_repo = v.UnicodeString(strip=True, required=True)
390 390 other_ref = v.UnicodeString(strip=True, required=True)
391 391 revisions = All(#v.NotReviewedRevisions(repo_id)(),
392 392 v.UniqueList(not_empty=True))
393 393 review_members = v.UniqueList(not_empty=True)
394 394
395 395 pullrequest_title = v.UnicodeString(strip=True, required=True, min=3)
396 396 pullrequest_desc = v.UnicodeString(strip=True, required=False)
397 397
398 398 ancestor_rev = v.UnicodeString(strip=True, required=True)
399 399 merge_rev = v.UnicodeString(strip=True, required=True)
400 400
401 401 return _PullRequestForm
@@ -1,813 +1,816 b''
1 1 """
2 2 Set of generic validators
3 3 """
4 4 import os
5 5 import re
6 6 import formencode
7 7 import logging
8 8 from collections import defaultdict
9 9 from pylons.i18n.translation import _
10 10 from webhelpers.pylonslib.secure_form import authentication_token
11 11
12 12 from formencode.validators import (
13 13 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set,
14 14 NotEmpty, IPAddress, CIDR
15 15 )
16 16 from rhodecode.lib.compat import OrderedSet
17 17 from rhodecode.lib import ipaddr
18 18 from rhodecode.lib.utils import repo_name_slug
19 from rhodecode.lib.utils2 import safe_int
19 20 from rhodecode.model.db import RepoGroup, Repository, UserGroup, User,\
20 21 ChangesetStatus
21 22 from rhodecode.lib.exceptions import LdapImportError
22 23 from rhodecode.config.routing import ADMIN_PREFIX
23 24 from rhodecode.lib.auth import HasReposGroupPermissionAny, HasPermissionAny
24 25
25 26 # silence warnings and pylint
26 27 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set, \
27 28 NotEmpty, IPAddress, CIDR
28 29
29 30 log = logging.getLogger(__name__)
30 31
31 32
32 33 class UniqueList(formencode.FancyValidator):
33 34 """
34 35 Unique List !
35 36 """
36 37 messages = dict(
37 38 empty=_('Value cannot be an empty list'),
38 39 missing_value=_('Value cannot be an empty list'),
39 40 )
40 41
41 42 def _to_python(self, value, state):
42 43 if isinstance(value, list):
43 44 return value
44 45 elif isinstance(value, set):
45 46 return list(value)
46 47 elif isinstance(value, tuple):
47 48 return list(value)
48 49 elif value is None:
49 50 return []
50 51 else:
51 52 return [value]
52 53
53 54 def empty_value(self, value):
54 55 return []
55 56
56 57
57 58 class StateObj(object):
58 59 """
59 60 this is needed to translate the messages using _() in validators
60 61 """
61 62 _ = staticmethod(_)
62 63
63 64
64 65 def M(self, key, state=None, **kwargs):
65 66 """
66 67 returns string from self.message based on given key,
67 68 passed kw params are used to substitute %(named)s params inside
68 69 translated strings
69 70
70 71 :param msg:
71 72 :param state:
72 73 """
73 74 if state is None:
74 75 state = StateObj()
75 76 else:
76 77 state._ = staticmethod(_)
77 78 #inject validator into state object
78 79 return self.message(key, state, **kwargs)
79 80
80 81
81 82 def ValidUsername(edit=False, old_data={}):
82 83 class _validator(formencode.validators.FancyValidator):
83 84 messages = {
84 85 'username_exists': _(u'Username "%(username)s" already exists'),
85 86 'system_invalid_username':
86 87 _(u'Username "%(username)s" is forbidden'),
87 88 'invalid_username':
88 89 _(u'Username may only contain alphanumeric characters '
89 90 'underscores, periods or dashes and must begin with '
90 91 'alphanumeric character')
91 92 }
92 93
93 94 def validate_python(self, value, state):
94 95 if value in ['default', 'new_user']:
95 96 msg = M(self, 'system_invalid_username', state, username=value)
96 97 raise formencode.Invalid(msg, value, state)
97 98 #check if user is unique
98 99 old_un = None
99 100 if edit:
100 101 old_un = User.get(old_data.get('user_id')).username
101 102
102 103 if old_un != value or not edit:
103 104 if User.get_by_username(value, case_insensitive=True):
104 105 msg = M(self, 'username_exists', state, username=value)
105 106 raise formencode.Invalid(msg, value, state)
106 107
107 108 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]*$', value) is None:
108 109 msg = M(self, 'invalid_username', state)
109 110 raise formencode.Invalid(msg, value, state)
110 111 return _validator
111 112
112 113
113 114 def ValidRepoUser():
114 115 class _validator(formencode.validators.FancyValidator):
115 116 messages = {
116 117 'invalid_username': _(u'Username %(username)s is not valid')
117 118 }
118 119
119 120 def validate_python(self, value, state):
120 121 try:
121 122 User.query().filter(User.active == True)\
122 123 .filter(User.username == value).one()
123 124 except Exception:
124 125 msg = M(self, 'invalid_username', state, username=value)
125 126 raise formencode.Invalid(msg, value, state,
126 127 error_dict=dict(username=msg)
127 128 )
128 129
129 130 return _validator
130 131
131 132
132 133 def ValidUserGroup(edit=False, old_data={}):
133 134 class _validator(formencode.validators.FancyValidator):
134 135 messages = {
135 136 'invalid_group': _(u'Invalid user group name'),
136 137 'group_exist': _(u'User group "%(usergroup)s" already exists'),
137 138 'invalid_usergroup_name':
138 139 _(u'user group name may only contain alphanumeric '
139 140 'characters underscores, periods or dashes and must begin '
140 141 'with alphanumeric character')
141 142 }
142 143
143 144 def validate_python(self, value, state):
144 145 if value in ['default']:
145 146 msg = M(self, 'invalid_group', state)
146 147 raise formencode.Invalid(msg, value, state,
147 148 error_dict=dict(users_group_name=msg)
148 149 )
149 150 #check if group is unique
150 151 old_ugname = None
151 152 if edit:
152 153 old_id = old_data.get('users_group_id')
153 154 old_ugname = UserGroup.get(old_id).users_group_name
154 155
155 156 if old_ugname != value or not edit:
156 157 is_existing_group = UserGroup.get_by_group_name(value,
157 158 case_insensitive=True)
158 159 if is_existing_group:
159 160 msg = M(self, 'group_exist', state, usergroup=value)
160 161 raise formencode.Invalid(msg, value, state,
161 162 error_dict=dict(users_group_name=msg)
162 163 )
163 164
164 165 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
165 166 msg = M(self, 'invalid_usergroup_name', state)
166 167 raise formencode.Invalid(msg, value, state,
167 168 error_dict=dict(users_group_name=msg)
168 169 )
169 170
170 171 return _validator
171 172
172 173
173 174 def ValidReposGroup(edit=False, old_data={}):
174 175 class _validator(formencode.validators.FancyValidator):
175 176 messages = {
176 177 'group_parent_id': _(u'Cannot assign this group as parent'),
177 178 'group_exists': _(u'Group "%(group_name)s" already exists'),
178 179 'repo_exists':
179 180 _(u'Repository with name "%(group_name)s" already exists')
180 181 }
181 182
182 183 def validate_python(self, value, state):
183 184 # TODO WRITE VALIDATIONS
184 185 group_name = value.get('group_name')
185 186 group_parent_id = value.get('group_parent_id')
186 187
187 188 # slugify repo group just in case :)
188 189 slug = repo_name_slug(group_name)
189 190
190 191 # check for parent of self
191 192 parent_of_self = lambda: (
192 193 old_data['group_id'] == int(group_parent_id)
193 194 if group_parent_id else False
194 195 )
195 196 if edit and parent_of_self():
196 197 msg = M(self, 'group_parent_id', state)
197 198 raise formencode.Invalid(msg, value, state,
198 199 error_dict=dict(group_parent_id=msg)
199 200 )
200 201
201 202 old_gname = None
202 203 if edit:
203 204 old_gname = RepoGroup.get(old_data.get('group_id')).group_name
204 205
205 206 if old_gname != group_name or not edit:
206 207
207 208 # check group
208 209 gr = RepoGroup.query()\
209 210 .filter(RepoGroup.group_name == slug)\
210 211 .filter(RepoGroup.group_parent_id == group_parent_id)\
211 212 .scalar()
212 213
213 214 if gr:
214 215 msg = M(self, 'group_exists', state, group_name=slug)
215 216 raise formencode.Invalid(msg, value, state,
216 217 error_dict=dict(group_name=msg)
217 218 )
218 219
219 220 # check for same repo
220 221 repo = Repository.query()\
221 222 .filter(Repository.repo_name == slug)\
222 223 .scalar()
223 224
224 225 if repo:
225 226 msg = M(self, 'repo_exists', state, group_name=slug)
226 227 raise formencode.Invalid(msg, value, state,
227 228 error_dict=dict(group_name=msg)
228 229 )
229 230
230 231 return _validator
231 232
232 233
233 234 def ValidPassword():
234 235 class _validator(formencode.validators.FancyValidator):
235 236 messages = {
236 237 'invalid_password':
237 238 _(u'Invalid characters (non-ascii) in password')
238 239 }
239 240
240 241 def validate_python(self, value, state):
241 242 try:
242 243 (value or '').decode('ascii')
243 244 except UnicodeError:
244 245 msg = M(self, 'invalid_password', state)
245 246 raise formencode.Invalid(msg, value, state,)
246 247 return _validator
247 248
248 249
249 250 def ValidPasswordsMatch():
250 251 class _validator(formencode.validators.FancyValidator):
251 252 messages = {
252 253 'password_mismatch': _(u'Passwords do not match'),
253 254 }
254 255
255 256 def validate_python(self, value, state):
256 257
257 258 pass_val = value.get('password') or value.get('new_password')
258 259 if pass_val != value['password_confirmation']:
259 260 msg = M(self, 'password_mismatch', state)
260 261 raise formencode.Invalid(msg, value, state,
261 262 error_dict=dict(password_confirmation=msg)
262 263 )
263 264 return _validator
264 265
265 266
266 267 def ValidAuth():
267 268 class _validator(formencode.validators.FancyValidator):
268 269 messages = {
269 270 'invalid_password': _(u'invalid password'),
270 271 'invalid_username': _(u'invalid user name'),
271 272 'disabled_account': _(u'Your account is disabled')
272 273 }
273 274
274 275 def validate_python(self, value, state):
275 276 from rhodecode.lib.auth import authenticate
276 277
277 278 password = value['password']
278 279 username = value['username']
279 280
280 281 if not authenticate(username, password):
281 282 user = User.get_by_username(username)
282 283 if user and user.active is False:
283 284 log.warning('user %s is disabled' % username)
284 285 msg = M(self, 'disabled_account', state)
285 286 raise formencode.Invalid(msg, value, state,
286 287 error_dict=dict(username=msg)
287 288 )
288 289 else:
289 290 log.warning('user %s failed to authenticate' % username)
290 291 msg = M(self, 'invalid_username', state)
291 292 msg2 = M(self, 'invalid_password', state)
292 293 raise formencode.Invalid(msg, value, state,
293 294 error_dict=dict(username=msg, password=msg2)
294 295 )
295 296 return _validator
296 297
297 298
298 299 def ValidAuthToken():
299 300 class _validator(formencode.validators.FancyValidator):
300 301 messages = {
301 302 'invalid_token': _(u'Token mismatch')
302 303 }
303 304
304 305 def validate_python(self, value, state):
305 306 if value != authentication_token():
306 307 msg = M(self, 'invalid_token', state)
307 308 raise formencode.Invalid(msg, value, state)
308 309 return _validator
309 310
310 311
311 312 def ValidRepoName(edit=False, old_data={}):
312 313 class _validator(formencode.validators.FancyValidator):
313 314 messages = {
314 315 'invalid_repo_name':
315 316 _(u'Repository name %(repo)s is disallowed'),
316 317 'repository_exists':
317 318 _(u'Repository named %(repo)s already exists'),
318 319 'repository_in_group_exists': _(u'Repository "%(repo)s" already '
319 320 'exists in group "%(group)s"'),
320 321 'same_group_exists': _(u'Repository group with name "%(repo)s" '
321 322 'already exists')
322 323 }
323 324
324 325 def _to_python(self, value, state):
325 326 repo_name = repo_name_slug(value.get('repo_name', ''))
326 327 repo_group = value.get('repo_group')
327 328 if repo_group:
328 329 gr = RepoGroup.get(repo_group)
329 330 group_path = gr.full_path
330 331 group_name = gr.group_name
331 332 # value needs to be aware of group name in order to check
332 333 # db key This is an actual just the name to store in the
333 334 # database
334 335 repo_name_full = group_path + RepoGroup.url_sep() + repo_name
335 336 else:
336 337 group_name = group_path = ''
337 338 repo_name_full = repo_name
338 339
339 340 value['repo_name'] = repo_name
340 341 value['repo_name_full'] = repo_name_full
341 342 value['group_path'] = group_path
342 343 value['group_name'] = group_name
343 344 return value
344 345
345 346 def validate_python(self, value, state):
346 347
347 348 repo_name = value.get('repo_name')
348 349 repo_name_full = value.get('repo_name_full')
349 350 group_path = value.get('group_path')
350 351 group_name = value.get('group_name')
351 352
352 353 if repo_name in [ADMIN_PREFIX, '']:
353 354 msg = M(self, 'invalid_repo_name', state, repo=repo_name)
354 355 raise formencode.Invalid(msg, value, state,
355 356 error_dict=dict(repo_name=msg)
356 357 )
357 358
358 359 rename = old_data.get('repo_name') != repo_name_full
359 360 create = not edit
360 361 if rename or create:
361 362
362 363 if group_path != '':
363 364 if Repository.get_by_repo_name(repo_name_full):
364 365 msg = M(self, 'repository_in_group_exists', state,
365 366 repo=repo_name, group=group_name)
366 367 raise formencode.Invalid(msg, value, state,
367 368 error_dict=dict(repo_name=msg)
368 369 )
369 370 elif RepoGroup.get_by_group_name(repo_name_full):
370 371 msg = M(self, 'same_group_exists', state,
371 372 repo=repo_name)
372 373 raise formencode.Invalid(msg, value, state,
373 374 error_dict=dict(repo_name=msg)
374 375 )
375 376
376 377 elif Repository.get_by_repo_name(repo_name_full):
377 378 msg = M(self, 'repository_exists', state,
378 379 repo=repo_name)
379 380 raise formencode.Invalid(msg, value, state,
380 381 error_dict=dict(repo_name=msg)
381 382 )
382 383 return value
383 384 return _validator
384 385
385 386
386 387 def ValidForkName(*args, **kwargs):
387 388 return ValidRepoName(*args, **kwargs)
388 389
389 390
390 391 def SlugifyName():
391 392 class _validator(formencode.validators.FancyValidator):
392 393
393 394 def _to_python(self, value, state):
394 395 return repo_name_slug(value)
395 396
396 397 def validate_python(self, value, state):
397 398 pass
398 399
399 400 return _validator
400 401
401 402
402 403 def ValidCloneUri():
403 404 from rhodecode.lib.utils import make_ui
404 405
405 406 def url_handler(repo_type, url, ui=None):
406 407 if repo_type == 'hg':
407 408 from rhodecode.lib.vcs.backends.hg.repository import MercurialRepository
408 409 from mercurial.httppeer import httppeer
409 410 if url.startswith('http'):
410 411 ## initially check if it's at least the proper URL
411 412 ## or does it pass basic auth
412 413 MercurialRepository._check_url(url)
413 414 httppeer(ui, url)._capabilities()
414 415 elif url.startswith('svn+http'):
415 416 from hgsubversion.svnrepo import svnremoterepo
416 417 svnremoterepo(ui, url).capabilities
417 418 elif url.startswith('git+http'):
418 419 raise NotImplementedError()
419 420 else:
420 421 raise Exception('clone from URI %s not allowed' % (url))
421 422
422 423 elif repo_type == 'git':
423 424 from rhodecode.lib.vcs.backends.git.repository import GitRepository
424 425 if url.startswith('http'):
425 426 ## initially check if it's at least the proper URL
426 427 ## or does it pass basic auth
427 428 GitRepository._check_url(url)
428 429 elif url.startswith('svn+http'):
429 430 raise NotImplementedError()
430 431 elif url.startswith('hg+http'):
431 432 raise NotImplementedError()
432 433 else:
433 434 raise Exception('clone from URI %s not allowed' % (url))
434 435
435 436 class _validator(formencode.validators.FancyValidator):
436 437 messages = {
437 438 'clone_uri': _(u'invalid clone url'),
438 439 'invalid_clone_uri': _(u'Invalid clone url, provide a '
439 440 'valid clone http(s)/svn+http(s) url')
440 441 }
441 442
442 443 def validate_python(self, value, state):
443 444 repo_type = value.get('repo_type')
444 445 url = value.get('clone_uri')
445 446
446 447 if not url:
447 448 pass
448 449 else:
449 450 try:
450 451 url_handler(repo_type, url, make_ui('db', clear_session=False))
451 452 except Exception:
452 453 log.exception('Url validation failed')
453 454 msg = M(self, 'clone_uri')
454 455 raise formencode.Invalid(msg, value, state,
455 456 error_dict=dict(clone_uri=msg)
456 457 )
457 458 return _validator
458 459
459 460
460 461 def ValidForkType(old_data={}):
461 462 class _validator(formencode.validators.FancyValidator):
462 463 messages = {
463 464 'invalid_fork_type': _(u'Fork have to be the same type as parent')
464 465 }
465 466
466 467 def validate_python(self, value, state):
467 468 if old_data['repo_type'] != value:
468 469 msg = M(self, 'invalid_fork_type', state)
469 470 raise formencode.Invalid(msg, value, state,
470 471 error_dict=dict(repo_type=msg)
471 472 )
472 473 return _validator
473 474
474 475
475 def CanWriteGroup():
476 def CanWriteGroup(old_data=None):
476 477 class _validator(formencode.validators.FancyValidator):
477 478 messages = {
478 479 'permission_denied': _(u"You don't have permissions "
479 480 "to create repository in this group"),
480 481 'permission_denied_root': _(u"no permission to create repository "
481 482 "in root location")
482 483 }
483 484
484 485 def _to_python(self, value, state):
485 486 #root location
486 487 if value in [-1, "-1"]:
487 488 return None
488 489 return value
489 490
490 491 def validate_python(self, value, state):
491 492 gr = RepoGroup.get(value)
492 493 gr_name = gr.group_name if gr else None # None means ROOT location
493 494 val = HasReposGroupPermissionAny('group.write', 'group.admin')
494 495 can_create_repos = HasPermissionAny('hg.admin', 'hg.create.repository')
495 496 forbidden = not val(gr_name, 'can write into group validator')
497 value_changed = True # old_data['repo_group'].get('group_id') != safe_int(value)
498 if value_changed: # do check if we changed the value
496 499 #parent group need to be existing
497 500 if gr and forbidden:
498 501 msg = M(self, 'permission_denied', state)
499 502 raise formencode.Invalid(msg, value, state,
500 503 error_dict=dict(repo_type=msg)
501 504 )
502 505 ## check if we can write to root location !
503 506 elif gr is None and can_create_repos() is False:
504 507 msg = M(self, 'permission_denied_root', state)
505 508 raise formencode.Invalid(msg, value, state,
506 509 error_dict=dict(repo_type=msg)
507 510 )
508 511
509 512 return _validator
510 513
511 514
512 515 def CanCreateGroup(can_create_in_root=False):
513 516 class _validator(formencode.validators.FancyValidator):
514 517 messages = {
515 518 'permission_denied': _(u"You don't have permissions "
516 519 "to create a group in this location")
517 520 }
518 521
519 522 def to_python(self, value, state):
520 523 #root location
521 524 if value in [-1, "-1"]:
522 525 return None
523 526 return value
524 527
525 528 def validate_python(self, value, state):
526 529 gr = RepoGroup.get(value)
527 530 gr_name = gr.group_name if gr else None # None means ROOT location
528 531
529 532 if can_create_in_root and gr is None:
530 533 #we can create in root, we're fine no validations required
531 534 return
532 535
533 536 forbidden_in_root = gr is None and can_create_in_root is False
534 537 val = HasReposGroupPermissionAny('group.admin')
535 538 forbidden = not val(gr_name, 'can create group validator')
536 539 if forbidden_in_root or forbidden:
537 540 msg = M(self, 'permission_denied', state)
538 541 raise formencode.Invalid(msg, value, state,
539 542 error_dict=dict(group_parent_id=msg)
540 543 )
541 544
542 545 return _validator
543 546
544 547
545 548 def ValidPerms(type_='repo'):
546 549 if type_ == 'group':
547 550 EMPTY_PERM = 'group.none'
548 551 elif type_ == 'repo':
549 552 EMPTY_PERM = 'repository.none'
550 553
551 554 class _validator(formencode.validators.FancyValidator):
552 555 messages = {
553 556 'perm_new_member_name':
554 557 _(u'This username or user group name is not valid')
555 558 }
556 559
557 560 def to_python(self, value, state):
558 561 perms_update = OrderedSet()
559 562 perms_new = OrderedSet()
560 563 # build a list of permission to update and new permission to create
561 564
562 565 #CLEAN OUT ORG VALUE FROM NEW MEMBERS, and group them using
563 566 new_perms_group = defaultdict(dict)
564 567 for k, v in value.copy().iteritems():
565 568 if k.startswith('perm_new_member'):
566 569 del value[k]
567 570 _type, part = k.split('perm_new_member_')
568 571 args = part.split('_')
569 572 if len(args) == 1:
570 573 new_perms_group[args[0]]['perm'] = v
571 574 elif len(args) == 2:
572 575 _key, pos = args
573 576 new_perms_group[pos][_key] = v
574 577
575 578 # fill new permissions in order of how they were added
576 579 for k in sorted(map(int, new_perms_group.keys())):
577 580 perm_dict = new_perms_group[str(k)]
578 581 new_member = perm_dict.get('name')
579 582 new_perm = perm_dict.get('perm')
580 583 new_type = perm_dict.get('type')
581 584 if new_member and new_perm and new_type:
582 585 perms_new.add((new_member, new_perm, new_type))
583 586
584 587 for k, v in value.iteritems():
585 588 if k.startswith('u_perm_') or k.startswith('g_perm_'):
586 589 member = k[7:]
587 590 t = {'u': 'user',
588 591 'g': 'users_group'
589 592 }[k[0]]
590 593 if member == 'default':
591 594 if value.get('repo_private'):
592 595 # set none for default when updating to
593 596 # private repo
594 597 v = EMPTY_PERM
595 598 perms_update.add((member, v, t))
596 599 #always set NONE when private flag is set
597 600 if value.get('repo_private'):
598 601 perms_update.add(('default', EMPTY_PERM, 'user'))
599 602
600 603 value['perms_updates'] = list(perms_update)
601 604 value['perms_new'] = list(perms_new)
602 605
603 606 # update permissions
604 607 for k, v, t in perms_new:
605 608 try:
606 609 if t is 'user':
607 610 self.user_db = User.query()\
608 611 .filter(User.active == True)\
609 612 .filter(User.username == k).one()
610 613 if t is 'users_group':
611 614 self.user_db = UserGroup.query()\
612 615 .filter(UserGroup.users_group_active == True)\
613 616 .filter(UserGroup.users_group_name == k).one()
614 617
615 618 except Exception:
616 619 log.exception('Updated permission failed')
617 620 msg = M(self, 'perm_new_member_type', state)
618 621 raise formencode.Invalid(msg, value, state,
619 622 error_dict=dict(perm_new_member_name=msg)
620 623 )
621 624 return value
622 625 return _validator
623 626
624 627
625 628 def ValidSettings():
626 629 class _validator(formencode.validators.FancyValidator):
627 630 def _to_python(self, value, state):
628 631 # settings form for users that are not admin
629 632 # can't edit certain parameters, it's extra backup if they mangle
630 633 # with forms
631 634
632 635 forbidden_params = [
633 636 'user', 'repo_type', 'repo_enable_locking',
634 637 'repo_enable_downloads', 'repo_enable_statistics'
635 638 ]
636 639
637 640 for param in forbidden_params:
638 641 if param in value:
639 642 del value[param]
640 643 return value
641 644
642 645 def validate_python(self, value, state):
643 646 pass
644 647 return _validator
645 648
646 649
647 650 def ValidPath():
648 651 class _validator(formencode.validators.FancyValidator):
649 652 messages = {
650 653 'invalid_path': _(u'This is not a valid path')
651 654 }
652 655
653 656 def validate_python(self, value, state):
654 657 if not os.path.isdir(value):
655 658 msg = M(self, 'invalid_path', state)
656 659 raise formencode.Invalid(msg, value, state,
657 660 error_dict=dict(paths_root_path=msg)
658 661 )
659 662 return _validator
660 663
661 664
662 665 def UniqSystemEmail(old_data={}):
663 666 class _validator(formencode.validators.FancyValidator):
664 667 messages = {
665 668 'email_taken': _(u'This e-mail address is already taken')
666 669 }
667 670
668 671 def _to_python(self, value, state):
669 672 return value.lower()
670 673
671 674 def validate_python(self, value, state):
672 675 if (old_data.get('email') or '').lower() != value:
673 676 user = User.get_by_email(value, case_insensitive=True)
674 677 if user:
675 678 msg = M(self, 'email_taken', state)
676 679 raise formencode.Invalid(msg, value, state,
677 680 error_dict=dict(email=msg)
678 681 )
679 682 return _validator
680 683
681 684
682 685 def ValidSystemEmail():
683 686 class _validator(formencode.validators.FancyValidator):
684 687 messages = {
685 688 'non_existing_email': _(u'e-mail "%(email)s" does not exist.')
686 689 }
687 690
688 691 def _to_python(self, value, state):
689 692 return value.lower()
690 693
691 694 def validate_python(self, value, state):
692 695 user = User.get_by_email(value, case_insensitive=True)
693 696 if user is None:
694 697 msg = M(self, 'non_existing_email', state, email=value)
695 698 raise formencode.Invalid(msg, value, state,
696 699 error_dict=dict(email=msg)
697 700 )
698 701
699 702 return _validator
700 703
701 704
702 705 def LdapLibValidator():
703 706 class _validator(formencode.validators.FancyValidator):
704 707 messages = {
705 708
706 709 }
707 710
708 711 def validate_python(self, value, state):
709 712 try:
710 713 import ldap
711 714 ldap # pyflakes silence !
712 715 except ImportError:
713 716 raise LdapImportError()
714 717
715 718 return _validator
716 719
717 720
718 721 def AttrLoginValidator():
719 722 class _validator(formencode.validators.FancyValidator):
720 723 messages = {
721 724 'invalid_cn':
722 725 _(u'The LDAP Login attribute of the CN must be specified - '
723 726 'this is the name of the attribute that is equivalent '
724 727 'to "username"')
725 728 }
726 729
727 730 def validate_python(self, value, state):
728 731 if not value or not isinstance(value, (str, unicode)):
729 732 msg = M(self, 'invalid_cn', state)
730 733 raise formencode.Invalid(msg, value, state,
731 734 error_dict=dict(ldap_attr_login=msg)
732 735 )
733 736
734 737 return _validator
735 738
736 739
737 740 def NotReviewedRevisions(repo_id):
738 741 class _validator(formencode.validators.FancyValidator):
739 742 messages = {
740 743 'rev_already_reviewed':
741 744 _(u'Revisions %(revs)s are already part of pull request '
742 745 'or have set status')
743 746 }
744 747
745 748 def validate_python(self, value, state):
746 749 # check revisions if they are not reviewed, or a part of another
747 750 # pull request
748 751 statuses = ChangesetStatus.query()\
749 752 .filter(ChangesetStatus.revision.in_(value))\
750 753 .filter(ChangesetStatus.repo_id == repo_id)\
751 754 .all()
752 755
753 756 errors = []
754 757 for cs in statuses:
755 758 if cs.pull_request_id:
756 759 errors.append(['pull_req', cs.revision[:12]])
757 760 elif cs.status:
758 761 errors.append(['status', cs.revision[:12]])
759 762
760 763 if errors:
761 764 revs = ','.join([x[1] for x in errors])
762 765 msg = M(self, 'rev_already_reviewed', state, revs=revs)
763 766 raise formencode.Invalid(msg, value, state,
764 767 error_dict=dict(revisions=revs)
765 768 )
766 769
767 770 return _validator
768 771
769 772
770 773 def ValidIp():
771 774 class _validator(CIDR):
772 775 messages = dict(
773 776 badFormat=_('Please enter a valid IPv4 or IpV6 address'),
774 777 illegalBits=_('The network size (bits) must be within the range'
775 778 ' of 0-32 (not %(bits)r)'))
776 779
777 780 def to_python(self, value, state):
778 781 v = super(_validator, self).to_python(value, state)
779 782 v = v.strip()
780 783 net = ipaddr.IPNetwork(address=v)
781 784 if isinstance(net, ipaddr.IPv4Network):
782 785 #if IPv4 doesn't end with a mask, add /32
783 786 if '/' not in value:
784 787 v += '/32'
785 788 if isinstance(net, ipaddr.IPv6Network):
786 789 #if IPv6 doesn't end with a mask, add /128
787 790 if '/' not in value:
788 791 v += '/128'
789 792 return v
790 793
791 794 def validate_python(self, value, state):
792 795 try:
793 796 addr = value.strip()
794 797 #this raises an ValueError if address is not IpV4 or IpV6
795 798 ipaddr.IPNetwork(address=addr)
796 799 except ValueError:
797 800 raise formencode.Invalid(self.message('badFormat', state),
798 801 value, state)
799 802
800 803 return _validator
801 804
802 805
803 806 def FieldKey():
804 807 class _validator(formencode.validators.FancyValidator):
805 808 messages = dict(
806 809 badFormat=_('Key name can only consist of letters, '
807 810 'underscore, dash or numbers'),)
808 811
809 812 def validate_python(self, value, state):
810 813 if not re.match('[a-zA-Z0-9_-]+$', value):
811 814 raise formencode.Invalid(self.message('badFormat', state),
812 815 value, state)
813 816 return _validator
General Comments 0
You need to be logged in to leave comments. Login now