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