##// END OF EJS Templates
added more validations when opening pull request...
marcink -
r2711:1de45f58 beta
parent child Browse files
Show More
@@ -1,356 +1,378 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.pullrequests
3 rhodecode.controllers.pullrequests
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 pull requests controller for rhodecode for initializing pull requests
6 pull requests controller for rhodecode for initializing pull requests
7
7
8 :created_on: May 7, 2012
8 :created_on: May 7, 2012
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 import logging
25 import logging
26 import traceback
26 import traceback
27 import formencode
27
28
28 from webob.exc import HTTPNotFound, HTTPForbidden
29 from webob.exc import HTTPNotFound, HTTPForbidden
29 from collections import defaultdict
30 from collections import defaultdict
30 from itertools import groupby
31 from itertools import groupby
31
32
32 from pylons import request, response, session, tmpl_context as c, url
33 from pylons import request, response, session, tmpl_context as c, url
33 from pylons.controllers.util import abort, redirect
34 from pylons.controllers.util import abort, redirect
34 from pylons.i18n.translation import _
35 from pylons.i18n.translation import _
35 from pylons.decorators import jsonify
36 from pylons.decorators import jsonify
36
37
37 from rhodecode.lib.compat import json
38 from rhodecode.lib.compat import json
38 from rhodecode.lib.base import BaseRepoController, render
39 from rhodecode.lib.base import BaseRepoController, render
39 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator,\
40 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator,\
40 NotAnonymous
41 NotAnonymous
41 from rhodecode.lib import helpers as h
42 from rhodecode.lib import helpers as h
42 from rhodecode.lib import diffs
43 from rhodecode.lib import diffs
43 from rhodecode.lib.utils import action_logger
44 from rhodecode.lib.utils import action_logger
44 from rhodecode.model.db import User, PullRequest, ChangesetStatus,\
45 from rhodecode.model.db import User, PullRequest, ChangesetStatus,\
45 ChangesetComment
46 ChangesetComment
46 from rhodecode.model.pull_request import PullRequestModel
47 from rhodecode.model.pull_request import PullRequestModel
47 from rhodecode.model.meta import Session
48 from rhodecode.model.meta import Session
48 from rhodecode.model.repo import RepoModel
49 from rhodecode.model.repo import RepoModel
49 from rhodecode.model.comment import ChangesetCommentsModel
50 from rhodecode.model.comment import ChangesetCommentsModel
50 from rhodecode.model.changeset_status import ChangesetStatusModel
51 from rhodecode.model.changeset_status import ChangesetStatusModel
52 from rhodecode.model.forms import PullRequestForm
51
53
52 log = logging.getLogger(__name__)
54 log = logging.getLogger(__name__)
53
55
54
56
55 class PullrequestsController(BaseRepoController):
57 class PullrequestsController(BaseRepoController):
56
58
57 @LoginRequired()
59 @LoginRequired()
58 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
60 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
59 'repository.admin')
61 'repository.admin')
60 def __before__(self):
62 def __before__(self):
61 super(PullrequestsController, self).__before__()
63 super(PullrequestsController, self).__before__()
62 repo_model = RepoModel()
64 repo_model = RepoModel()
63 c.users_array = repo_model.get_users_js()
65 c.users_array = repo_model.get_users_js()
64 c.users_groups_array = repo_model.get_users_groups_js()
66 c.users_groups_array = repo_model.get_users_groups_js()
65
67
66 def _get_repo_refs(self, repo):
68 def _get_repo_refs(self, repo):
67 hist_l = []
69 hist_l = []
68
70
69 branches_group = ([('branch:%s:%s' % (k, v), k) for
71 branches_group = ([('branch:%s:%s' % (k, v), k) for
70 k, v in repo.branches.iteritems()], _("Branches"))
72 k, v in repo.branches.iteritems()], _("Branches"))
71 bookmarks_group = ([('book:%s:%s' % (k, v), k) for
73 bookmarks_group = ([('book:%s:%s' % (k, v), k) for
72 k, v in repo.bookmarks.iteritems()], _("Bookmarks"))
74 k, v in repo.bookmarks.iteritems()], _("Bookmarks"))
73 tags_group = ([('tag:%s:%s' % (k, v), k) for
75 tags_group = ([('tag:%s:%s' % (k, v), k) for
74 k, v in repo.tags.iteritems()], _("Tags"))
76 k, v in repo.tags.iteritems()], _("Tags"))
75
77
76 hist_l.append(bookmarks_group)
78 hist_l.append(bookmarks_group)
77 hist_l.append(branches_group)
79 hist_l.append(branches_group)
78 hist_l.append(tags_group)
80 hist_l.append(tags_group)
79
81
80 return hist_l
82 return hist_l
81
83
82 def show_all(self, repo_name):
84 def show_all(self, repo_name):
83 c.pull_requests = PullRequestModel().get_all(repo_name)
85 c.pull_requests = PullRequestModel().get_all(repo_name)
84 c.repo_name = repo_name
86 c.repo_name = repo_name
85 return render('/pullrequests/pullrequest_show_all.html')
87 return render('/pullrequests/pullrequest_show_all.html')
86
88
87 @NotAnonymous()
89 @NotAnonymous()
88 def index(self):
90 def index(self):
89 org_repo = c.rhodecode_db_repo
91 org_repo = c.rhodecode_db_repo
90
92
91 if org_repo.scm_instance.alias != 'hg':
93 if org_repo.scm_instance.alias != 'hg':
92 log.error('Review not available for GIT REPOS')
94 log.error('Review not available for GIT REPOS')
93 raise HTTPNotFound
95 raise HTTPNotFound
94
96
95 other_repos_info = {}
97 other_repos_info = {}
96
98
97 c.org_refs = self._get_repo_refs(c.rhodecode_repo)
99 c.org_refs = self._get_repo_refs(c.rhodecode_repo)
98 c.org_repos = []
100 c.org_repos = []
99 c.other_repos = []
101 c.other_repos = []
100 c.org_repos.append((org_repo.repo_name, '%s/%s' % (
102 c.org_repos.append((org_repo.repo_name, '%s/%s' % (
101 org_repo.user.username, c.repo_name))
103 org_repo.user.username, c.repo_name))
102 )
104 )
103
105
104 c.other_refs = c.org_refs
106 c.other_refs = c.org_refs
105 c.other_repos.extend(c.org_repos)
107 c.other_repos.extend(c.org_repos)
106
108
107 #add orginal repo
109 #add orginal repo
108 other_repos_info[org_repo.repo_name] = {
110 other_repos_info[org_repo.repo_name] = {
109 'gravatar': h.gravatar_url(org_repo.user.email, 24),
111 'gravatar': h.gravatar_url(org_repo.user.email, 24),
110 'description': org_repo.description
112 'description': org_repo.description
111 }
113 }
112
114
113 c.default_pull_request = org_repo.repo_name
115 c.default_pull_request = org_repo.repo_name
114 #gather forks and add to this list
116 #gather forks and add to this list
115 for fork in org_repo.forks:
117 for fork in org_repo.forks:
116 c.other_repos.append((fork.repo_name, '%s/%s' % (
118 c.other_repos.append((fork.repo_name, '%s/%s' % (
117 fork.user.username, fork.repo_name))
119 fork.user.username, fork.repo_name))
118 )
120 )
119 other_repos_info[fork.repo_name] = {
121 other_repos_info[fork.repo_name] = {
120 'gravatar': h.gravatar_url(fork.user.email, 24),
122 'gravatar': h.gravatar_url(fork.user.email, 24),
121 'description': fork.description
123 'description': fork.description
122 }
124 }
123 #add parents of this fork also
125 #add parents of this fork also
124 if org_repo.parent:
126 if org_repo.parent:
125 c.default_pull_request = org_repo.parent.repo_name
127 c.default_pull_request = org_repo.parent.repo_name
126 c.other_repos.append((org_repo.parent.repo_name, '%s/%s' % (
128 c.other_repos.append((org_repo.parent.repo_name, '%s/%s' % (
127 org_repo.parent.user.username,
129 org_repo.parent.user.username,
128 org_repo.parent.repo_name))
130 org_repo.parent.repo_name))
129 )
131 )
130 other_repos_info[org_repo.parent.repo_name] = {
132 other_repos_info[org_repo.parent.repo_name] = {
131 'gravatar': h.gravatar_url(org_repo.parent.user.email, 24),
133 'gravatar': h.gravatar_url(org_repo.parent.user.email, 24),
132 'description': org_repo.parent.description
134 'description': org_repo.parent.description
133 }
135 }
134
136
135 c.other_repos_info = json.dumps(other_repos_info)
137 c.other_repos_info = json.dumps(other_repos_info)
136 c.review_members = [org_repo.user]
138 c.review_members = [org_repo.user]
137 return render('/pullrequests/pullrequest.html')
139 return render('/pullrequests/pullrequest.html')
138
140
139 @NotAnonymous()
141 @NotAnonymous()
140 def create(self, repo_name):
142 def create(self, repo_name):
141 req_p = request.POST
143
142 org_repo = req_p['org_repo']
144 try:
143 org_ref = req_p['org_ref']
145 _form = PullRequestForm()().to_python(request.POST)
144 other_repo = req_p['other_repo']
146 except formencode.Invalid, errors:
145 other_ref = req_p['other_ref']
147 log.error(traceback.format_exc())
146 revisions = req_p.getall('revisions')
148 if errors.error_dict.get('revisions'):
147 reviewers = req_p.getall('review_members')
149 msg = _('Cannot open a pull request with '
150 'empty list of changesets')
151 elif errors.error_dict.get('pullrequest_title'):
152 msg = _('Pull request requires a title with min. 3 chars')
153 else:
154 msg = _('error during creation of pull request')
148
155
149 #TODO: wrap this into a FORM !!!
156 h.flash(msg, 'error')
157 return redirect(url('pullrequest_home', repo_name=repo_name))
150
158
151 title = req_p['pullrequest_title']
159 org_repo = _form['org_repo']
152 description = req_p['pullrequest_desc']
160 org_ref = _form['org_ref']
161 other_repo = _form['other_repo']
162 other_ref = _form['other_ref']
163 revisions = _form['revisions']
164 reviewers = _form['review_members']
165
166 title = _form['pullrequest_title']
167 description = _form['pullrequest_desc']
153
168
154 try:
169 try:
155 pull_request = PullRequestModel().create(
170 pull_request = PullRequestModel().create(
156 self.rhodecode_user.user_id, org_repo, org_ref, other_repo,
171 self.rhodecode_user.user_id, org_repo, org_ref, other_repo,
157 other_ref, revisions, reviewers, title, description
172 other_ref, revisions, reviewers, title, description
158 )
173 )
159 Session().commit()
174 Session().commit()
160 h.flash(_('Successfully opened new pull request'),
175 h.flash(_('Successfully opened new pull request'),
161 category='success')
176 category='success')
162 except Exception:
177 except Exception:
163 h.flash(_('Error occurred during sending pull request'),
178 h.flash(_('Error occurred during sending pull request'),
164 category='error')
179 category='error')
165 log.error(traceback.format_exc())
180 log.error(traceback.format_exc())
166 return redirect(url('changelog_home', repo_name=org_repo,))
181 return redirect(url('pullrequest_home', repo_name=repo_name))
167
182
168 return redirect(url('pullrequest_show', repo_name=other_repo,
183 return redirect(url('pullrequest_show', repo_name=other_repo,
169 pull_request_id=pull_request.pull_request_id))
184 pull_request_id=pull_request.pull_request_id))
170
185
171 @NotAnonymous()
186 @NotAnonymous()
172 @jsonify
187 @jsonify
173 def update(self, repo_name, pull_request_id):
188 def update(self, repo_name, pull_request_id):
174 pull_request = PullRequest.get_or_404(pull_request_id)
189 pull_request = PullRequest.get_or_404(pull_request_id)
175 if pull_request.is_closed():
190 if pull_request.is_closed():
176 raise HTTPForbidden()
191 raise HTTPForbidden()
177
192
178 reviewers_ids = map(int, filter(lambda v: v not in [None, ''],
193 reviewers_ids = map(int, filter(lambda v: v not in [None, ''],
179 request.POST.get('reviewers_ids', '').split(',')))
194 request.POST.get('reviewers_ids', '').split(',')))
180 PullRequestModel().update_reviewers(pull_request_id, reviewers_ids)
195 PullRequestModel().update_reviewers(pull_request_id, reviewers_ids)
181 Session.commit()
196 Session.commit()
182 return True
197 return True
183
198
184 def _load_compare_data(self, pull_request, enable_comments=True):
199 def _load_compare_data(self, pull_request, enable_comments=True):
185 """
200 """
186 Load context data needed for generating compare diff
201 Load context data needed for generating compare diff
187
202
188 :param pull_request:
203 :param pull_request:
189 :type pull_request:
204 :type pull_request:
190 """
205 """
191
206
192 org_repo = pull_request.org_repo
207 org_repo = pull_request.org_repo
193 org_ref_type, org_ref_, org_ref = pull_request.org_ref.split(':')
208 (org_ref_type,
209 org_ref_name,
210 org_ref_rev) = pull_request.org_ref.split(':')
211
194 other_repo = pull_request.other_repo
212 other_repo = pull_request.other_repo
195 other_ref_type, other_ref, other_ref_ = pull_request.other_ref.split(':')
213 (other_ref_type,
214 other_ref_name,
215 other_ref_rev) = pull_request.other_ref.split(':')
196
216
197 org_ref = (org_ref_type, org_ref)
217 # dispite opening revisions for bookmarks/branches/tags, we always
198 other_ref = (other_ref_type, other_ref)
218 # convert this to rev to prevent changes after book or branch change
219 org_ref = ('rev', org_ref_rev)
220 other_ref = ('rev', other_ref_rev)
199
221
200 c.org_repo = org_repo
222 c.org_repo = org_repo
201 c.other_repo = other_repo
223 c.other_repo = other_repo
202
224
203 c.cs_ranges, discovery_data = PullRequestModel().get_compare_data(
225 c.cs_ranges, discovery_data = PullRequestModel().get_compare_data(
204 org_repo, org_ref, other_repo, other_ref
226 org_repo, org_ref, other_repo, other_ref
205 )
227 )
206
228
207 c.statuses = c.rhodecode_db_repo.statuses([x.raw_id for x in
229 c.statuses = c.rhodecode_db_repo.statuses([x.raw_id for x in
208 c.cs_ranges])
230 c.cs_ranges])
209 # defines that we need hidden inputs with changesets
231 # defines that we need hidden inputs with changesets
210 c.as_form = request.GET.get('as_form', False)
232 c.as_form = request.GET.get('as_form', False)
211
233
212 c.org_ref = org_ref[1]
234 c.org_ref = org_ref[1]
213 c.other_ref = other_ref[1]
235 c.other_ref = other_ref[1]
214 # diff needs to have swapped org with other to generate proper diff
236 # diff needs to have swapped org with other to generate proper diff
215 _diff = diffs.differ(other_repo, other_ref, org_repo, org_ref,
237 _diff = diffs.differ(other_repo, other_ref, org_repo, org_ref,
216 discovery_data)
238 discovery_data)
217 diff_processor = diffs.DiffProcessor(_diff, format='gitdiff')
239 diff_processor = diffs.DiffProcessor(_diff, format='gitdiff')
218 _parsed = diff_processor.prepare()
240 _parsed = diff_processor.prepare()
219
241
220 c.files = []
242 c.files = []
221 c.changes = {}
243 c.changes = {}
222
244
223 for f in _parsed:
245 for f in _parsed:
224 fid = h.FID('', f['filename'])
246 fid = h.FID('', f['filename'])
225 c.files.append([fid, f['operation'], f['filename'], f['stats']])
247 c.files.append([fid, f['operation'], f['filename'], f['stats']])
226 diff = diff_processor.as_html(enable_comments=enable_comments,
248 diff = diff_processor.as_html(enable_comments=enable_comments,
227 diff_lines=[f])
249 diff_lines=[f])
228 c.changes[fid] = [f['operation'], f['filename'], diff]
250 c.changes[fid] = [f['operation'], f['filename'], diff]
229
251
230 def show(self, repo_name, pull_request_id):
252 def show(self, repo_name, pull_request_id):
231 repo_model = RepoModel()
253 repo_model = RepoModel()
232 c.users_array = repo_model.get_users_js()
254 c.users_array = repo_model.get_users_js()
233 c.users_groups_array = repo_model.get_users_groups_js()
255 c.users_groups_array = repo_model.get_users_groups_js()
234 c.pull_request = PullRequest.get_or_404(pull_request_id)
256 c.pull_request = PullRequest.get_or_404(pull_request_id)
235
257
236 cc_model = ChangesetCommentsModel()
258 cc_model = ChangesetCommentsModel()
237 cs_model = ChangesetStatusModel()
259 cs_model = ChangesetStatusModel()
238 _cs_statuses = cs_model.get_statuses(c.pull_request.org_repo,
260 _cs_statuses = cs_model.get_statuses(c.pull_request.org_repo,
239 pull_request=c.pull_request,
261 pull_request=c.pull_request,
240 with_revisions=True)
262 with_revisions=True)
241
263
242 cs_statuses = defaultdict(list)
264 cs_statuses = defaultdict(list)
243 for st in _cs_statuses:
265 for st in _cs_statuses:
244 cs_statuses[st.author.username] += [st]
266 cs_statuses[st.author.username] += [st]
245
267
246 c.pull_request_reviewers = []
268 c.pull_request_reviewers = []
247 for o in c.pull_request.reviewers:
269 for o in c.pull_request.reviewers:
248 st = cs_statuses.get(o.user.username, None)
270 st = cs_statuses.get(o.user.username, None)
249 if st:
271 if st:
250 sorter = lambda k: k.version
272 sorter = lambda k: k.version
251 st = [(x, list(y)[0])
273 st = [(x, list(y)[0])
252 for x, y in (groupby(sorted(st, key=sorter), sorter))]
274 for x, y in (groupby(sorted(st, key=sorter), sorter))]
253 c.pull_request_reviewers.append([o.user, st])
275 c.pull_request_reviewers.append([o.user, st])
254
276
255 # pull_requests repo_name we opened it against
277 # pull_requests repo_name we opened it against
256 # ie. other_repo must match
278 # ie. other_repo must match
257 if repo_name != c.pull_request.other_repo.repo_name:
279 if repo_name != c.pull_request.other_repo.repo_name:
258 raise HTTPNotFound
280 raise HTTPNotFound
259
281
260 # load compare data into template context
282 # load compare data into template context
261 enable_comments = not c.pull_request.is_closed()
283 enable_comments = not c.pull_request.is_closed()
262 self._load_compare_data(c.pull_request, enable_comments=enable_comments)
284 self._load_compare_data(c.pull_request, enable_comments=enable_comments)
263
285
264 # inline comments
286 # inline comments
265 c.inline_cnt = 0
287 c.inline_cnt = 0
266 c.inline_comments = cc_model.get_inline_comments(
288 c.inline_comments = cc_model.get_inline_comments(
267 c.rhodecode_db_repo.repo_id,
289 c.rhodecode_db_repo.repo_id,
268 pull_request=pull_request_id)
290 pull_request=pull_request_id)
269 # count inline comments
291 # count inline comments
270 for __, lines in c.inline_comments:
292 for __, lines in c.inline_comments:
271 for comments in lines.values():
293 for comments in lines.values():
272 c.inline_cnt += len(comments)
294 c.inline_cnt += len(comments)
273 # comments
295 # comments
274 c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id,
296 c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id,
275 pull_request=pull_request_id)
297 pull_request=pull_request_id)
276
298
277 # changeset(pull-request) status
299 # changeset(pull-request) status
278 c.current_changeset_status = cs_model.calculate_status(
300 c.current_changeset_status = cs_model.calculate_status(
279 c.pull_request_reviewers
301 c.pull_request_reviewers
280 )
302 )
281 c.changeset_statuses = ChangesetStatus.STATUSES
303 c.changeset_statuses = ChangesetStatus.STATUSES
282 c.target_repo = c.pull_request.org_repo.repo_name
304 c.target_repo = c.pull_request.org_repo.repo_name
283 return render('/pullrequests/pullrequest_show.html')
305 return render('/pullrequests/pullrequest_show.html')
284
306
285 @NotAnonymous()
307 @NotAnonymous()
286 @jsonify
308 @jsonify
287 def comment(self, repo_name, pull_request_id):
309 def comment(self, repo_name, pull_request_id):
288 pull_request = PullRequest.get_or_404(pull_request_id)
310 pull_request = PullRequest.get_or_404(pull_request_id)
289 if pull_request.is_closed():
311 if pull_request.is_closed():
290 raise HTTPForbidden()
312 raise HTTPForbidden()
291
313
292 status = request.POST.get('changeset_status')
314 status = request.POST.get('changeset_status')
293 change_status = request.POST.get('change_changeset_status')
315 change_status = request.POST.get('change_changeset_status')
294
316
295 comm = ChangesetCommentsModel().create(
317 comm = ChangesetCommentsModel().create(
296 text=request.POST.get('text'),
318 text=request.POST.get('text'),
297 repo=c.rhodecode_db_repo.repo_id,
319 repo=c.rhodecode_db_repo.repo_id,
298 user=c.rhodecode_user.user_id,
320 user=c.rhodecode_user.user_id,
299 pull_request=pull_request_id,
321 pull_request=pull_request_id,
300 f_path=request.POST.get('f_path'),
322 f_path=request.POST.get('f_path'),
301 line_no=request.POST.get('line'),
323 line_no=request.POST.get('line'),
302 status_change=(ChangesetStatus.get_status_lbl(status)
324 status_change=(ChangesetStatus.get_status_lbl(status)
303 if status and change_status else None)
325 if status and change_status else None)
304 )
326 )
305
327
306 # get status if set !
328 # get status if set !
307 if status and change_status:
329 if status and change_status:
308 ChangesetStatusModel().set_status(
330 ChangesetStatusModel().set_status(
309 c.rhodecode_db_repo.repo_id,
331 c.rhodecode_db_repo.repo_id,
310 status,
332 status,
311 c.rhodecode_user.user_id,
333 c.rhodecode_user.user_id,
312 comm,
334 comm,
313 pull_request=pull_request_id
335 pull_request=pull_request_id
314 )
336 )
315 action_logger(self.rhodecode_user,
337 action_logger(self.rhodecode_user,
316 'user_commented_pull_request:%s' % pull_request_id,
338 'user_commented_pull_request:%s' % pull_request_id,
317 c.rhodecode_db_repo, self.ip_addr, self.sa)
339 c.rhodecode_db_repo, self.ip_addr, self.sa)
318
340
319 if request.POST.get('save_close'):
341 if request.POST.get('save_close'):
320 PullRequestModel().close_pull_request(pull_request_id)
342 PullRequestModel().close_pull_request(pull_request_id)
321 action_logger(self.rhodecode_user,
343 action_logger(self.rhodecode_user,
322 'user_closed_pull_request:%s' % pull_request_id,
344 'user_closed_pull_request:%s' % pull_request_id,
323 c.rhodecode_db_repo, self.ip_addr, self.sa)
345 c.rhodecode_db_repo, self.ip_addr, self.sa)
324
346
325 Session().commit()
347 Session().commit()
326
348
327 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
349 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
328 return redirect(h.url('pullrequest_show', repo_name=repo_name,
350 return redirect(h.url('pullrequest_show', repo_name=repo_name,
329 pull_request_id=pull_request_id))
351 pull_request_id=pull_request_id))
330
352
331 data = {
353 data = {
332 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
354 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
333 }
355 }
334 if comm:
356 if comm:
335 c.co = comm
357 c.co = comm
336 data.update(comm.get_dict())
358 data.update(comm.get_dict())
337 data.update({'rendered_text':
359 data.update({'rendered_text':
338 render('changeset/changeset_comment_block.html')})
360 render('changeset/changeset_comment_block.html')})
339
361
340 return data
362 return data
341
363
342 @NotAnonymous()
364 @NotAnonymous()
343 @jsonify
365 @jsonify
344 def delete_comment(self, repo_name, comment_id):
366 def delete_comment(self, repo_name, comment_id):
345 co = ChangesetComment.get(comment_id)
367 co = ChangesetComment.get(comment_id)
346 if co.pull_request.is_closed():
368 if co.pull_request.is_closed():
347 #don't allow deleting comments on closed pull request
369 #don't allow deleting comments on closed pull request
348 raise HTTPForbidden()
370 raise HTTPForbidden()
349
371
350 owner = lambda: co.author.user_id == c.rhodecode_user.user_id
372 owner = lambda: co.author.user_id == c.rhodecode_user.user_id
351 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
373 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
352 ChangesetCommentsModel().delete(comment=co)
374 ChangesetCommentsModel().delete(comment=co)
353 Session().commit()
375 Session().commit()
354 return True
376 return True
355 else:
377 else:
356 raise HTTPForbidden()
378 raise HTTPForbidden()
@@ -1,323 +1,342 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
130
131 chained_validators = [v.ValidReposGroup(edit, old_data),
131 chained_validators = [v.ValidReposGroup(edit, old_data),
132 v.ValidPerms('group')]
132 v.ValidPerms('group')]
133
133
134 return _ReposGroupForm
134 return _ReposGroupForm
135
135
136
136
137 def RegisterForm(edit=False, old_data={}):
137 def RegisterForm(edit=False, old_data={}):
138 class _RegisterForm(formencode.Schema):
138 class _RegisterForm(formencode.Schema):
139 allow_extra_fields = True
139 allow_extra_fields = True
140 filter_extra_fields = True
140 filter_extra_fields = True
141 username = All(
141 username = All(
142 v.ValidUsername(edit, old_data),
142 v.ValidUsername(edit, old_data),
143 v.UnicodeString(strip=True, min=1, not_empty=True)
143 v.UnicodeString(strip=True, min=1, not_empty=True)
144 )
144 )
145 password = All(
145 password = All(
146 v.ValidPassword(),
146 v.ValidPassword(),
147 v.UnicodeString(strip=False, min=6, not_empty=True)
147 v.UnicodeString(strip=False, min=6, not_empty=True)
148 )
148 )
149 password_confirmation = All(
149 password_confirmation = All(
150 v.ValidPassword(),
150 v.ValidPassword(),
151 v.UnicodeString(strip=False, min=6, not_empty=True)
151 v.UnicodeString(strip=False, min=6, not_empty=True)
152 )
152 )
153 active = v.StringBoolean(if_missing=False)
153 active = v.StringBoolean(if_missing=False)
154 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
154 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
155 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
155 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
156 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
156 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
157
157
158 chained_validators = [v.ValidPasswordsMatch()]
158 chained_validators = [v.ValidPasswordsMatch()]
159
159
160 return _RegisterForm
160 return _RegisterForm
161
161
162
162
163 def PasswordResetForm():
163 def PasswordResetForm():
164 class _PasswordResetForm(formencode.Schema):
164 class _PasswordResetForm(formencode.Schema):
165 allow_extra_fields = True
165 allow_extra_fields = True
166 filter_extra_fields = True
166 filter_extra_fields = True
167 email = All(v.ValidSystemEmail(), v.Email(not_empty=True))
167 email = All(v.ValidSystemEmail(), v.Email(not_empty=True))
168 return _PasswordResetForm
168 return _PasswordResetForm
169
169
170
170
171 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
171 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
172 repo_groups=[], landing_revs=[]):
172 repo_groups=[], landing_revs=[]):
173 class _RepoForm(formencode.Schema):
173 class _RepoForm(formencode.Schema):
174 allow_extra_fields = True
174 allow_extra_fields = True
175 filter_extra_fields = False
175 filter_extra_fields = False
176 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
176 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
177 v.SlugifyName())
177 v.SlugifyName())
178 clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False))
178 clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False))
179 repo_group = v.OneOf(repo_groups, hideList=True)
179 repo_group = v.OneOf(repo_groups, hideList=True)
180 repo_type = v.OneOf(supported_backends)
180 repo_type = v.OneOf(supported_backends)
181 description = v.UnicodeString(strip=True, min=1, not_empty=False)
181 description = v.UnicodeString(strip=True, min=1, not_empty=False)
182 private = v.StringBoolean(if_missing=False)
182 private = v.StringBoolean(if_missing=False)
183 enable_statistics = v.StringBoolean(if_missing=False)
183 enable_statistics = v.StringBoolean(if_missing=False)
184 enable_downloads = v.StringBoolean(if_missing=False)
184 enable_downloads = v.StringBoolean(if_missing=False)
185 landing_rev = v.OneOf(landing_revs, hideList=True)
185 landing_rev = v.OneOf(landing_revs, hideList=True)
186
186
187 if edit:
187 if edit:
188 #this is repo owner
188 #this is repo owner
189 user = All(v.UnicodeString(not_empty=True), v.ValidRepoUser())
189 user = All(v.UnicodeString(not_empty=True), v.ValidRepoUser())
190
190
191 chained_validators = [v.ValidCloneUri(),
191 chained_validators = [v.ValidCloneUri(),
192 v.ValidRepoName(edit, old_data),
192 v.ValidRepoName(edit, old_data),
193 v.ValidPerms()]
193 v.ValidPerms()]
194 return _RepoForm
194 return _RepoForm
195
195
196
196
197 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
197 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
198 repo_groups=[], landing_revs=[]):
198 repo_groups=[], landing_revs=[]):
199 class _RepoForkForm(formencode.Schema):
199 class _RepoForkForm(formencode.Schema):
200 allow_extra_fields = True
200 allow_extra_fields = True
201 filter_extra_fields = False
201 filter_extra_fields = False
202 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
202 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
203 v.SlugifyName())
203 v.SlugifyName())
204 repo_group = v.OneOf(repo_groups, hideList=True)
204 repo_group = v.OneOf(repo_groups, hideList=True)
205 repo_type = All(v.ValidForkType(old_data), v.OneOf(supported_backends))
205 repo_type = All(v.ValidForkType(old_data), v.OneOf(supported_backends))
206 description = v.UnicodeString(strip=True, min=1, not_empty=True)
206 description = v.UnicodeString(strip=True, min=1, not_empty=True)
207 private = v.StringBoolean(if_missing=False)
207 private = v.StringBoolean(if_missing=False)
208 copy_permissions = v.StringBoolean(if_missing=False)
208 copy_permissions = v.StringBoolean(if_missing=False)
209 update_after_clone = v.StringBoolean(if_missing=False)
209 update_after_clone = v.StringBoolean(if_missing=False)
210 fork_parent_id = v.UnicodeString()
210 fork_parent_id = v.UnicodeString()
211 chained_validators = [v.ValidForkName(edit, old_data)]
211 chained_validators = [v.ValidForkName(edit, old_data)]
212 landing_rev = v.OneOf(landing_revs, hideList=True)
212 landing_rev = v.OneOf(landing_revs, hideList=True)
213
213
214 return _RepoForkForm
214 return _RepoForkForm
215
215
216
216
217 def RepoSettingsForm(edit=False, old_data={},
217 def RepoSettingsForm(edit=False, old_data={},
218 supported_backends=BACKENDS.keys(), repo_groups=[],
218 supported_backends=BACKENDS.keys(), repo_groups=[],
219 landing_revs=[]):
219 landing_revs=[]):
220 class _RepoForm(formencode.Schema):
220 class _RepoForm(formencode.Schema):
221 allow_extra_fields = True
221 allow_extra_fields = True
222 filter_extra_fields = False
222 filter_extra_fields = False
223 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
223 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
224 v.SlugifyName())
224 v.SlugifyName())
225 description = v.UnicodeString(strip=True, min=1, not_empty=True)
225 description = v.UnicodeString(strip=True, min=1, not_empty=True)
226 repo_group = v.OneOf(repo_groups, hideList=True)
226 repo_group = v.OneOf(repo_groups, hideList=True)
227 private = v.StringBoolean(if_missing=False)
227 private = v.StringBoolean(if_missing=False)
228 landing_rev = v.OneOf(landing_revs, hideList=True)
228 landing_rev = v.OneOf(landing_revs, hideList=True)
229 chained_validators = [v.ValidRepoName(edit, old_data), v.ValidPerms(),
229 chained_validators = [v.ValidRepoName(edit, old_data), v.ValidPerms(),
230 v.ValidSettings()]
230 v.ValidSettings()]
231 return _RepoForm
231 return _RepoForm
232
232
233
233
234 def ApplicationSettingsForm():
234 def ApplicationSettingsForm():
235 class _ApplicationSettingsForm(formencode.Schema):
235 class _ApplicationSettingsForm(formencode.Schema):
236 allow_extra_fields = True
236 allow_extra_fields = True
237 filter_extra_fields = False
237 filter_extra_fields = False
238 rhodecode_title = v.UnicodeString(strip=True, min=1, not_empty=True)
238 rhodecode_title = v.UnicodeString(strip=True, min=1, not_empty=True)
239 rhodecode_realm = v.UnicodeString(strip=True, min=1, not_empty=True)
239 rhodecode_realm = v.UnicodeString(strip=True, min=1, not_empty=True)
240 rhodecode_ga_code = v.UnicodeString(strip=True, min=1, not_empty=False)
240 rhodecode_ga_code = v.UnicodeString(strip=True, min=1, not_empty=False)
241
241
242 return _ApplicationSettingsForm
242 return _ApplicationSettingsForm
243
243
244
244
245 def ApplicationVisualisationForm():
245 def ApplicationVisualisationForm():
246 class _ApplicationVisualisationForm(formencode.Schema):
246 class _ApplicationVisualisationForm(formencode.Schema):
247 allow_extra_fields = True
247 allow_extra_fields = True
248 filter_extra_fields = False
248 filter_extra_fields = False
249 rhodecode_show_public_icon = v.StringBoolean(if_missing=False)
249 rhodecode_show_public_icon = v.StringBoolean(if_missing=False)
250 rhodecode_show_private_icon = v.StringBoolean(if_missing=False)
250 rhodecode_show_private_icon = v.StringBoolean(if_missing=False)
251 rhodecode_stylify_metatags = v.StringBoolean(if_missing=False)
251 rhodecode_stylify_metatags = v.StringBoolean(if_missing=False)
252
252
253 return _ApplicationVisualisationForm
253 return _ApplicationVisualisationForm
254
254
255
255
256 def ApplicationUiSettingsForm():
256 def ApplicationUiSettingsForm():
257 class _ApplicationUiSettingsForm(formencode.Schema):
257 class _ApplicationUiSettingsForm(formencode.Schema):
258 allow_extra_fields = True
258 allow_extra_fields = True
259 filter_extra_fields = False
259 filter_extra_fields = False
260 web_push_ssl = v.StringBoolean(if_missing=False)
260 web_push_ssl = v.StringBoolean(if_missing=False)
261 paths_root_path = All(
261 paths_root_path = All(
262 v.ValidPath(),
262 v.ValidPath(),
263 v.UnicodeString(strip=True, min=1, not_empty=True)
263 v.UnicodeString(strip=True, min=1, not_empty=True)
264 )
264 )
265 hooks_changegroup_update = v.StringBoolean(if_missing=False)
265 hooks_changegroup_update = v.StringBoolean(if_missing=False)
266 hooks_changegroup_repo_size = v.StringBoolean(if_missing=False)
266 hooks_changegroup_repo_size = v.StringBoolean(if_missing=False)
267 hooks_changegroup_push_logger = v.StringBoolean(if_missing=False)
267 hooks_changegroup_push_logger = v.StringBoolean(if_missing=False)
268 hooks_preoutgoing_pull_logger = v.StringBoolean(if_missing=False)
268 hooks_preoutgoing_pull_logger = v.StringBoolean(if_missing=False)
269
269
270 extensions_largefiles = v.StringBoolean(if_missing=False)
270 extensions_largefiles = v.StringBoolean(if_missing=False)
271 extensions_hgsubversion = v.StringBoolean(if_missing=False)
271 extensions_hgsubversion = v.StringBoolean(if_missing=False)
272 extensions_hggit = v.StringBoolean(if_missing=False)
272 extensions_hggit = v.StringBoolean(if_missing=False)
273
273
274 return _ApplicationUiSettingsForm
274 return _ApplicationUiSettingsForm
275
275
276
276
277 def DefaultPermissionsForm(perms_choices, register_choices, create_choices,
277 def DefaultPermissionsForm(perms_choices, register_choices, create_choices,
278 fork_choices):
278 fork_choices):
279 class _DefaultPermissionsForm(formencode.Schema):
279 class _DefaultPermissionsForm(formencode.Schema):
280 allow_extra_fields = True
280 allow_extra_fields = True
281 filter_extra_fields = True
281 filter_extra_fields = True
282 overwrite_default = v.StringBoolean(if_missing=False)
282 overwrite_default = v.StringBoolean(if_missing=False)
283 anonymous = v.StringBoolean(if_missing=False)
283 anonymous = v.StringBoolean(if_missing=False)
284 default_perm = v.OneOf(perms_choices)
284 default_perm = v.OneOf(perms_choices)
285 default_register = v.OneOf(register_choices)
285 default_register = v.OneOf(register_choices)
286 default_create = v.OneOf(create_choices)
286 default_create = v.OneOf(create_choices)
287 default_fork = v.OneOf(fork_choices)
287 default_fork = v.OneOf(fork_choices)
288
288
289 return _DefaultPermissionsForm
289 return _DefaultPermissionsForm
290
290
291
291
292 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices,
292 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices,
293 tls_kind_choices):
293 tls_kind_choices):
294 class _LdapSettingsForm(formencode.Schema):
294 class _LdapSettingsForm(formencode.Schema):
295 allow_extra_fields = True
295 allow_extra_fields = True
296 filter_extra_fields = True
296 filter_extra_fields = True
297 #pre_validators = [LdapLibValidator]
297 #pre_validators = [LdapLibValidator]
298 ldap_active = v.StringBoolean(if_missing=False)
298 ldap_active = v.StringBoolean(if_missing=False)
299 ldap_host = v.UnicodeString(strip=True,)
299 ldap_host = v.UnicodeString(strip=True,)
300 ldap_port = v.Number(strip=True,)
300 ldap_port = v.Number(strip=True,)
301 ldap_tls_kind = v.OneOf(tls_kind_choices)
301 ldap_tls_kind = v.OneOf(tls_kind_choices)
302 ldap_tls_reqcert = v.OneOf(tls_reqcert_choices)
302 ldap_tls_reqcert = v.OneOf(tls_reqcert_choices)
303 ldap_dn_user = v.UnicodeString(strip=True,)
303 ldap_dn_user = v.UnicodeString(strip=True,)
304 ldap_dn_pass = v.UnicodeString(strip=True,)
304 ldap_dn_pass = v.UnicodeString(strip=True,)
305 ldap_base_dn = v.UnicodeString(strip=True,)
305 ldap_base_dn = v.UnicodeString(strip=True,)
306 ldap_filter = v.UnicodeString(strip=True,)
306 ldap_filter = v.UnicodeString(strip=True,)
307 ldap_search_scope = v.OneOf(search_scope_choices)
307 ldap_search_scope = v.OneOf(search_scope_choices)
308 ldap_attr_login = All(
308 ldap_attr_login = All(
309 v.AttrLoginValidator(),
309 v.AttrLoginValidator(),
310 v.UnicodeString(strip=True,)
310 v.UnicodeString(strip=True,)
311 )
311 )
312 ldap_attr_firstname = v.UnicodeString(strip=True,)
312 ldap_attr_firstname = v.UnicodeString(strip=True,)
313 ldap_attr_lastname = v.UnicodeString(strip=True,)
313 ldap_attr_lastname = v.UnicodeString(strip=True,)
314 ldap_attr_email = v.UnicodeString(strip=True,)
314 ldap_attr_email = v.UnicodeString(strip=True,)
315
315
316 return _LdapSettingsForm
316 return _LdapSettingsForm
317
317
318
318
319 def UserExtraEmailForm():
319 def UserExtraEmailForm():
320 class _UserExtraEmailForm(formencode.Schema):
320 class _UserExtraEmailForm(formencode.Schema):
321 email = All(v.UniqSystemEmail(), v.Email)
321 email = All(v.UniqSystemEmail(), v.Email)
322
322
323 return _UserExtraEmailForm
323 return _UserExtraEmailForm
324
325
326 def PullRequestForm():
327 class _PullRequestForm(formencode.Schema):
328 allow_extra_fields = True
329 filter_extra_fields = True
330
331 user = v.UnicodeString(strip=True, required=True)
332 org_repo = v.UnicodeString(strip=True, required=True)
333 org_ref = v.UnicodeString(strip=True, required=True)
334 other_repo = v.UnicodeString(strip=True, required=True)
335 other_ref = v.UnicodeString(strip=True, required=True)
336 revisions = v.Set(required=True)
337 review_members = v.Set(required=True)
338
339 pullrequest_title = v.UnicodeString(strip=True, required=True, min=3)
340 pullrequest_desc = v.UnicodeString(strip=True, required=False)
341
342 return _PullRequestForm No newline at end of file
@@ -1,602 +1,601 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 pylons.i18n.translation import _
8 from pylons.i18n.translation import _
9 from webhelpers.pylonslib.secure_form import authentication_token
9 from webhelpers.pylonslib.secure_form import authentication_token
10
10
11 from formencode.validators import (
11 from formencode.validators import (
12 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set
12 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set,
13 )
13 )
14
15 from rhodecode.lib.utils import repo_name_slug
14 from rhodecode.lib.utils import repo_name_slug
16 from rhodecode.model.db import RepoGroup, Repository, UsersGroup, User
15 from rhodecode.model.db import RepoGroup, Repository, UsersGroup, User
17 from rhodecode.lib.exceptions import LdapImportError
16 from rhodecode.lib.exceptions import LdapImportError
18 from rhodecode.config.routing import ADMIN_PREFIX
17 from rhodecode.config.routing import ADMIN_PREFIX
19 # silence warnings and pylint
18 # silence warnings and pylint
20 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set
19 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set
21
20
22 log = logging.getLogger(__name__)
21 log = logging.getLogger(__name__)
23
22
24
23
25 class StateObj(object):
24 class StateObj(object):
26 """
25 """
27 this is needed to translate the messages using _() in validators
26 this is needed to translate the messages using _() in validators
28 """
27 """
29 _ = staticmethod(_)
28 _ = staticmethod(_)
30
29
31
30
32 def M(self, key, state=None, **kwargs):
31 def M(self, key, state=None, **kwargs):
33 """
32 """
34 returns string from self.message based on given key,
33 returns string from self.message based on given key,
35 passed kw params are used to substitute %(named)s params inside
34 passed kw params are used to substitute %(named)s params inside
36 translated strings
35 translated strings
37
36
38 :param msg:
37 :param msg:
39 :param state:
38 :param state:
40 """
39 """
41 if state is None:
40 if state is None:
42 state = StateObj()
41 state = StateObj()
43 else:
42 else:
44 state._ = staticmethod(_)
43 state._ = staticmethod(_)
45 #inject validator into state object
44 #inject validator into state object
46 return self.message(key, state, **kwargs)
45 return self.message(key, state, **kwargs)
47
46
48
47
49 def ValidUsername(edit=False, old_data={}):
48 def ValidUsername(edit=False, old_data={}):
50 class _validator(formencode.validators.FancyValidator):
49 class _validator(formencode.validators.FancyValidator):
51 messages = {
50 messages = {
52 'username_exists': _(u'Username "%(username)s" already exists'),
51 'username_exists': _(u'Username "%(username)s" already exists'),
53 'system_invalid_username':
52 'system_invalid_username':
54 _(u'Username "%(username)s" is forbidden'),
53 _(u'Username "%(username)s" is forbidden'),
55 'invalid_username':
54 'invalid_username':
56 _(u'Username may only contain alphanumeric characters '
55 _(u'Username may only contain alphanumeric characters '
57 'underscores, periods or dashes and must begin with '
56 'underscores, periods or dashes and must begin with '
58 'alphanumeric character')
57 'alphanumeric character')
59 }
58 }
60
59
61 def validate_python(self, value, state):
60 def validate_python(self, value, state):
62 if value in ['default', 'new_user']:
61 if value in ['default', 'new_user']:
63 msg = M(self, 'system_invalid_username', state, username=value)
62 msg = M(self, 'system_invalid_username', state, username=value)
64 raise formencode.Invalid(msg, value, state)
63 raise formencode.Invalid(msg, value, state)
65 #check if user is unique
64 #check if user is unique
66 old_un = None
65 old_un = None
67 if edit:
66 if edit:
68 old_un = User.get(old_data.get('user_id')).username
67 old_un = User.get(old_data.get('user_id')).username
69
68
70 if old_un != value or not edit:
69 if old_un != value or not edit:
71 if User.get_by_username(value, case_insensitive=True):
70 if User.get_by_username(value, case_insensitive=True):
72 msg = M(self, 'username_exists', state, username=value)
71 msg = M(self, 'username_exists', state, username=value)
73 raise formencode.Invalid(msg, value, state)
72 raise formencode.Invalid(msg, value, state)
74
73
75 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
74 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
76 msg = M(self, 'invalid_username', state)
75 msg = M(self, 'invalid_username', state)
77 raise formencode.Invalid(msg, value, state)
76 raise formencode.Invalid(msg, value, state)
78 return _validator
77 return _validator
79
78
80
79
81 def ValidRepoUser():
80 def ValidRepoUser():
82 class _validator(formencode.validators.FancyValidator):
81 class _validator(formencode.validators.FancyValidator):
83 messages = {
82 messages = {
84 'invalid_username': _(u'Username %(username)s is not valid')
83 'invalid_username': _(u'Username %(username)s is not valid')
85 }
84 }
86
85
87 def validate_python(self, value, state):
86 def validate_python(self, value, state):
88 try:
87 try:
89 User.query().filter(User.active == True)\
88 User.query().filter(User.active == True)\
90 .filter(User.username == value).one()
89 .filter(User.username == value).one()
91 except Exception:
90 except Exception:
92 msg = M(self, 'invalid_username', state, username=value)
91 msg = M(self, 'invalid_username', state, username=value)
93 raise formencode.Invalid(msg, value, state,
92 raise formencode.Invalid(msg, value, state,
94 error_dict=dict(username=msg)
93 error_dict=dict(username=msg)
95 )
94 )
96
95
97 return _validator
96 return _validator
98
97
99
98
100 def ValidUsersGroup(edit=False, old_data={}):
99 def ValidUsersGroup(edit=False, old_data={}):
101 class _validator(formencode.validators.FancyValidator):
100 class _validator(formencode.validators.FancyValidator):
102 messages = {
101 messages = {
103 'invalid_group': _(u'Invalid users group name'),
102 'invalid_group': _(u'Invalid users group name'),
104 'group_exist': _(u'Users group "%(usersgroup)s" already exists'),
103 'group_exist': _(u'Users group "%(usersgroup)s" already exists'),
105 'invalid_usersgroup_name':
104 'invalid_usersgroup_name':
106 _(u'users group name may only contain alphanumeric '
105 _(u'users group name may only contain alphanumeric '
107 'characters underscores, periods or dashes and must begin '
106 'characters underscores, periods or dashes and must begin '
108 'with alphanumeric character')
107 'with alphanumeric character')
109 }
108 }
110
109
111 def validate_python(self, value, state):
110 def validate_python(self, value, state):
112 if value in ['default']:
111 if value in ['default']:
113 msg = M(self, 'invalid_group', state)
112 msg = M(self, 'invalid_group', state)
114 raise formencode.Invalid(msg, value, state,
113 raise formencode.Invalid(msg, value, state,
115 error_dict=dict(users_group_name=msg)
114 error_dict=dict(users_group_name=msg)
116 )
115 )
117 #check if group is unique
116 #check if group is unique
118 old_ugname = None
117 old_ugname = None
119 if edit:
118 if edit:
120 old_id = old_data.get('users_group_id')
119 old_id = old_data.get('users_group_id')
121 old_ugname = UsersGroup.get(old_id).users_group_name
120 old_ugname = UsersGroup.get(old_id).users_group_name
122
121
123 if old_ugname != value or not edit:
122 if old_ugname != value or not edit:
124 is_existing_group = UsersGroup.get_by_group_name(value,
123 is_existing_group = UsersGroup.get_by_group_name(value,
125 case_insensitive=True)
124 case_insensitive=True)
126 if is_existing_group:
125 if is_existing_group:
127 msg = M(self, 'group_exist', state, usersgroup=value)
126 msg = M(self, 'group_exist', state, usersgroup=value)
128 raise formencode.Invalid(msg, value, state,
127 raise formencode.Invalid(msg, value, state,
129 error_dict=dict(users_group_name=msg)
128 error_dict=dict(users_group_name=msg)
130 )
129 )
131
130
132 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
131 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
133 msg = M(self, 'invalid_usersgroup_name', state)
132 msg = M(self, 'invalid_usersgroup_name', state)
134 raise formencode.Invalid(msg, value, state,
133 raise formencode.Invalid(msg, value, state,
135 error_dict=dict(users_group_name=msg)
134 error_dict=dict(users_group_name=msg)
136 )
135 )
137
136
138 return _validator
137 return _validator
139
138
140
139
141 def ValidReposGroup(edit=False, old_data={}):
140 def ValidReposGroup(edit=False, old_data={}):
142 class _validator(formencode.validators.FancyValidator):
141 class _validator(formencode.validators.FancyValidator):
143 messages = {
142 messages = {
144 'group_parent_id': _(u'Cannot assign this group as parent'),
143 'group_parent_id': _(u'Cannot assign this group as parent'),
145 'group_exists': _(u'Group "%(group_name)s" already exists'),
144 'group_exists': _(u'Group "%(group_name)s" already exists'),
146 'repo_exists':
145 'repo_exists':
147 _(u'Repository with name "%(group_name)s" already exists')
146 _(u'Repository with name "%(group_name)s" already exists')
148 }
147 }
149
148
150 def validate_python(self, value, state):
149 def validate_python(self, value, state):
151 # TODO WRITE VALIDATIONS
150 # TODO WRITE VALIDATIONS
152 group_name = value.get('group_name')
151 group_name = value.get('group_name')
153 group_parent_id = value.get('group_parent_id')
152 group_parent_id = value.get('group_parent_id')
154
153
155 # slugify repo group just in case :)
154 # slugify repo group just in case :)
156 slug = repo_name_slug(group_name)
155 slug = repo_name_slug(group_name)
157
156
158 # check for parent of self
157 # check for parent of self
159 parent_of_self = lambda: (
158 parent_of_self = lambda: (
160 old_data['group_id'] == int(group_parent_id)
159 old_data['group_id'] == int(group_parent_id)
161 if group_parent_id else False
160 if group_parent_id else False
162 )
161 )
163 if edit and parent_of_self():
162 if edit and parent_of_self():
164 msg = M(self, 'group_parent_id', state)
163 msg = M(self, 'group_parent_id', state)
165 raise formencode.Invalid(msg, value, state,
164 raise formencode.Invalid(msg, value, state,
166 error_dict=dict(group_parent_id=msg)
165 error_dict=dict(group_parent_id=msg)
167 )
166 )
168
167
169 old_gname = None
168 old_gname = None
170 if edit:
169 if edit:
171 old_gname = RepoGroup.get(old_data.get('group_id')).group_name
170 old_gname = RepoGroup.get(old_data.get('group_id')).group_name
172
171
173 if old_gname != group_name or not edit:
172 if old_gname != group_name or not edit:
174
173
175 # check group
174 # check group
176 gr = RepoGroup.query()\
175 gr = RepoGroup.query()\
177 .filter(RepoGroup.group_name == slug)\
176 .filter(RepoGroup.group_name == slug)\
178 .filter(RepoGroup.group_parent_id == group_parent_id)\
177 .filter(RepoGroup.group_parent_id == group_parent_id)\
179 .scalar()
178 .scalar()
180
179
181 if gr:
180 if gr:
182 msg = M(self, 'group_exists', state, group_name=slug)
181 msg = M(self, 'group_exists', state, group_name=slug)
183 raise formencode.Invalid(msg, value, state,
182 raise formencode.Invalid(msg, value, state,
184 error_dict=dict(group_name=msg)
183 error_dict=dict(group_name=msg)
185 )
184 )
186
185
187 # check for same repo
186 # check for same repo
188 repo = Repository.query()\
187 repo = Repository.query()\
189 .filter(Repository.repo_name == slug)\
188 .filter(Repository.repo_name == slug)\
190 .scalar()
189 .scalar()
191
190
192 if repo:
191 if repo:
193 msg = M(self, 'repo_exists', state, group_name=slug)
192 msg = M(self, 'repo_exists', state, group_name=slug)
194 raise formencode.Invalid(msg, value, state,
193 raise formencode.Invalid(msg, value, state,
195 error_dict=dict(group_name=msg)
194 error_dict=dict(group_name=msg)
196 )
195 )
197
196
198 return _validator
197 return _validator
199
198
200
199
201 def ValidPassword():
200 def ValidPassword():
202 class _validator(formencode.validators.FancyValidator):
201 class _validator(formencode.validators.FancyValidator):
203 messages = {
202 messages = {
204 'invalid_password':
203 'invalid_password':
205 _(u'Invalid characters (non-ascii) in password')
204 _(u'Invalid characters (non-ascii) in password')
206 }
205 }
207
206
208 def validate_python(self, value, state):
207 def validate_python(self, value, state):
209 try:
208 try:
210 (value or '').decode('ascii')
209 (value or '').decode('ascii')
211 except UnicodeError:
210 except UnicodeError:
212 msg = M(self, 'invalid_password', state)
211 msg = M(self, 'invalid_password', state)
213 raise formencode.Invalid(msg, value, state,)
212 raise formencode.Invalid(msg, value, state,)
214 return _validator
213 return _validator
215
214
216
215
217 def ValidPasswordsMatch():
216 def ValidPasswordsMatch():
218 class _validator(formencode.validators.FancyValidator):
217 class _validator(formencode.validators.FancyValidator):
219 messages = {
218 messages = {
220 'password_mismatch': _(u'Passwords do not match'),
219 'password_mismatch': _(u'Passwords do not match'),
221 }
220 }
222
221
223 def validate_python(self, value, state):
222 def validate_python(self, value, state):
224
223
225 pass_val = value.get('password') or value.get('new_password')
224 pass_val = value.get('password') or value.get('new_password')
226 if pass_val != value['password_confirmation']:
225 if pass_val != value['password_confirmation']:
227 msg = M(self, 'password_mismatch', state)
226 msg = M(self, 'password_mismatch', state)
228 raise formencode.Invalid(msg, value, state,
227 raise formencode.Invalid(msg, value, state,
229 error_dict=dict(password_confirmation=msg)
228 error_dict=dict(password_confirmation=msg)
230 )
229 )
231 return _validator
230 return _validator
232
231
233
232
234 def ValidAuth():
233 def ValidAuth():
235 class _validator(formencode.validators.FancyValidator):
234 class _validator(formencode.validators.FancyValidator):
236 messages = {
235 messages = {
237 'invalid_password': _(u'invalid password'),
236 'invalid_password': _(u'invalid password'),
238 'invalid_username': _(u'invalid user name'),
237 'invalid_username': _(u'invalid user name'),
239 'disabled_account': _(u'Your account is disabled')
238 'disabled_account': _(u'Your account is disabled')
240 }
239 }
241
240
242 def validate_python(self, value, state):
241 def validate_python(self, value, state):
243 from rhodecode.lib.auth import authenticate
242 from rhodecode.lib.auth import authenticate
244
243
245 password = value['password']
244 password = value['password']
246 username = value['username']
245 username = value['username']
247
246
248 if not authenticate(username, password):
247 if not authenticate(username, password):
249 user = User.get_by_username(username)
248 user = User.get_by_username(username)
250 if user and user.active is False:
249 if user and user.active is False:
251 log.warning('user %s is disabled' % username)
250 log.warning('user %s is disabled' % username)
252 msg = M(self, 'disabled_account', state)
251 msg = M(self, 'disabled_account', state)
253 raise formencode.Invalid(msg, value, state,
252 raise formencode.Invalid(msg, value, state,
254 error_dict=dict(username=msg)
253 error_dict=dict(username=msg)
255 )
254 )
256 else:
255 else:
257 log.warning('user %s failed to authenticate' % username)
256 log.warning('user %s failed to authenticate' % username)
258 msg = M(self, 'invalid_username', state)
257 msg = M(self, 'invalid_username', state)
259 msg2 = M(self, 'invalid_password', state)
258 msg2 = M(self, 'invalid_password', state)
260 raise formencode.Invalid(msg, value, state,
259 raise formencode.Invalid(msg, value, state,
261 error_dict=dict(username=msg, password=msg2)
260 error_dict=dict(username=msg, password=msg2)
262 )
261 )
263 return _validator
262 return _validator
264
263
265
264
266 def ValidAuthToken():
265 def ValidAuthToken():
267 class _validator(formencode.validators.FancyValidator):
266 class _validator(formencode.validators.FancyValidator):
268 messages = {
267 messages = {
269 'invalid_token': _(u'Token mismatch')
268 'invalid_token': _(u'Token mismatch')
270 }
269 }
271
270
272 def validate_python(self, value, state):
271 def validate_python(self, value, state):
273 if value != authentication_token():
272 if value != authentication_token():
274 msg = M(self, 'invalid_token', state)
273 msg = M(self, 'invalid_token', state)
275 raise formencode.Invalid(msg, value, state)
274 raise formencode.Invalid(msg, value, state)
276 return _validator
275 return _validator
277
276
278
277
279 def ValidRepoName(edit=False, old_data={}):
278 def ValidRepoName(edit=False, old_data={}):
280 class _validator(formencode.validators.FancyValidator):
279 class _validator(formencode.validators.FancyValidator):
281 messages = {
280 messages = {
282 'invalid_repo_name':
281 'invalid_repo_name':
283 _(u'Repository name %(repo)s is disallowed'),
282 _(u'Repository name %(repo)s is disallowed'),
284 'repository_exists':
283 'repository_exists':
285 _(u'Repository named %(repo)s already exists'),
284 _(u'Repository named %(repo)s already exists'),
286 'repository_in_group_exists': _(u'Repository "%(repo)s" already '
285 'repository_in_group_exists': _(u'Repository "%(repo)s" already '
287 'exists in group "%(group)s"'),
286 'exists in group "%(group)s"'),
288 'same_group_exists': _(u'Repositories group with name "%(repo)s" '
287 'same_group_exists': _(u'Repositories group with name "%(repo)s" '
289 'already exists')
288 'already exists')
290 }
289 }
291
290
292 def _to_python(self, value, state):
291 def _to_python(self, value, state):
293 repo_name = repo_name_slug(value.get('repo_name', ''))
292 repo_name = repo_name_slug(value.get('repo_name', ''))
294 repo_group = value.get('repo_group')
293 repo_group = value.get('repo_group')
295 if repo_group:
294 if repo_group:
296 gr = RepoGroup.get(repo_group)
295 gr = RepoGroup.get(repo_group)
297 group_path = gr.full_path
296 group_path = gr.full_path
298 group_name = gr.group_name
297 group_name = gr.group_name
299 # value needs to be aware of group name in order to check
298 # value needs to be aware of group name in order to check
300 # db key This is an actual just the name to store in the
299 # db key This is an actual just the name to store in the
301 # database
300 # database
302 repo_name_full = group_path + RepoGroup.url_sep() + repo_name
301 repo_name_full = group_path + RepoGroup.url_sep() + repo_name
303 else:
302 else:
304 group_name = group_path = ''
303 group_name = group_path = ''
305 repo_name_full = repo_name
304 repo_name_full = repo_name
306
305
307 value['repo_name'] = repo_name
306 value['repo_name'] = repo_name
308 value['repo_name_full'] = repo_name_full
307 value['repo_name_full'] = repo_name_full
309 value['group_path'] = group_path
308 value['group_path'] = group_path
310 value['group_name'] = group_name
309 value['group_name'] = group_name
311 return value
310 return value
312
311
313 def validate_python(self, value, state):
312 def validate_python(self, value, state):
314
313
315 repo_name = value.get('repo_name')
314 repo_name = value.get('repo_name')
316 repo_name_full = value.get('repo_name_full')
315 repo_name_full = value.get('repo_name_full')
317 group_path = value.get('group_path')
316 group_path = value.get('group_path')
318 group_name = value.get('group_name')
317 group_name = value.get('group_name')
319
318
320 if repo_name in [ADMIN_PREFIX, '']:
319 if repo_name in [ADMIN_PREFIX, '']:
321 msg = M(self, 'invalid_repo_name', state, repo=repo_name)
320 msg = M(self, 'invalid_repo_name', state, repo=repo_name)
322 raise formencode.Invalid(msg, value, state,
321 raise formencode.Invalid(msg, value, state,
323 error_dict=dict(repo_name=msg)
322 error_dict=dict(repo_name=msg)
324 )
323 )
325
324
326 rename = old_data.get('repo_name') != repo_name_full
325 rename = old_data.get('repo_name') != repo_name_full
327 create = not edit
326 create = not edit
328 if rename or create:
327 if rename or create:
329
328
330 if group_path != '':
329 if group_path != '':
331 if Repository.get_by_repo_name(repo_name_full):
330 if Repository.get_by_repo_name(repo_name_full):
332 msg = M(self, 'repository_in_group_exists', state,
331 msg = M(self, 'repository_in_group_exists', state,
333 repo=repo_name, group=group_name)
332 repo=repo_name, group=group_name)
334 raise formencode.Invalid(msg, value, state,
333 raise formencode.Invalid(msg, value, state,
335 error_dict=dict(repo_name=msg)
334 error_dict=dict(repo_name=msg)
336 )
335 )
337 elif RepoGroup.get_by_group_name(repo_name_full):
336 elif RepoGroup.get_by_group_name(repo_name_full):
338 msg = M(self, 'same_group_exists', state,
337 msg = M(self, 'same_group_exists', state,
339 repo=repo_name)
338 repo=repo_name)
340 raise formencode.Invalid(msg, value, state,
339 raise formencode.Invalid(msg, value, state,
341 error_dict=dict(repo_name=msg)
340 error_dict=dict(repo_name=msg)
342 )
341 )
343
342
344 elif Repository.get_by_repo_name(repo_name_full):
343 elif Repository.get_by_repo_name(repo_name_full):
345 msg = M(self, 'repository_exists', state,
344 msg = M(self, 'repository_exists', state,
346 repo=repo_name)
345 repo=repo_name)
347 raise formencode.Invalid(msg, value, state,
346 raise formencode.Invalid(msg, value, state,
348 error_dict=dict(repo_name=msg)
347 error_dict=dict(repo_name=msg)
349 )
348 )
350 return value
349 return value
351 return _validator
350 return _validator
352
351
353
352
354 def ValidForkName(*args, **kwargs):
353 def ValidForkName(*args, **kwargs):
355 return ValidRepoName(*args, **kwargs)
354 return ValidRepoName(*args, **kwargs)
356
355
357
356
358 def SlugifyName():
357 def SlugifyName():
359 class _validator(formencode.validators.FancyValidator):
358 class _validator(formencode.validators.FancyValidator):
360
359
361 def _to_python(self, value, state):
360 def _to_python(self, value, state):
362 return repo_name_slug(value)
361 return repo_name_slug(value)
363
362
364 def validate_python(self, value, state):
363 def validate_python(self, value, state):
365 pass
364 pass
366
365
367 return _validator
366 return _validator
368
367
369
368
370 def ValidCloneUri():
369 def ValidCloneUri():
371 from rhodecode.lib.utils import make_ui
370 from rhodecode.lib.utils import make_ui
372
371
373 def url_handler(repo_type, url, ui=None):
372 def url_handler(repo_type, url, ui=None):
374 if repo_type == 'hg':
373 if repo_type == 'hg':
375 from rhodecode.lib.vcs.backends.hg.repository import MercurialRepository
374 from rhodecode.lib.vcs.backends.hg.repository import MercurialRepository
376 from mercurial.httppeer import httppeer
375 from mercurial.httppeer import httppeer
377 if url.startswith('http'):
376 if url.startswith('http'):
378 ## initially check if it's at least the proper URL
377 ## initially check if it's at least the proper URL
379 ## or does it pass basic auth
378 ## or does it pass basic auth
380 MercurialRepository._check_url(url)
379 MercurialRepository._check_url(url)
381 httppeer(make_ui('db'), url)._capabilities()
380 httppeer(make_ui('db'), url)._capabilities()
382 elif url.startswith('svn+http'):
381 elif url.startswith('svn+http'):
383 from hgsubversion.svnrepo import svnremoterepo
382 from hgsubversion.svnrepo import svnremoterepo
384 svnremoterepo(make_ui('db'), url).capabilities
383 svnremoterepo(make_ui('db'), url).capabilities
385 elif url.startswith('git+http'):
384 elif url.startswith('git+http'):
386 raise NotImplementedError()
385 raise NotImplementedError()
387
386
388 elif repo_type == 'git':
387 elif repo_type == 'git':
389 from rhodecode.lib.vcs.backends.git.repository import GitRepository
388 from rhodecode.lib.vcs.backends.git.repository import GitRepository
390 if url.startswith('http'):
389 if url.startswith('http'):
391 ## initially check if it's at least the proper URL
390 ## initially check if it's at least the proper URL
392 ## or does it pass basic auth
391 ## or does it pass basic auth
393 GitRepository._check_url(url)
392 GitRepository._check_url(url)
394 elif url.startswith('svn+http'):
393 elif url.startswith('svn+http'):
395 raise NotImplementedError()
394 raise NotImplementedError()
396 elif url.startswith('hg+http'):
395 elif url.startswith('hg+http'):
397 raise NotImplementedError()
396 raise NotImplementedError()
398
397
399 class _validator(formencode.validators.FancyValidator):
398 class _validator(formencode.validators.FancyValidator):
400 messages = {
399 messages = {
401 'clone_uri': _(u'invalid clone url'),
400 'clone_uri': _(u'invalid clone url'),
402 'invalid_clone_uri': _(u'Invalid clone url, provide a '
401 'invalid_clone_uri': _(u'Invalid clone url, provide a '
403 'valid clone http(s)/svn+http(s) url')
402 'valid clone http(s)/svn+http(s) url')
404 }
403 }
405
404
406 def validate_python(self, value, state):
405 def validate_python(self, value, state):
407 repo_type = value.get('repo_type')
406 repo_type = value.get('repo_type')
408 url = value.get('clone_uri')
407 url = value.get('clone_uri')
409
408
410 if not url:
409 if not url:
411 pass
410 pass
412 else:
411 else:
413 try:
412 try:
414 url_handler(repo_type, url, make_ui('db'))
413 url_handler(repo_type, url, make_ui('db'))
415 except Exception:
414 except Exception:
416 log.exception('Url validation failed')
415 log.exception('Url validation failed')
417 msg = M(self, 'clone_uri')
416 msg = M(self, 'clone_uri')
418 raise formencode.Invalid(msg, value, state,
417 raise formencode.Invalid(msg, value, state,
419 error_dict=dict(clone_uri=msg)
418 error_dict=dict(clone_uri=msg)
420 )
419 )
421 return _validator
420 return _validator
422
421
423
422
424 def ValidForkType(old_data={}):
423 def ValidForkType(old_data={}):
425 class _validator(formencode.validators.FancyValidator):
424 class _validator(formencode.validators.FancyValidator):
426 messages = {
425 messages = {
427 'invalid_fork_type': _(u'Fork have to be the same type as parent')
426 'invalid_fork_type': _(u'Fork have to be the same type as parent')
428 }
427 }
429
428
430 def validate_python(self, value, state):
429 def validate_python(self, value, state):
431 if old_data['repo_type'] != value:
430 if old_data['repo_type'] != value:
432 msg = M(self, 'invalid_fork_type', state)
431 msg = M(self, 'invalid_fork_type', state)
433 raise formencode.Invalid(msg, value, state,
432 raise formencode.Invalid(msg, value, state,
434 error_dict=dict(repo_type=msg)
433 error_dict=dict(repo_type=msg)
435 )
434 )
436 return _validator
435 return _validator
437
436
438
437
439 def ValidPerms(type_='repo'):
438 def ValidPerms(type_='repo'):
440 if type_ == 'group':
439 if type_ == 'group':
441 EMPTY_PERM = 'group.none'
440 EMPTY_PERM = 'group.none'
442 elif type_ == 'repo':
441 elif type_ == 'repo':
443 EMPTY_PERM = 'repository.none'
442 EMPTY_PERM = 'repository.none'
444
443
445 class _validator(formencode.validators.FancyValidator):
444 class _validator(formencode.validators.FancyValidator):
446 messages = {
445 messages = {
447 'perm_new_member_name':
446 'perm_new_member_name':
448 _(u'This username or users group name is not valid')
447 _(u'This username or users group name is not valid')
449 }
448 }
450
449
451 def to_python(self, value, state):
450 def to_python(self, value, state):
452 perms_update = []
451 perms_update = []
453 perms_new = []
452 perms_new = []
454 # build a list of permission to update and new permission to create
453 # build a list of permission to update and new permission to create
455 for k, v in value.items():
454 for k, v in value.items():
456 # means new added member to permissions
455 # means new added member to permissions
457 if k.startswith('perm_new_member'):
456 if k.startswith('perm_new_member'):
458 new_perm = value.get('perm_new_member', False)
457 new_perm = value.get('perm_new_member', False)
459 new_member = value.get('perm_new_member_name', False)
458 new_member = value.get('perm_new_member_name', False)
460 new_type = value.get('perm_new_member_type')
459 new_type = value.get('perm_new_member_type')
461
460
462 if new_member and new_perm:
461 if new_member and new_perm:
463 if (new_member, new_perm, new_type) not in perms_new:
462 if (new_member, new_perm, new_type) not in perms_new:
464 perms_new.append((new_member, new_perm, new_type))
463 perms_new.append((new_member, new_perm, new_type))
465 elif k.startswith('u_perm_') or k.startswith('g_perm_'):
464 elif k.startswith('u_perm_') or k.startswith('g_perm_'):
466 member = k[7:]
465 member = k[7:]
467 t = {'u': 'user',
466 t = {'u': 'user',
468 'g': 'users_group'
467 'g': 'users_group'
469 }[k[0]]
468 }[k[0]]
470 if member == 'default':
469 if member == 'default':
471 if value.get('private'):
470 if value.get('private'):
472 # set none for default when updating to
471 # set none for default when updating to
473 # private repo
472 # private repo
474 v = EMPTY_PERM
473 v = EMPTY_PERM
475 perms_update.append((member, v, t))
474 perms_update.append((member, v, t))
476
475
477 value['perms_updates'] = perms_update
476 value['perms_updates'] = perms_update
478 value['perms_new'] = perms_new
477 value['perms_new'] = perms_new
479
478
480 # update permissions
479 # update permissions
481 for k, v, t in perms_new:
480 for k, v, t in perms_new:
482 try:
481 try:
483 if t is 'user':
482 if t is 'user':
484 self.user_db = User.query()\
483 self.user_db = User.query()\
485 .filter(User.active == True)\
484 .filter(User.active == True)\
486 .filter(User.username == k).one()
485 .filter(User.username == k).one()
487 if t is 'users_group':
486 if t is 'users_group':
488 self.user_db = UsersGroup.query()\
487 self.user_db = UsersGroup.query()\
489 .filter(UsersGroup.users_group_active == True)\
488 .filter(UsersGroup.users_group_active == True)\
490 .filter(UsersGroup.users_group_name == k).one()
489 .filter(UsersGroup.users_group_name == k).one()
491
490
492 except Exception:
491 except Exception:
493 log.exception('Updated permission failed')
492 log.exception('Updated permission failed')
494 msg = M(self, 'perm_new_member_type', state)
493 msg = M(self, 'perm_new_member_type', state)
495 raise formencode.Invalid(msg, value, state,
494 raise formencode.Invalid(msg, value, state,
496 error_dict=dict(perm_new_member_name=msg)
495 error_dict=dict(perm_new_member_name=msg)
497 )
496 )
498 return value
497 return value
499 return _validator
498 return _validator
500
499
501
500
502 def ValidSettings():
501 def ValidSettings():
503 class _validator(formencode.validators.FancyValidator):
502 class _validator(formencode.validators.FancyValidator):
504 def _to_python(self, value, state):
503 def _to_python(self, value, state):
505 # settings form can't edit user
504 # settings form can't edit user
506 if 'user' in value:
505 if 'user' in value:
507 del value['user']
506 del value['user']
508 return value
507 return value
509
508
510 def validate_python(self, value, state):
509 def validate_python(self, value, state):
511 pass
510 pass
512 return _validator
511 return _validator
513
512
514
513
515 def ValidPath():
514 def ValidPath():
516 class _validator(formencode.validators.FancyValidator):
515 class _validator(formencode.validators.FancyValidator):
517 messages = {
516 messages = {
518 'invalid_path': _(u'This is not a valid path')
517 'invalid_path': _(u'This is not a valid path')
519 }
518 }
520
519
521 def validate_python(self, value, state):
520 def validate_python(self, value, state):
522 if not os.path.isdir(value):
521 if not os.path.isdir(value):
523 msg = M(self, 'invalid_path', state)
522 msg = M(self, 'invalid_path', state)
524 raise formencode.Invalid(msg, value, state,
523 raise formencode.Invalid(msg, value, state,
525 error_dict=dict(paths_root_path=msg)
524 error_dict=dict(paths_root_path=msg)
526 )
525 )
527 return _validator
526 return _validator
528
527
529
528
530 def UniqSystemEmail(old_data={}):
529 def UniqSystemEmail(old_data={}):
531 class _validator(formencode.validators.FancyValidator):
530 class _validator(formencode.validators.FancyValidator):
532 messages = {
531 messages = {
533 'email_taken': _(u'This e-mail address is already taken')
532 'email_taken': _(u'This e-mail address is already taken')
534 }
533 }
535
534
536 def _to_python(self, value, state):
535 def _to_python(self, value, state):
537 return value.lower()
536 return value.lower()
538
537
539 def validate_python(self, value, state):
538 def validate_python(self, value, state):
540 if (old_data.get('email') or '').lower() != value:
539 if (old_data.get('email') or '').lower() != value:
541 user = User.get_by_email(value, case_insensitive=True)
540 user = User.get_by_email(value, case_insensitive=True)
542 if user:
541 if user:
543 msg = M(self, 'email_taken', state)
542 msg = M(self, 'email_taken', state)
544 raise formencode.Invalid(msg, value, state,
543 raise formencode.Invalid(msg, value, state,
545 error_dict=dict(email=msg)
544 error_dict=dict(email=msg)
546 )
545 )
547 return _validator
546 return _validator
548
547
549
548
550 def ValidSystemEmail():
549 def ValidSystemEmail():
551 class _validator(formencode.validators.FancyValidator):
550 class _validator(formencode.validators.FancyValidator):
552 messages = {
551 messages = {
553 'non_existing_email': _(u'e-mail "%(email)s" does not exist.')
552 'non_existing_email': _(u'e-mail "%(email)s" does not exist.')
554 }
553 }
555
554
556 def _to_python(self, value, state):
555 def _to_python(self, value, state):
557 return value.lower()
556 return value.lower()
558
557
559 def validate_python(self, value, state):
558 def validate_python(self, value, state):
560 user = User.get_by_email(value, case_insensitive=True)
559 user = User.get_by_email(value, case_insensitive=True)
561 if user is None:
560 if user is None:
562 msg = M(self, 'non_existing_email', state, email=value)
561 msg = M(self, 'non_existing_email', state, email=value)
563 raise formencode.Invalid(msg, value, state,
562 raise formencode.Invalid(msg, value, state,
564 error_dict=dict(email=msg)
563 error_dict=dict(email=msg)
565 )
564 )
566
565
567 return _validator
566 return _validator
568
567
569
568
570 def LdapLibValidator():
569 def LdapLibValidator():
571 class _validator(formencode.validators.FancyValidator):
570 class _validator(formencode.validators.FancyValidator):
572 messages = {
571 messages = {
573
572
574 }
573 }
575
574
576 def validate_python(self, value, state):
575 def validate_python(self, value, state):
577 try:
576 try:
578 import ldap
577 import ldap
579 ldap # pyflakes silence !
578 ldap # pyflakes silence !
580 except ImportError:
579 except ImportError:
581 raise LdapImportError()
580 raise LdapImportError()
582
581
583 return _validator
582 return _validator
584
583
585
584
586 def AttrLoginValidator():
585 def AttrLoginValidator():
587 class _validator(formencode.validators.FancyValidator):
586 class _validator(formencode.validators.FancyValidator):
588 messages = {
587 messages = {
589 'invalid_cn':
588 'invalid_cn':
590 _(u'The LDAP Login attribute of the CN must be specified - '
589 _(u'The LDAP Login attribute of the CN must be specified - '
591 'this is the name of the attribute that is equivalent '
590 'this is the name of the attribute that is equivalent '
592 'to "username"')
591 'to "username"')
593 }
592 }
594
593
595 def validate_python(self, value, state):
594 def validate_python(self, value, state):
596 if not value or not isinstance(value, (str, unicode)):
595 if not value or not isinstance(value, (str, unicode)):
597 msg = M(self, 'invalid_cn', state)
596 msg = M(self, 'invalid_cn', state)
598 raise formencode.Invalid(msg, value, state,
597 raise formencode.Invalid(msg, value, state,
599 error_dict=dict(ldap_attr_login=msg)
598 error_dict=dict(ldap_attr_login=msg)
600 )
599 )
601
600
602 return _validator
601 return _validator
General Comments 0
You need to be logged in to leave comments. Login now