##// END OF EJS Templates
pull requests: make title optional - generate one automatically
Mads Kiilerich -
r4058:a2218bdb default
parent child Browse files
Show More
@@ -1,550 +1,552 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 import formencode
28
28
29 from webob.exc import HTTPNotFound, HTTPForbidden
29 from webob.exc import HTTPNotFound, HTTPForbidden
30 from collections import defaultdict
30 from collections import defaultdict
31 from itertools import groupby
31 from itertools import groupby
32
32
33 from pylons import request, tmpl_context as c, url
33 from pylons import request, tmpl_context as c, url
34 from pylons.controllers.util import redirect
34 from pylons.controllers.util import redirect
35 from pylons.i18n.translation import _
35 from pylons.i18n.translation import _
36
36
37 from rhodecode.lib.compat import json
37 from rhodecode.lib.compat import json
38 from rhodecode.lib.base import BaseRepoController, render
38 from rhodecode.lib.base import BaseRepoController, render
39 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator,\
39 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator,\
40 NotAnonymous
40 NotAnonymous
41 from rhodecode.lib.helpers import Page
41 from rhodecode.lib.helpers import Page
42 from rhodecode.lib import helpers as h
42 from rhodecode.lib import helpers as h
43 from rhodecode.lib import diffs
43 from rhodecode.lib import diffs
44 from rhodecode.lib.utils import action_logger, jsonify
44 from rhodecode.lib.utils import action_logger, jsonify
45 from rhodecode.lib.vcs.utils import safe_str
45 from rhodecode.lib.vcs.utils import safe_str
46 from rhodecode.lib.vcs.exceptions import EmptyRepositoryError
46 from rhodecode.lib.vcs.exceptions import EmptyRepositoryError
47 from rhodecode.lib.diffs import LimitedDiffContainer
47 from rhodecode.lib.diffs import LimitedDiffContainer
48 from rhodecode.model.db import PullRequest, ChangesetStatus, ChangesetComment
48 from rhodecode.model.db import PullRequest, ChangesetStatus, ChangesetComment
49 from rhodecode.model.pull_request import PullRequestModel
49 from rhodecode.model.pull_request import PullRequestModel
50 from rhodecode.model.meta import Session
50 from rhodecode.model.meta import Session
51 from rhodecode.model.repo import RepoModel
51 from rhodecode.model.repo import RepoModel
52 from rhodecode.model.comment import ChangesetCommentsModel
52 from rhodecode.model.comment import ChangesetCommentsModel
53 from rhodecode.model.changeset_status import ChangesetStatusModel
53 from rhodecode.model.changeset_status import ChangesetStatusModel
54 from rhodecode.model.forms import PullRequestForm
54 from rhodecode.model.forms import PullRequestForm
55 from rhodecode.lib.utils2 import safe_int
55 from rhodecode.lib.utils2 import safe_int
56
56
57 log = logging.getLogger(__name__)
57 log = logging.getLogger(__name__)
58
58
59
59
60 class PullrequestsController(BaseRepoController):
60 class PullrequestsController(BaseRepoController):
61
61
62 def __before__(self):
62 def __before__(self):
63 super(PullrequestsController, self).__before__()
63 super(PullrequestsController, self).__before__()
64 repo_model = RepoModel()
64 repo_model = RepoModel()
65 c.users_array = repo_model.get_users_js()
65 c.users_array = repo_model.get_users_js()
66 c.users_groups_array = repo_model.get_users_groups_js()
66 c.users_groups_array = repo_model.get_users_groups_js()
67
67
68 def _get_repo_refs(self, repo, rev=None, branch=None, branch_rev=None):
68 def _get_repo_refs(self, repo, rev=None, branch=None, branch_rev=None):
69 """return a structure with repo's interesting changesets, suitable for
69 """return a structure with repo's interesting changesets, suitable for
70 the selectors in pullrequest.html
70 the selectors in pullrequest.html
71
71
72 rev: a revision that must be in the list somehow and selected by default
72 rev: a revision that must be in the list somehow and selected by default
73 branch: a branch that must be in the list and selected by default - even if closed
73 branch: a branch that must be in the list and selected by default - even if closed
74 branch_rev: a revision of which peers should be preferred and available."""
74 branch_rev: a revision of which peers should be preferred and available."""
75 # list named branches that has been merged to this named branch - it should probably merge back
75 # list named branches that has been merged to this named branch - it should probably merge back
76 peers = []
76 peers = []
77
77
78 if rev:
78 if rev:
79 rev = safe_str(rev)
79 rev = safe_str(rev)
80
80
81 if branch:
81 if branch:
82 branch = safe_str(branch)
82 branch = safe_str(branch)
83
83
84 if branch_rev:
84 if branch_rev:
85 branch_rev = safe_str(branch_rev)
85 branch_rev = safe_str(branch_rev)
86 # not restricting to merge() would also get branch point and be better
86 # not restricting to merge() would also get branch point and be better
87 # (especially because it would get the branch point) ... but is currently too expensive
87 # (especially because it would get the branch point) ... but is currently too expensive
88 otherbranches = {}
88 otherbranches = {}
89 for i in repo._repo.revs(
89 for i in repo._repo.revs(
90 "sort(parents(branch(id(%s)) and merge()) - branch(id(%s)))",
90 "sort(parents(branch(id(%s)) and merge()) - branch(id(%s)))",
91 branch_rev, branch_rev):
91 branch_rev, branch_rev):
92 cs = repo.get_changeset(i)
92 cs = repo.get_changeset(i)
93 otherbranches[cs.branch] = cs.raw_id
93 otherbranches[cs.branch] = cs.raw_id
94 for abranch, node in otherbranches.iteritems():
94 for abranch, node in otherbranches.iteritems():
95 selected = 'branch:%s:%s' % (abranch, node)
95 selected = 'branch:%s:%s' % (abranch, node)
96 peers.append((selected, abranch))
96 peers.append((selected, abranch))
97
97
98 selected = None
98 selected = None
99
99
100 branches = []
100 branches = []
101 for abranch, branchrev in repo.branches.iteritems():
101 for abranch, branchrev in repo.branches.iteritems():
102 n = 'branch:%s:%s' % (abranch, branchrev)
102 n = 'branch:%s:%s' % (abranch, branchrev)
103 branches.append((n, abranch))
103 branches.append((n, abranch))
104 if rev == branchrev:
104 if rev == branchrev:
105 selected = n
105 selected = n
106 if branch == abranch:
106 if branch == abranch:
107 selected = n
107 selected = n
108 branch = None
108 branch = None
109 if branch: # branch not in list - it is probably closed
109 if branch: # branch not in list - it is probably closed
110 revs = repo._repo.revs('max(branch(%s))', branch)
110 revs = repo._repo.revs('max(branch(%s))', branch)
111 if revs:
111 if revs:
112 cs = repo.get_changeset(revs[0])
112 cs = repo.get_changeset(revs[0])
113 selected = 'branch:%s:%s' % (branch, cs.raw_id)
113 selected = 'branch:%s:%s' % (branch, cs.raw_id)
114 branches.append((selected, branch))
114 branches.append((selected, branch))
115
115
116 bookmarks = []
116 bookmarks = []
117 for bookmark, bookmarkrev in repo.bookmarks.iteritems():
117 for bookmark, bookmarkrev in repo.bookmarks.iteritems():
118 n = 'book:%s:%s' % (bookmark, bookmarkrev)
118 n = 'book:%s:%s' % (bookmark, bookmarkrev)
119 bookmarks.append((n, bookmark))
119 bookmarks.append((n, bookmark))
120 if rev == bookmarkrev:
120 if rev == bookmarkrev:
121 selected = n
121 selected = n
122
122
123 tags = []
123 tags = []
124 for tag, tagrev in repo.tags.iteritems():
124 for tag, tagrev in repo.tags.iteritems():
125 n = 'tag:%s:%s' % (tag, tagrev)
125 n = 'tag:%s:%s' % (tag, tagrev)
126 tags.append((n, tag))
126 tags.append((n, tag))
127 if rev == tagrev and tag != 'tip': # tip is not a real tag - and its branch is better
127 if rev == tagrev and tag != 'tip': # tip is not a real tag - and its branch is better
128 selected = n
128 selected = n
129
129
130 # prio 1: rev was selected as existing entry above
130 # prio 1: rev was selected as existing entry above
131
131
132 # prio 2: create special entry for rev; rev _must_ be used
132 # prio 2: create special entry for rev; rev _must_ be used
133 specials = []
133 specials = []
134 if rev and selected is None:
134 if rev and selected is None:
135 selected = 'rev:%s:%s' % (rev, rev)
135 selected = 'rev:%s:%s' % (rev, rev)
136 specials = [(selected, '%s: %s' % (_("Changeset"), rev[:12]))]
136 specials = [(selected, '%s: %s' % (_("Changeset"), rev[:12]))]
137
137
138 # prio 3: most recent peer branch
138 # prio 3: most recent peer branch
139 if peers and not selected:
139 if peers and not selected:
140 selected = peers[0][0][0]
140 selected = peers[0][0][0]
141
141
142 # prio 4: tip revision
142 # prio 4: tip revision
143 if not selected:
143 if not selected:
144 selected = 'tag:tip:%s' % repo.tags['tip']
144 selected = 'tag:tip:%s' % repo.tags['tip']
145
145
146 groups = [(specials, _("Special")),
146 groups = [(specials, _("Special")),
147 (peers, _("Peer branches")),
147 (peers, _("Peer branches")),
148 (bookmarks, _("Bookmarks")),
148 (bookmarks, _("Bookmarks")),
149 (branches, _("Branches")),
149 (branches, _("Branches")),
150 (tags, _("Tags")),
150 (tags, _("Tags")),
151 ]
151 ]
152 return [g for g in groups if g[0]], selected
152 return [g for g in groups if g[0]], selected
153
153
154 def _get_is_allowed_change_status(self, pull_request):
154 def _get_is_allowed_change_status(self, pull_request):
155 owner = self.rhodecode_user.user_id == pull_request.user_id
155 owner = self.rhodecode_user.user_id == pull_request.user_id
156 reviewer = self.rhodecode_user.user_id in [x.user_id for x in
156 reviewer = self.rhodecode_user.user_id in [x.user_id for x in
157 pull_request.reviewers]
157 pull_request.reviewers]
158 return (self.rhodecode_user.admin or owner or reviewer)
158 return (self.rhodecode_user.admin or owner or reviewer)
159
159
160 def _load_compare_data(self, pull_request, enable_comments=True):
160 def _load_compare_data(self, pull_request, enable_comments=True):
161 """
161 """
162 Load context data needed for generating compare diff
162 Load context data needed for generating compare diff
163
163
164 :param pull_request:
164 :param pull_request:
165 """
165 """
166 org_repo = pull_request.org_repo
166 org_repo = pull_request.org_repo
167 (org_ref_type,
167 (org_ref_type,
168 org_ref_name,
168 org_ref_name,
169 org_ref_rev) = pull_request.org_ref.split(':')
169 org_ref_rev) = pull_request.org_ref.split(':')
170
170
171 other_repo = org_repo
171 other_repo = org_repo
172 (other_ref_type,
172 (other_ref_type,
173 other_ref_name,
173 other_ref_name,
174 other_ref_rev) = pull_request.other_ref.split(':')
174 other_ref_rev) = pull_request.other_ref.split(':')
175
175
176 # despite opening revisions for bookmarks/branches/tags, we always
176 # despite opening revisions for bookmarks/branches/tags, we always
177 # convert this to rev to prevent changes after bookmark or branch change
177 # convert this to rev to prevent changes after bookmark or branch change
178 org_ref = ('rev', org_ref_rev)
178 org_ref = ('rev', org_ref_rev)
179 other_ref = ('rev', other_ref_rev)
179 other_ref = ('rev', other_ref_rev)
180
180
181 c.org_repo = org_repo
181 c.org_repo = org_repo
182 c.other_repo = other_repo
182 c.other_repo = other_repo
183
183
184 c.fulldiff = fulldiff = request.GET.get('fulldiff')
184 c.fulldiff = fulldiff = request.GET.get('fulldiff')
185
185
186 c.cs_ranges = [org_repo.get_changeset(x) for x in pull_request.revisions]
186 c.cs_ranges = [org_repo.get_changeset(x) for x in pull_request.revisions]
187
187
188 c.statuses = org_repo.statuses([x.raw_id for x in c.cs_ranges])
188 c.statuses = org_repo.statuses([x.raw_id for x in c.cs_ranges])
189
189
190 c.org_ref = org_ref[1]
190 c.org_ref = org_ref[1]
191 c.org_ref_type = org_ref[0]
191 c.org_ref_type = org_ref[0]
192 c.other_ref = other_ref[1]
192 c.other_ref = other_ref[1]
193 c.other_ref_type = other_ref[0]
193 c.other_ref_type = other_ref[0]
194
194
195 diff_limit = self.cut_off_limit if not fulldiff else None
195 diff_limit = self.cut_off_limit if not fulldiff else None
196
196
197 # we swap org/other ref since we run a simple diff on one repo
197 # we swap org/other ref since we run a simple diff on one repo
198 log.debug('running diff between %s and %s in %s'
198 log.debug('running diff between %s and %s in %s'
199 % (other_ref, org_ref, org_repo.scm_instance.path))
199 % (other_ref, org_ref, org_repo.scm_instance.path))
200 txtdiff = org_repo.scm_instance.get_diff(rev1=safe_str(other_ref[1]), rev2=safe_str(org_ref[1]))
200 txtdiff = org_repo.scm_instance.get_diff(rev1=safe_str(other_ref[1]), rev2=safe_str(org_ref[1]))
201
201
202 diff_processor = diffs.DiffProcessor(txtdiff or '', format='gitdiff',
202 diff_processor = diffs.DiffProcessor(txtdiff or '', format='gitdiff',
203 diff_limit=diff_limit)
203 diff_limit=diff_limit)
204 _parsed = diff_processor.prepare()
204 _parsed = diff_processor.prepare()
205
205
206 c.limited_diff = False
206 c.limited_diff = False
207 if isinstance(_parsed, LimitedDiffContainer):
207 if isinstance(_parsed, LimitedDiffContainer):
208 c.limited_diff = True
208 c.limited_diff = True
209
209
210 c.files = []
210 c.files = []
211 c.changes = {}
211 c.changes = {}
212 c.lines_added = 0
212 c.lines_added = 0
213 c.lines_deleted = 0
213 c.lines_deleted = 0
214
214
215 for f in _parsed:
215 for f in _parsed:
216 st = f['stats']
216 st = f['stats']
217 c.lines_added += st['added']
217 c.lines_added += st['added']
218 c.lines_deleted += st['deleted']
218 c.lines_deleted += st['deleted']
219 fid = h.FID('', f['filename'])
219 fid = h.FID('', f['filename'])
220 c.files.append([fid, f['operation'], f['filename'], f['stats']])
220 c.files.append([fid, f['operation'], f['filename'], f['stats']])
221 htmldiff = diff_processor.as_html(enable_comments=enable_comments,
221 htmldiff = diff_processor.as_html(enable_comments=enable_comments,
222 parsed_lines=[f])
222 parsed_lines=[f])
223 c.changes[fid] = [f['operation'], f['filename'], htmldiff]
223 c.changes[fid] = [f['operation'], f['filename'], htmldiff]
224
224
225 @LoginRequired()
225 @LoginRequired()
226 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
226 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
227 'repository.admin')
227 'repository.admin')
228 def show_all(self, repo_name):
228 def show_all(self, repo_name):
229 c.from_ = request.GET.get('from_') or ''
229 c.from_ = request.GET.get('from_') or ''
230 c.closed = request.GET.get('closed') or ''
230 c.closed = request.GET.get('closed') or ''
231 c.pull_requests = PullRequestModel().get_all(repo_name, from_=c.from_, closed=c.closed)
231 c.pull_requests = PullRequestModel().get_all(repo_name, from_=c.from_, closed=c.closed)
232 c.repo_name = repo_name
232 c.repo_name = repo_name
233 p = safe_int(request.GET.get('page', 1), 1)
233 p = safe_int(request.GET.get('page', 1), 1)
234
234
235 c.pullrequests_pager = Page(c.pull_requests, page=p, items_per_page=10)
235 c.pullrequests_pager = Page(c.pull_requests, page=p, items_per_page=10)
236
236
237 c.pullrequest_data = render('/pullrequests/pullrequest_data.html')
237 c.pullrequest_data = render('/pullrequests/pullrequest_data.html')
238
238
239 if request.environ.get('HTTP_X_PARTIAL_XHR'):
239 if request.environ.get('HTTP_X_PARTIAL_XHR'):
240 return c.pullrequest_data
240 return c.pullrequest_data
241
241
242 return render('/pullrequests/pullrequest_show_all.html')
242 return render('/pullrequests/pullrequest_show_all.html')
243
243
244 @LoginRequired()
244 @LoginRequired()
245 @NotAnonymous()
245 @NotAnonymous()
246 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
246 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
247 'repository.admin')
247 'repository.admin')
248 def index(self):
248 def index(self):
249 org_repo = c.rhodecode_db_repo
249 org_repo = c.rhodecode_db_repo
250
250
251 if org_repo.scm_instance.alias != 'hg':
251 if org_repo.scm_instance.alias != 'hg':
252 log.error('Review not available for GIT REPOS')
252 log.error('Review not available for GIT REPOS')
253 raise HTTPNotFound
253 raise HTTPNotFound
254
254
255 try:
255 try:
256 org_repo.scm_instance.get_changeset()
256 org_repo.scm_instance.get_changeset()
257 except EmptyRepositoryError, e:
257 except EmptyRepositoryError, e:
258 h.flash(h.literal(_('There are no changesets yet')),
258 h.flash(h.literal(_('There are no changesets yet')),
259 category='warning')
259 category='warning')
260 redirect(url('summary_home', repo_name=org_repo.repo_name))
260 redirect(url('summary_home', repo_name=org_repo.repo_name))
261
261
262 org_rev = request.GET.get('rev_end')
262 org_rev = request.GET.get('rev_end')
263 # rev_start is not directly useful - its parent could however be used
263 # rev_start is not directly useful - its parent could however be used
264 # as default for other and thus give a simple compare view
264 # as default for other and thus give a simple compare view
265 #other_rev = request.POST.get('rev_start')
265 #other_rev = request.POST.get('rev_start')
266 branch = request.GET.get('branch')
266 branch = request.GET.get('branch')
267
267
268 c.org_repos = []
268 c.org_repos = []
269 c.org_repos.append((org_repo.repo_name, org_repo.repo_name))
269 c.org_repos.append((org_repo.repo_name, org_repo.repo_name))
270 c.default_org_repo = org_repo.repo_name
270 c.default_org_repo = org_repo.repo_name
271 c.org_refs, c.default_org_ref = self._get_repo_refs(org_repo.scm_instance, rev=org_rev, branch=branch)
271 c.org_refs, c.default_org_ref = self._get_repo_refs(org_repo.scm_instance, rev=org_rev, branch=branch)
272
272
273 c.other_repos = []
273 c.other_repos = []
274 other_repos_info = {}
274 other_repos_info = {}
275
275
276 def add_other_repo(repo, branch_rev=None):
276 def add_other_repo(repo, branch_rev=None):
277 if repo.repo_name in other_repos_info: # shouldn't happen
277 if repo.repo_name in other_repos_info: # shouldn't happen
278 return
278 return
279 c.other_repos.append((repo.repo_name, repo.repo_name))
279 c.other_repos.append((repo.repo_name, repo.repo_name))
280 other_refs, selected_other_ref = self._get_repo_refs(repo.scm_instance, branch_rev=branch_rev)
280 other_refs, selected_other_ref = self._get_repo_refs(repo.scm_instance, branch_rev=branch_rev)
281 other_repos_info[repo.repo_name] = {
281 other_repos_info[repo.repo_name] = {
282 'user': dict(user_id=repo.user.user_id,
282 'user': dict(user_id=repo.user.user_id,
283 username=repo.user.username,
283 username=repo.user.username,
284 firstname=repo.user.firstname,
284 firstname=repo.user.firstname,
285 lastname=repo.user.lastname,
285 lastname=repo.user.lastname,
286 gravatar_link=h.gravatar_url(repo.user.email, 14)),
286 gravatar_link=h.gravatar_url(repo.user.email, 14)),
287 'description': repo.description.split('\n', 1)[0],
287 'description': repo.description.split('\n', 1)[0],
288 'revs': h.select('other_ref', selected_other_ref, other_refs, class_='refs')
288 'revs': h.select('other_ref', selected_other_ref, other_refs, class_='refs')
289 }
289 }
290
290
291 # add org repo to other so we can open pull request against peer branches on itself
291 # add org repo to other so we can open pull request against peer branches on itself
292 add_other_repo(org_repo, branch_rev=org_rev)
292 add_other_repo(org_repo, branch_rev=org_rev)
293 c.default_other_repo = org_repo.repo_name
293 c.default_other_repo = org_repo.repo_name
294
294
295 # gather forks and add to this list ... even though it is rare to
295 # gather forks and add to this list ... even though it is rare to
296 # request forks to pull from their parent
296 # request forks to pull from their parent
297 for fork in org_repo.forks:
297 for fork in org_repo.forks:
298 add_other_repo(fork)
298 add_other_repo(fork)
299
299
300 # add parents of this fork also, but only if it's not empty
300 # add parents of this fork also, but only if it's not empty
301 if org_repo.parent and org_repo.parent.scm_instance.revisions:
301 if org_repo.parent and org_repo.parent.scm_instance.revisions:
302 add_other_repo(org_repo.parent)
302 add_other_repo(org_repo.parent)
303 c.default_other_repo = org_repo.parent.repo_name
303 c.default_other_repo = org_repo.parent.repo_name
304
304
305 c.default_other_repo_info = other_repos_info[c.default_other_repo]
305 c.default_other_repo_info = other_repos_info[c.default_other_repo]
306 c.other_repos_info = json.dumps(other_repos_info)
306 c.other_repos_info = json.dumps(other_repos_info)
307
307
308 return render('/pullrequests/pullrequest.html')
308 return render('/pullrequests/pullrequest.html')
309
309
310 @LoginRequired()
310 @LoginRequired()
311 @NotAnonymous()
311 @NotAnonymous()
312 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
312 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
313 'repository.admin')
313 'repository.admin')
314 def create(self, repo_name):
314 def create(self, repo_name):
315 repo = RepoModel()._get_repo(repo_name)
315 repo = RepoModel()._get_repo(repo_name)
316 try:
316 try:
317 _form = PullRequestForm(repo.repo_id)().to_python(request.POST)
317 _form = PullRequestForm(repo.repo_id)().to_python(request.POST)
318 except formencode.Invalid, errors:
318 except formencode.Invalid, errors:
319 log.error(traceback.format_exc())
319 log.error(traceback.format_exc())
320 if errors.error_dict.get('revisions'):
320 if errors.error_dict.get('revisions'):
321 msg = 'Revisions: %s' % errors.error_dict['revisions']
321 msg = 'Revisions: %s' % errors.error_dict['revisions']
322 elif errors.error_dict.get('pullrequest_title'):
322 elif errors.error_dict.get('pullrequest_title'):
323 msg = _('Pull request requires a title with min. 3 chars')
323 msg = _('Pull request requires a title with min. 3 chars')
324 else:
324 else:
325 msg = _('Error creating pull request')
325 msg = _('Error creating pull request')
326
326
327 h.flash(msg, 'error')
327 h.flash(msg, 'error')
328 return redirect(url('pullrequest_home', repo_name=repo_name))
328 return redirect(url('pullrequest_home', repo_name=repo_name))
329
329
330 org_repo = _form['org_repo']
330 org_repo = _form['org_repo']
331 org_ref = _form['org_ref'] # will end with merge_rev but have symbolic name
331 org_ref = _form['org_ref'] # will end with merge_rev but have symbolic name
332 other_repo = _form['other_repo']
332 other_repo = _form['other_repo']
333 other_ref = 'rev:ancestor:%s' % _form['ancestor_rev'] # could be calculated from other_ref ...
333 other_ref = 'rev:ancestor:%s' % _form['ancestor_rev'] # could be calculated from other_ref ...
334 revisions = [x for x in reversed(_form['revisions'])]
334 revisions = [x for x in reversed(_form['revisions'])]
335 reviewers = _form['review_members']
335 reviewers = _form['review_members']
336
336
337 title = _form['pullrequest_title']
337 title = _form['pullrequest_title']
338 if not title:
339 title = '%s#%s to %s' % (org_repo, org_ref.split(':', 2)[1], other_repo)
338 description = _form['pullrequest_desc']
340 description = _form['pullrequest_desc']
339 try:
341 try:
340 pull_request = PullRequestModel().create(
342 pull_request = PullRequestModel().create(
341 self.rhodecode_user.user_id, org_repo, org_ref, other_repo,
343 self.rhodecode_user.user_id, org_repo, org_ref, other_repo,
342 other_ref, revisions, reviewers, title, description
344 other_ref, revisions, reviewers, title, description
343 )
345 )
344 Session().commit()
346 Session().commit()
345 h.flash(_('Successfully opened new pull request'),
347 h.flash(_('Successfully opened new pull request'),
346 category='success')
348 category='success')
347 except Exception:
349 except Exception:
348 h.flash(_('Error occurred during sending pull request'),
350 h.flash(_('Error occurred during sending pull request'),
349 category='error')
351 category='error')
350 log.error(traceback.format_exc())
352 log.error(traceback.format_exc())
351 return redirect(url('pullrequest_home', repo_name=repo_name))
353 return redirect(url('pullrequest_home', repo_name=repo_name))
352
354
353 return redirect(url('pullrequest_show', repo_name=other_repo,
355 return redirect(url('pullrequest_show', repo_name=other_repo,
354 pull_request_id=pull_request.pull_request_id))
356 pull_request_id=pull_request.pull_request_id))
355
357
356 @LoginRequired()
358 @LoginRequired()
357 @NotAnonymous()
359 @NotAnonymous()
358 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
360 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
359 'repository.admin')
361 'repository.admin')
360 @jsonify
362 @jsonify
361 def update(self, repo_name, pull_request_id):
363 def update(self, repo_name, pull_request_id):
362 pull_request = PullRequest.get_or_404(pull_request_id)
364 pull_request = PullRequest.get_or_404(pull_request_id)
363 if pull_request.is_closed():
365 if pull_request.is_closed():
364 raise HTTPForbidden()
366 raise HTTPForbidden()
365 #only owner or admin can update it
367 #only owner or admin can update it
366 owner = pull_request.author.user_id == c.rhodecode_user.user_id
368 owner = pull_request.author.user_id == c.rhodecode_user.user_id
367 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
369 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
368 reviewers_ids = map(int, filter(lambda v: v not in [None, ''],
370 reviewers_ids = map(int, filter(lambda v: v not in [None, ''],
369 request.POST.get('reviewers_ids', '').split(',')))
371 request.POST.get('reviewers_ids', '').split(',')))
370
372
371 PullRequestModel().update_reviewers(pull_request_id, reviewers_ids)
373 PullRequestModel().update_reviewers(pull_request_id, reviewers_ids)
372 Session().commit()
374 Session().commit()
373 return True
375 return True
374 raise HTTPForbidden()
376 raise HTTPForbidden()
375
377
376 @LoginRequired()
378 @LoginRequired()
377 @NotAnonymous()
379 @NotAnonymous()
378 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
380 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
379 'repository.admin')
381 'repository.admin')
380 @jsonify
382 @jsonify
381 def delete(self, repo_name, pull_request_id):
383 def delete(self, repo_name, pull_request_id):
382 pull_request = PullRequest.get_or_404(pull_request_id)
384 pull_request = PullRequest.get_or_404(pull_request_id)
383 #only owner can delete it !
385 #only owner can delete it !
384 if pull_request.author.user_id == c.rhodecode_user.user_id:
386 if pull_request.author.user_id == c.rhodecode_user.user_id:
385 PullRequestModel().delete(pull_request)
387 PullRequestModel().delete(pull_request)
386 Session().commit()
388 Session().commit()
387 h.flash(_('Successfully deleted pull request'),
389 h.flash(_('Successfully deleted pull request'),
388 category='success')
390 category='success')
389 return redirect(url('admin_settings_my_account', anchor='pullrequests'))
391 return redirect(url('admin_settings_my_account', anchor='pullrequests'))
390 raise HTTPForbidden()
392 raise HTTPForbidden()
391
393
392 @LoginRequired()
394 @LoginRequired()
393 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
395 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
394 'repository.admin')
396 'repository.admin')
395 def show(self, repo_name, pull_request_id):
397 def show(self, repo_name, pull_request_id):
396 repo_model = RepoModel()
398 repo_model = RepoModel()
397 c.users_array = repo_model.get_users_js()
399 c.users_array = repo_model.get_users_js()
398 c.users_groups_array = repo_model.get_users_groups_js()
400 c.users_groups_array = repo_model.get_users_groups_js()
399 c.pull_request = PullRequest.get_or_404(pull_request_id)
401 c.pull_request = PullRequest.get_or_404(pull_request_id)
400 c.allowed_to_change_status = self._get_is_allowed_change_status(c.pull_request)
402 c.allowed_to_change_status = self._get_is_allowed_change_status(c.pull_request)
401 cc_model = ChangesetCommentsModel()
403 cc_model = ChangesetCommentsModel()
402 cs_model = ChangesetStatusModel()
404 cs_model = ChangesetStatusModel()
403 _cs_statuses = cs_model.get_statuses(c.pull_request.org_repo,
405 _cs_statuses = cs_model.get_statuses(c.pull_request.org_repo,
404 pull_request=c.pull_request,
406 pull_request=c.pull_request,
405 with_revisions=True)
407 with_revisions=True)
406
408
407 cs_statuses = defaultdict(list)
409 cs_statuses = defaultdict(list)
408 for st in _cs_statuses:
410 for st in _cs_statuses:
409 cs_statuses[st.author.username] += [st]
411 cs_statuses[st.author.username] += [st]
410
412
411 c.pull_request_reviewers = []
413 c.pull_request_reviewers = []
412 c.pull_request_pending_reviewers = []
414 c.pull_request_pending_reviewers = []
413 for o in c.pull_request.reviewers:
415 for o in c.pull_request.reviewers:
414 st = cs_statuses.get(o.user.username, None)
416 st = cs_statuses.get(o.user.username, None)
415 if st:
417 if st:
416 sorter = lambda k: k.version
418 sorter = lambda k: k.version
417 st = [(x, list(y)[0])
419 st = [(x, list(y)[0])
418 for x, y in (groupby(sorted(st, key=sorter), sorter))]
420 for x, y in (groupby(sorted(st, key=sorter), sorter))]
419 else:
421 else:
420 c.pull_request_pending_reviewers.append(o.user)
422 c.pull_request_pending_reviewers.append(o.user)
421 c.pull_request_reviewers.append([o.user, st])
423 c.pull_request_reviewers.append([o.user, st])
422
424
423 # pull_requests repo_name we opened it against
425 # pull_requests repo_name we opened it against
424 # ie. other_repo must match
426 # ie. other_repo must match
425 if repo_name != c.pull_request.other_repo.repo_name:
427 if repo_name != c.pull_request.other_repo.repo_name:
426 raise HTTPNotFound
428 raise HTTPNotFound
427
429
428 # load compare data into template context
430 # load compare data into template context
429 enable_comments = not c.pull_request.is_closed()
431 enable_comments = not c.pull_request.is_closed()
430 self._load_compare_data(c.pull_request, enable_comments=enable_comments)
432 self._load_compare_data(c.pull_request, enable_comments=enable_comments)
431
433
432 # inline comments
434 # inline comments
433 c.inline_cnt = 0
435 c.inline_cnt = 0
434 c.inline_comments = cc_model.get_inline_comments(
436 c.inline_comments = cc_model.get_inline_comments(
435 c.rhodecode_db_repo.repo_id,
437 c.rhodecode_db_repo.repo_id,
436 pull_request=pull_request_id)
438 pull_request=pull_request_id)
437 # count inline comments
439 # count inline comments
438 for __, lines in c.inline_comments:
440 for __, lines in c.inline_comments:
439 for comments in lines.values():
441 for comments in lines.values():
440 c.inline_cnt += len(comments)
442 c.inline_cnt += len(comments)
441 # comments
443 # comments
442 c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id,
444 c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id,
443 pull_request=pull_request_id)
445 pull_request=pull_request_id)
444
446
445 # (badly named) pull-request status calculation based on reviewer votes
447 # (badly named) pull-request status calculation based on reviewer votes
446 c.current_changeset_status = cs_model.calculate_status(
448 c.current_changeset_status = cs_model.calculate_status(
447 c.pull_request_reviewers,
449 c.pull_request_reviewers,
448 )
450 )
449 c.changeset_statuses = ChangesetStatus.STATUSES
451 c.changeset_statuses = ChangesetStatus.STATUSES
450
452
451 c.as_form = False
453 c.as_form = False
452 c.ancestor = None # there is one - but right here we don't know which
454 c.ancestor = None # there is one - but right here we don't know which
453 return render('/pullrequests/pullrequest_show.html')
455 return render('/pullrequests/pullrequest_show.html')
454
456
455 @LoginRequired()
457 @LoginRequired()
456 @NotAnonymous()
458 @NotAnonymous()
457 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
459 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
458 'repository.admin')
460 'repository.admin')
459 @jsonify
461 @jsonify
460 def comment(self, repo_name, pull_request_id):
462 def comment(self, repo_name, pull_request_id):
461 pull_request = PullRequest.get_or_404(pull_request_id)
463 pull_request = PullRequest.get_or_404(pull_request_id)
462 if pull_request.is_closed():
464 if pull_request.is_closed():
463 raise HTTPForbidden()
465 raise HTTPForbidden()
464
466
465 status = request.POST.get('changeset_status')
467 status = request.POST.get('changeset_status')
466 change_status = request.POST.get('change_changeset_status')
468 change_status = request.POST.get('change_changeset_status')
467 text = request.POST.get('text')
469 text = request.POST.get('text')
468 close_pr = request.POST.get('save_close')
470 close_pr = request.POST.get('save_close')
469
471
470 allowed_to_change_status = self._get_is_allowed_change_status(pull_request)
472 allowed_to_change_status = self._get_is_allowed_change_status(pull_request)
471 if status and change_status and allowed_to_change_status:
473 if status and change_status and allowed_to_change_status:
472 _def = (_('Status change -> %s')
474 _def = (_('Status change -> %s')
473 % ChangesetStatus.get_status_lbl(status))
475 % ChangesetStatus.get_status_lbl(status))
474 if close_pr:
476 if close_pr:
475 _def = _('Closing with') + ' ' + _def
477 _def = _('Closing with') + ' ' + _def
476 text = text or _def
478 text = text or _def
477 comm = ChangesetCommentsModel().create(
479 comm = ChangesetCommentsModel().create(
478 text=text,
480 text=text,
479 repo=c.rhodecode_db_repo.repo_id,
481 repo=c.rhodecode_db_repo.repo_id,
480 user=c.rhodecode_user.user_id,
482 user=c.rhodecode_user.user_id,
481 pull_request=pull_request_id,
483 pull_request=pull_request_id,
482 f_path=request.POST.get('f_path'),
484 f_path=request.POST.get('f_path'),
483 line_no=request.POST.get('line'),
485 line_no=request.POST.get('line'),
484 status_change=(ChangesetStatus.get_status_lbl(status)
486 status_change=(ChangesetStatus.get_status_lbl(status)
485 if status and change_status
487 if status and change_status
486 and allowed_to_change_status else None),
488 and allowed_to_change_status else None),
487 closing_pr=close_pr
489 closing_pr=close_pr
488 )
490 )
489
491
490 action_logger(self.rhodecode_user,
492 action_logger(self.rhodecode_user,
491 'user_commented_pull_request:%s' % pull_request_id,
493 'user_commented_pull_request:%s' % pull_request_id,
492 c.rhodecode_db_repo, self.ip_addr, self.sa)
494 c.rhodecode_db_repo, self.ip_addr, self.sa)
493
495
494 if allowed_to_change_status:
496 if allowed_to_change_status:
495 # get status if set !
497 # get status if set !
496 if status and change_status:
498 if status and change_status:
497 ChangesetStatusModel().set_status(
499 ChangesetStatusModel().set_status(
498 c.rhodecode_db_repo.repo_id,
500 c.rhodecode_db_repo.repo_id,
499 status,
501 status,
500 c.rhodecode_user.user_id,
502 c.rhodecode_user.user_id,
501 comm,
503 comm,
502 pull_request=pull_request_id
504 pull_request=pull_request_id
503 )
505 )
504
506
505 if close_pr:
507 if close_pr:
506 if status in ['rejected', 'approved']:
508 if status in ['rejected', 'approved']:
507 PullRequestModel().close_pull_request(pull_request_id)
509 PullRequestModel().close_pull_request(pull_request_id)
508 action_logger(self.rhodecode_user,
510 action_logger(self.rhodecode_user,
509 'user_closed_pull_request:%s' % pull_request_id,
511 'user_closed_pull_request:%s' % pull_request_id,
510 c.rhodecode_db_repo, self.ip_addr, self.sa)
512 c.rhodecode_db_repo, self.ip_addr, self.sa)
511 else:
513 else:
512 h.flash(_('Closing pull request on other statuses than '
514 h.flash(_('Closing pull request on other statuses than '
513 'rejected or approved forbidden'),
515 'rejected or approved forbidden'),
514 category='warning')
516 category='warning')
515
517
516 Session().commit()
518 Session().commit()
517
519
518 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
520 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
519 return redirect(h.url('pullrequest_show', repo_name=repo_name,
521 return redirect(h.url('pullrequest_show', repo_name=repo_name,
520 pull_request_id=pull_request_id))
522 pull_request_id=pull_request_id))
521
523
522 data = {
524 data = {
523 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
525 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
524 }
526 }
525 if comm:
527 if comm:
526 c.co = comm
528 c.co = comm
527 data.update(comm.get_dict())
529 data.update(comm.get_dict())
528 data.update({'rendered_text':
530 data.update({'rendered_text':
529 render('changeset/changeset_comment_block.html')})
531 render('changeset/changeset_comment_block.html')})
530
532
531 return data
533 return data
532
534
533 @LoginRequired()
535 @LoginRequired()
534 @NotAnonymous()
536 @NotAnonymous()
535 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
537 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
536 'repository.admin')
538 'repository.admin')
537 @jsonify
539 @jsonify
538 def delete_comment(self, repo_name, comment_id):
540 def delete_comment(self, repo_name, comment_id):
539 co = ChangesetComment.get(comment_id)
541 co = ChangesetComment.get(comment_id)
540 if co.pull_request.is_closed():
542 if co.pull_request.is_closed():
541 #don't allow deleting comments on closed pull request
543 #don't allow deleting comments on closed pull request
542 raise HTTPForbidden()
544 raise HTTPForbidden()
543
545
544 owner = co.author.user_id == c.rhodecode_user.user_id
546 owner = co.author.user_id == c.rhodecode_user.user_id
545 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
547 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
546 ChangesetCommentsModel().delete(comment=co)
548 ChangesetCommentsModel().delete(comment=co)
547 Session().commit()
549 Session().commit()
548 return True
550 return True
549 else:
551 else:
550 raise HTTPForbidden()
552 raise HTTPForbidden()
@@ -1,439 +1,439 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 UserGroupForm(edit=False, old_data={}, available_members=[]):
97 def UserGroupForm(edit=False, old_data={}, available_members=[]):
98 class _UserGroupForm(formencode.Schema):
98 class _UserGroupForm(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.ValidUserGroup(edit, old_data)
104 v.ValidUserGroup(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 _UserGroupForm
115 return _UserGroupForm
116
116
117
117
118 def ReposGroupForm(edit=False, old_data={}, available_groups=[],
118 def ReposGroupForm(edit=False, old_data={}, available_groups=[],
119 can_create_in_root=False):
119 can_create_in_root=False):
120 class _ReposGroupForm(formencode.Schema):
120 class _ReposGroupForm(formencode.Schema):
121 allow_extra_fields = True
121 allow_extra_fields = True
122 filter_extra_fields = False
122 filter_extra_fields = False
123
123
124 group_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
124 group_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
125 v.SlugifyName())
125 v.SlugifyName())
126 group_description = v.UnicodeString(strip=True, min=1,
126 group_description = v.UnicodeString(strip=True, min=1,
127 not_empty=False)
127 not_empty=False)
128 if edit:
128 if edit:
129 #FIXME: do a special check that we cannot move a group to one of
129 #FIXME: do a special check that we cannot move a group to one of
130 #it's children
130 #it's children
131 pass
131 pass
132 group_parent_id = All(v.CanCreateGroup(can_create_in_root),
132 group_parent_id = All(v.CanCreateGroup(can_create_in_root),
133 v.OneOf(available_groups, hideList=False,
133 v.OneOf(available_groups, hideList=False,
134 testValueList=True,
134 testValueList=True,
135 if_missing=None, not_empty=True))
135 if_missing=None, not_empty=True))
136 enable_locking = v.StringBoolean(if_missing=False)
136 enable_locking = v.StringBoolean(if_missing=False)
137 chained_validators = [v.ValidReposGroup(edit, old_data)]
137 chained_validators = [v.ValidReposGroup(edit, old_data)]
138
138
139 return _ReposGroupForm
139 return _ReposGroupForm
140
140
141
141
142 def RegisterForm(edit=False, old_data={}):
142 def RegisterForm(edit=False, old_data={}):
143 class _RegisterForm(formencode.Schema):
143 class _RegisterForm(formencode.Schema):
144 allow_extra_fields = True
144 allow_extra_fields = True
145 filter_extra_fields = True
145 filter_extra_fields = True
146 username = All(
146 username = All(
147 v.ValidUsername(edit, old_data),
147 v.ValidUsername(edit, old_data),
148 v.UnicodeString(strip=True, min=1, not_empty=True)
148 v.UnicodeString(strip=True, min=1, not_empty=True)
149 )
149 )
150 password = All(
150 password = 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 password_confirmation = All(
154 password_confirmation = All(
155 v.ValidPassword(),
155 v.ValidPassword(),
156 v.UnicodeString(strip=False, min=6, not_empty=True)
156 v.UnicodeString(strip=False, min=6, not_empty=True)
157 )
157 )
158 active = v.StringBoolean(if_missing=False)
158 active = v.StringBoolean(if_missing=False)
159 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
159 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
160 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
160 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
161 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
161 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
162
162
163 chained_validators = [v.ValidPasswordsMatch()]
163 chained_validators = [v.ValidPasswordsMatch()]
164
164
165 return _RegisterForm
165 return _RegisterForm
166
166
167
167
168 def PasswordResetForm():
168 def PasswordResetForm():
169 class _PasswordResetForm(formencode.Schema):
169 class _PasswordResetForm(formencode.Schema):
170 allow_extra_fields = True
170 allow_extra_fields = True
171 filter_extra_fields = True
171 filter_extra_fields = True
172 email = All(v.ValidSystemEmail(), v.Email(not_empty=True))
172 email = All(v.ValidSystemEmail(), v.Email(not_empty=True))
173 return _PasswordResetForm
173 return _PasswordResetForm
174
174
175
175
176 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
176 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
177 repo_groups=[], landing_revs=[]):
177 repo_groups=[], landing_revs=[]):
178 class _RepoForm(formencode.Schema):
178 class _RepoForm(formencode.Schema):
179 allow_extra_fields = True
179 allow_extra_fields = True
180 filter_extra_fields = False
180 filter_extra_fields = False
181 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
181 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
182 v.SlugifyName())
182 v.SlugifyName())
183 repo_group = All(v.CanWriteGroup(old_data),
183 repo_group = All(v.CanWriteGroup(old_data),
184 v.OneOf(repo_groups, hideList=True))
184 v.OneOf(repo_groups, hideList=True))
185 repo_type = v.OneOf(supported_backends, required=False,
185 repo_type = v.OneOf(supported_backends, required=False,
186 if_missing=old_data.get('repo_type'))
186 if_missing=old_data.get('repo_type'))
187 repo_description = v.UnicodeString(strip=True, min=1, not_empty=False)
187 repo_description = v.UnicodeString(strip=True, min=1, not_empty=False)
188 repo_private = v.StringBoolean(if_missing=False)
188 repo_private = v.StringBoolean(if_missing=False)
189 repo_landing_rev = v.OneOf(landing_revs, hideList=True)
189 repo_landing_rev = v.OneOf(landing_revs, hideList=True)
190 clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False))
190 clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False))
191
191
192 repo_enable_statistics = v.StringBoolean(if_missing=False)
192 repo_enable_statistics = v.StringBoolean(if_missing=False)
193 repo_enable_downloads = v.StringBoolean(if_missing=False)
193 repo_enable_downloads = v.StringBoolean(if_missing=False)
194 repo_enable_locking = v.StringBoolean(if_missing=False)
194 repo_enable_locking = v.StringBoolean(if_missing=False)
195
195
196 if edit:
196 if edit:
197 #this is repo owner
197 #this is repo owner
198 user = All(v.UnicodeString(not_empty=True), v.ValidRepoUser())
198 user = All(v.UnicodeString(not_empty=True), v.ValidRepoUser())
199
199
200 chained_validators = [v.ValidCloneUri(),
200 chained_validators = [v.ValidCloneUri(),
201 v.ValidRepoName(edit, old_data)]
201 v.ValidRepoName(edit, old_data)]
202 return _RepoForm
202 return _RepoForm
203
203
204
204
205 def RepoPermsForm():
205 def RepoPermsForm():
206 class _RepoPermsForm(formencode.Schema):
206 class _RepoPermsForm(formencode.Schema):
207 allow_extra_fields = True
207 allow_extra_fields = True
208 filter_extra_fields = False
208 filter_extra_fields = False
209 chained_validators = [v.ValidPerms(type_='repo')]
209 chained_validators = [v.ValidPerms(type_='repo')]
210 return _RepoPermsForm
210 return _RepoPermsForm
211
211
212
212
213 def RepoGroupPermsForm():
213 def RepoGroupPermsForm():
214 class _RepoGroupPermsForm(formencode.Schema):
214 class _RepoGroupPermsForm(formencode.Schema):
215 allow_extra_fields = True
215 allow_extra_fields = True
216 filter_extra_fields = False
216 filter_extra_fields = False
217 recursive = v.StringBoolean(if_missing=False)
217 recursive = v.StringBoolean(if_missing=False)
218 chained_validators = [v.ValidPerms(type_='repo_group')]
218 chained_validators = [v.ValidPerms(type_='repo_group')]
219 return _RepoGroupPermsForm
219 return _RepoGroupPermsForm
220
220
221
221
222 def UserGroupPermsForm():
222 def UserGroupPermsForm():
223 class _UserPermsForm(formencode.Schema):
223 class _UserPermsForm(formencode.Schema):
224 allow_extra_fields = True
224 allow_extra_fields = True
225 filter_extra_fields = False
225 filter_extra_fields = False
226 chained_validators = [v.ValidPerms(type_='user_group')]
226 chained_validators = [v.ValidPerms(type_='user_group')]
227 return _UserPermsForm
227 return _UserPermsForm
228
228
229
229
230 def RepoFieldForm():
230 def RepoFieldForm():
231 class _RepoFieldForm(formencode.Schema):
231 class _RepoFieldForm(formencode.Schema):
232 filter_extra_fields = True
232 filter_extra_fields = True
233 allow_extra_fields = True
233 allow_extra_fields = True
234
234
235 new_field_key = All(v.FieldKey(),
235 new_field_key = All(v.FieldKey(),
236 v.UnicodeString(strip=True, min=3, not_empty=True))
236 v.UnicodeString(strip=True, min=3, not_empty=True))
237 new_field_value = v.UnicodeString(not_empty=False, if_missing='')
237 new_field_value = v.UnicodeString(not_empty=False, if_missing='')
238 new_field_type = v.OneOf(['str', 'unicode', 'list', 'tuple'],
238 new_field_type = v.OneOf(['str', 'unicode', 'list', 'tuple'],
239 if_missing='str')
239 if_missing='str')
240 new_field_label = v.UnicodeString(not_empty=False)
240 new_field_label = v.UnicodeString(not_empty=False)
241 new_field_desc = v.UnicodeString(not_empty=False)
241 new_field_desc = v.UnicodeString(not_empty=False)
242
242
243 return _RepoFieldForm
243 return _RepoFieldForm
244
244
245
245
246 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
246 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
247 repo_groups=[], landing_revs=[]):
247 repo_groups=[], landing_revs=[]):
248 class _RepoForkForm(formencode.Schema):
248 class _RepoForkForm(formencode.Schema):
249 allow_extra_fields = True
249 allow_extra_fields = True
250 filter_extra_fields = False
250 filter_extra_fields = False
251 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
251 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
252 v.SlugifyName())
252 v.SlugifyName())
253 repo_group = All(v.CanWriteGroup(),
253 repo_group = All(v.CanWriteGroup(),
254 v.OneOf(repo_groups, hideList=True))
254 v.OneOf(repo_groups, hideList=True))
255 repo_type = All(v.ValidForkType(old_data), v.OneOf(supported_backends))
255 repo_type = All(v.ValidForkType(old_data), v.OneOf(supported_backends))
256 description = v.UnicodeString(strip=True, min=1, not_empty=True)
256 description = v.UnicodeString(strip=True, min=1, not_empty=True)
257 private = v.StringBoolean(if_missing=False)
257 private = v.StringBoolean(if_missing=False)
258 copy_permissions = v.StringBoolean(if_missing=False)
258 copy_permissions = v.StringBoolean(if_missing=False)
259 update_after_clone = v.StringBoolean(if_missing=False)
259 update_after_clone = v.StringBoolean(if_missing=False)
260 fork_parent_id = v.UnicodeString()
260 fork_parent_id = v.UnicodeString()
261 chained_validators = [v.ValidForkName(edit, old_data)]
261 chained_validators = [v.ValidForkName(edit, old_data)]
262 landing_rev = v.OneOf(landing_revs, hideList=True)
262 landing_rev = v.OneOf(landing_revs, hideList=True)
263
263
264 return _RepoForkForm
264 return _RepoForkForm
265
265
266
266
267 def ApplicationSettingsForm():
267 def ApplicationSettingsForm():
268 class _ApplicationSettingsForm(formencode.Schema):
268 class _ApplicationSettingsForm(formencode.Schema):
269 allow_extra_fields = True
269 allow_extra_fields = True
270 filter_extra_fields = False
270 filter_extra_fields = False
271 rhodecode_title = v.UnicodeString(strip=True, min=1, not_empty=True)
271 rhodecode_title = v.UnicodeString(strip=True, min=1, not_empty=True)
272 rhodecode_realm = v.UnicodeString(strip=True, min=1, not_empty=True)
272 rhodecode_realm = v.UnicodeString(strip=True, min=1, not_empty=True)
273 rhodecode_ga_code = v.UnicodeString(strip=True, min=1, not_empty=False)
273 rhodecode_ga_code = v.UnicodeString(strip=True, min=1, not_empty=False)
274
274
275 return _ApplicationSettingsForm
275 return _ApplicationSettingsForm
276
276
277
277
278 def ApplicationVisualisationForm():
278 def ApplicationVisualisationForm():
279 class _ApplicationVisualisationForm(formencode.Schema):
279 class _ApplicationVisualisationForm(formencode.Schema):
280 allow_extra_fields = True
280 allow_extra_fields = True
281 filter_extra_fields = False
281 filter_extra_fields = False
282 rhodecode_show_public_icon = v.StringBoolean(if_missing=False)
282 rhodecode_show_public_icon = v.StringBoolean(if_missing=False)
283 rhodecode_show_private_icon = v.StringBoolean(if_missing=False)
283 rhodecode_show_private_icon = v.StringBoolean(if_missing=False)
284 rhodecode_stylify_metatags = v.StringBoolean(if_missing=False)
284 rhodecode_stylify_metatags = v.StringBoolean(if_missing=False)
285
285
286 rhodecode_repository_fields = v.StringBoolean(if_missing=False)
286 rhodecode_repository_fields = v.StringBoolean(if_missing=False)
287 rhodecode_lightweight_journal = v.StringBoolean(if_missing=False)
287 rhodecode_lightweight_journal = v.StringBoolean(if_missing=False)
288 rhodecode_dashboard_items = v.Int(min=5, not_empty=True)
288 rhodecode_dashboard_items = v.Int(min=5, not_empty=True)
289 rhodecode_show_version = v.StringBoolean(if_missing=False)
289 rhodecode_show_version = v.StringBoolean(if_missing=False)
290
290
291 return _ApplicationVisualisationForm
291 return _ApplicationVisualisationForm
292
292
293
293
294 def ApplicationUiSettingsForm():
294 def ApplicationUiSettingsForm():
295 class _ApplicationUiSettingsForm(formencode.Schema):
295 class _ApplicationUiSettingsForm(formencode.Schema):
296 allow_extra_fields = True
296 allow_extra_fields = True
297 filter_extra_fields = False
297 filter_extra_fields = False
298 web_push_ssl = v.StringBoolean(if_missing=False)
298 web_push_ssl = v.StringBoolean(if_missing=False)
299 paths_root_path = All(
299 paths_root_path = All(
300 v.ValidPath(),
300 v.ValidPath(),
301 v.UnicodeString(strip=True, min=1, not_empty=True)
301 v.UnicodeString(strip=True, min=1, not_empty=True)
302 )
302 )
303 hooks_changegroup_update = v.StringBoolean(if_missing=False)
303 hooks_changegroup_update = v.StringBoolean(if_missing=False)
304 hooks_changegroup_repo_size = v.StringBoolean(if_missing=False)
304 hooks_changegroup_repo_size = v.StringBoolean(if_missing=False)
305 hooks_changegroup_push_logger = v.StringBoolean(if_missing=False)
305 hooks_changegroup_push_logger = v.StringBoolean(if_missing=False)
306 hooks_outgoing_pull_logger = v.StringBoolean(if_missing=False)
306 hooks_outgoing_pull_logger = v.StringBoolean(if_missing=False)
307
307
308 extensions_largefiles = v.StringBoolean(if_missing=False)
308 extensions_largefiles = v.StringBoolean(if_missing=False)
309 extensions_hgsubversion = v.StringBoolean(if_missing=False)
309 extensions_hgsubversion = v.StringBoolean(if_missing=False)
310 extensions_hggit = v.StringBoolean(if_missing=False)
310 extensions_hggit = v.StringBoolean(if_missing=False)
311
311
312 return _ApplicationUiSettingsForm
312 return _ApplicationUiSettingsForm
313
313
314
314
315 def DefaultPermissionsForm(repo_perms_choices, group_perms_choices,
315 def DefaultPermissionsForm(repo_perms_choices, group_perms_choices,
316 user_group_perms_choices, create_choices,
316 user_group_perms_choices, create_choices,
317 repo_group_create_choices, user_group_create_choices,
317 repo_group_create_choices, user_group_create_choices,
318 fork_choices, register_choices, extern_activate_choices):
318 fork_choices, register_choices, extern_activate_choices):
319 class _DefaultPermissionsForm(formencode.Schema):
319 class _DefaultPermissionsForm(formencode.Schema):
320 allow_extra_fields = True
320 allow_extra_fields = True
321 filter_extra_fields = True
321 filter_extra_fields = True
322 overwrite_default_repo = v.StringBoolean(if_missing=False)
322 overwrite_default_repo = v.StringBoolean(if_missing=False)
323 overwrite_default_group = v.StringBoolean(if_missing=False)
323 overwrite_default_group = v.StringBoolean(if_missing=False)
324 overwrite_default_user_group = v.StringBoolean(if_missing=False)
324 overwrite_default_user_group = v.StringBoolean(if_missing=False)
325 anonymous = v.StringBoolean(if_missing=False)
325 anonymous = v.StringBoolean(if_missing=False)
326 default_repo_perm = v.OneOf(repo_perms_choices)
326 default_repo_perm = v.OneOf(repo_perms_choices)
327 default_group_perm = v.OneOf(group_perms_choices)
327 default_group_perm = v.OneOf(group_perms_choices)
328 default_user_group_perm = v.OneOf(user_group_perms_choices)
328 default_user_group_perm = v.OneOf(user_group_perms_choices)
329
329
330 default_repo_create = v.OneOf(create_choices)
330 default_repo_create = v.OneOf(create_choices)
331 default_user_group_create = v.OneOf(user_group_create_choices)
331 default_user_group_create = v.OneOf(user_group_create_choices)
332 #default_repo_group_create = v.OneOf(repo_group_create_choices) #not impl. yet
332 #default_repo_group_create = v.OneOf(repo_group_create_choices) #not impl. yet
333 default_fork = v.OneOf(fork_choices)
333 default_fork = v.OneOf(fork_choices)
334
334
335 default_register = v.OneOf(register_choices)
335 default_register = v.OneOf(register_choices)
336 default_extern_activate = v.OneOf(extern_activate_choices)
336 default_extern_activate = v.OneOf(extern_activate_choices)
337 return _DefaultPermissionsForm
337 return _DefaultPermissionsForm
338
338
339
339
340 def CustomDefaultPermissionsForm():
340 def CustomDefaultPermissionsForm():
341 class _CustomDefaultPermissionsForm(formencode.Schema):
341 class _CustomDefaultPermissionsForm(formencode.Schema):
342 filter_extra_fields = True
342 filter_extra_fields = True
343 allow_extra_fields = True
343 allow_extra_fields = True
344 inherit_default_permissions = v.StringBoolean(if_missing=False)
344 inherit_default_permissions = v.StringBoolean(if_missing=False)
345
345
346 create_repo_perm = v.StringBoolean(if_missing=False)
346 create_repo_perm = v.StringBoolean(if_missing=False)
347 create_user_group_perm = v.StringBoolean(if_missing=False)
347 create_user_group_perm = v.StringBoolean(if_missing=False)
348 #create_repo_group_perm Impl. later
348 #create_repo_group_perm Impl. later
349
349
350 fork_repo_perm = v.StringBoolean(if_missing=False)
350 fork_repo_perm = v.StringBoolean(if_missing=False)
351
351
352 return _CustomDefaultPermissionsForm
352 return _CustomDefaultPermissionsForm
353
353
354
354
355 def DefaultsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
355 def DefaultsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
356 class _DefaultsForm(formencode.Schema):
356 class _DefaultsForm(formencode.Schema):
357 allow_extra_fields = True
357 allow_extra_fields = True
358 filter_extra_fields = True
358 filter_extra_fields = True
359 default_repo_type = v.OneOf(supported_backends)
359 default_repo_type = v.OneOf(supported_backends)
360 default_repo_private = v.StringBoolean(if_missing=False)
360 default_repo_private = v.StringBoolean(if_missing=False)
361 default_repo_enable_statistics = v.StringBoolean(if_missing=False)
361 default_repo_enable_statistics = v.StringBoolean(if_missing=False)
362 default_repo_enable_downloads = v.StringBoolean(if_missing=False)
362 default_repo_enable_downloads = v.StringBoolean(if_missing=False)
363 default_repo_enable_locking = v.StringBoolean(if_missing=False)
363 default_repo_enable_locking = v.StringBoolean(if_missing=False)
364
364
365 return _DefaultsForm
365 return _DefaultsForm
366
366
367
367
368 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices,
368 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices,
369 tls_kind_choices):
369 tls_kind_choices):
370 class _LdapSettingsForm(formencode.Schema):
370 class _LdapSettingsForm(formencode.Schema):
371 allow_extra_fields = True
371 allow_extra_fields = True
372 filter_extra_fields = True
372 filter_extra_fields = True
373 #pre_validators = [LdapLibValidator]
373 #pre_validators = [LdapLibValidator]
374 ldap_active = v.StringBoolean(if_missing=False)
374 ldap_active = v.StringBoolean(if_missing=False)
375 ldap_host = v.UnicodeString(strip=True,)
375 ldap_host = v.UnicodeString(strip=True,)
376 ldap_port = v.Number(strip=True,)
376 ldap_port = v.Number(strip=True,)
377 ldap_tls_kind = v.OneOf(tls_kind_choices)
377 ldap_tls_kind = v.OneOf(tls_kind_choices)
378 ldap_tls_reqcert = v.OneOf(tls_reqcert_choices)
378 ldap_tls_reqcert = v.OneOf(tls_reqcert_choices)
379 ldap_dn_user = v.UnicodeString(strip=True,)
379 ldap_dn_user = v.UnicodeString(strip=True,)
380 ldap_dn_pass = v.UnicodeString(strip=True,)
380 ldap_dn_pass = v.UnicodeString(strip=True,)
381 ldap_base_dn = v.UnicodeString(strip=True,)
381 ldap_base_dn = v.UnicodeString(strip=True,)
382 ldap_filter = v.UnicodeString(strip=True,)
382 ldap_filter = v.UnicodeString(strip=True,)
383 ldap_search_scope = v.OneOf(search_scope_choices)
383 ldap_search_scope = v.OneOf(search_scope_choices)
384 ldap_attr_login = v.AttrLoginValidator()(not_empty=True)
384 ldap_attr_login = v.AttrLoginValidator()(not_empty=True)
385 ldap_attr_firstname = v.UnicodeString(strip=True,)
385 ldap_attr_firstname = v.UnicodeString(strip=True,)
386 ldap_attr_lastname = v.UnicodeString(strip=True,)
386 ldap_attr_lastname = v.UnicodeString(strip=True,)
387 ldap_attr_email = v.UnicodeString(strip=True,)
387 ldap_attr_email = v.UnicodeString(strip=True,)
388
388
389 return _LdapSettingsForm
389 return _LdapSettingsForm
390
390
391
391
392 def UserExtraEmailForm():
392 def UserExtraEmailForm():
393 class _UserExtraEmailForm(formencode.Schema):
393 class _UserExtraEmailForm(formencode.Schema):
394 email = All(v.UniqSystemEmail(), v.Email(not_empty=True))
394 email = All(v.UniqSystemEmail(), v.Email(not_empty=True))
395 return _UserExtraEmailForm
395 return _UserExtraEmailForm
396
396
397
397
398 def UserExtraIpForm():
398 def UserExtraIpForm():
399 class _UserExtraIpForm(formencode.Schema):
399 class _UserExtraIpForm(formencode.Schema):
400 ip = v.ValidIp()(not_empty=True)
400 ip = v.ValidIp()(not_empty=True)
401 return _UserExtraIpForm
401 return _UserExtraIpForm
402
402
403
403
404 def PullRequestForm(repo_id):
404 def PullRequestForm(repo_id):
405 class _PullRequestForm(formencode.Schema):
405 class _PullRequestForm(formencode.Schema):
406 allow_extra_fields = True
406 allow_extra_fields = True
407 filter_extra_fields = True
407 filter_extra_fields = True
408
408
409 user = v.UnicodeString(strip=True, required=True)
409 user = v.UnicodeString(strip=True, required=True)
410 org_repo = v.UnicodeString(strip=True, required=True)
410 org_repo = v.UnicodeString(strip=True, required=True)
411 org_ref = v.UnicodeString(strip=True, required=True)
411 org_ref = v.UnicodeString(strip=True, required=True)
412 other_repo = v.UnicodeString(strip=True, required=True)
412 other_repo = v.UnicodeString(strip=True, required=True)
413 other_ref = v.UnicodeString(strip=True, required=True)
413 other_ref = v.UnicodeString(strip=True, required=True)
414 revisions = All(#v.NotReviewedRevisions(repo_id)(),
414 revisions = All(#v.NotReviewedRevisions(repo_id)(),
415 v.UniqueList(not_empty=True))
415 v.UniqueList(not_empty=True))
416 review_members = v.UniqueList(not_empty=True)
416 review_members = v.UniqueList(not_empty=True)
417
417
418 pullrequest_title = v.UnicodeString(strip=True, required=True, min=3)
418 pullrequest_title = v.UnicodeString(strip=True, required=True)
419 pullrequest_desc = v.UnicodeString(strip=True, required=False)
419 pullrequest_desc = v.UnicodeString(strip=True, required=False)
420
420
421 ancestor_rev = v.UnicodeString(strip=True, required=True)
421 ancestor_rev = v.UnicodeString(strip=True, required=True)
422 merge_rev = v.UnicodeString(strip=True, required=True)
422 merge_rev = v.UnicodeString(strip=True, required=True)
423
423
424 return _PullRequestForm
424 return _PullRequestForm
425
425
426
426
427 def GistForm(lifetime_options):
427 def GistForm(lifetime_options):
428 class _GistForm(formencode.Schema):
428 class _GistForm(formencode.Schema):
429
429
430 filename = All(v.BasePath()(),
430 filename = All(v.BasePath()(),
431 v.UnicodeString(strip=True, required=False))
431 v.UnicodeString(strip=True, required=False))
432 description = v.UnicodeString(required=False, if_missing='')
432 description = v.UnicodeString(required=False, if_missing='')
433 lifetime = v.OneOf(lifetime_options)
433 lifetime = v.OneOf(lifetime_options)
434 mimetype = v.UnicodeString(required=False, if_missing=None)
434 mimetype = v.UnicodeString(required=False, if_missing=None)
435 content = v.UnicodeString(required=True, not_empty=True)
435 content = v.UnicodeString(required=True, not_empty=True)
436 public = v.UnicodeString(required=False, if_missing='')
436 public = v.UnicodeString(required=False, if_missing='')
437 private = v.UnicodeString(required=False, if_missing='')
437 private = v.UnicodeString(required=False, if_missing='')
438
438
439 return _GistForm
439 return _GistForm
General Comments 0
You need to be logged in to leave comments. Login now