##// END OF EJS Templates
protect agains pull requests on empty repositories
marcink -
r2874:95923493 beta
parent child Browse files
Show More
@@ -1,431 +1,439 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, response, session, tmpl_context as c, url
33 from pylons import request, response, session, tmpl_context as c, url
34 from pylons.controllers.util import abort, redirect
34 from pylons.controllers.util import abort, redirect
35 from pylons.i18n.translation import _
35 from pylons.i18n.translation import _
36 from pylons.decorators import jsonify
36 from pylons.decorators import jsonify
37
37
38 from rhodecode.lib.compat import json
38 from rhodecode.lib.compat import json
39 from rhodecode.lib.base import BaseRepoController, render
39 from rhodecode.lib.base import BaseRepoController, render
40 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator,\
40 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator,\
41 NotAnonymous
41 NotAnonymous
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
44 from rhodecode.lib.utils import action_logger
45 from rhodecode.model.db import User, PullRequest, ChangesetStatus,\
45 from rhodecode.model.db import User, PullRequest, ChangesetStatus,\
46 ChangesetComment
46 ChangesetComment
47 from rhodecode.model.pull_request import PullRequestModel
47 from rhodecode.model.pull_request import PullRequestModel
48 from rhodecode.model.meta import Session
48 from rhodecode.model.meta import Session
49 from rhodecode.model.repo import RepoModel
49 from rhodecode.model.repo import RepoModel
50 from rhodecode.model.comment import ChangesetCommentsModel
50 from rhodecode.model.comment import ChangesetCommentsModel
51 from rhodecode.model.changeset_status import ChangesetStatusModel
51 from rhodecode.model.changeset_status import ChangesetStatusModel
52 from rhodecode.model.forms import PullRequestForm
52 from rhodecode.model.forms import PullRequestForm
53 from rhodecode.lib.vcs.exceptions import EmptyRepositoryError
53
54
54 log = logging.getLogger(__name__)
55 log = logging.getLogger(__name__)
55
56
56
57
57 class PullrequestsController(BaseRepoController):
58 class PullrequestsController(BaseRepoController):
58
59
59 @LoginRequired()
60 @LoginRequired()
60 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
61 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
61 'repository.admin')
62 'repository.admin')
62 def __before__(self):
63 def __before__(self):
63 super(PullrequestsController, self).__before__()
64 super(PullrequestsController, self).__before__()
64 repo_model = RepoModel()
65 repo_model = RepoModel()
65 c.users_array = repo_model.get_users_js()
66 c.users_array = repo_model.get_users_js()
66 c.users_groups_array = repo_model.get_users_groups_js()
67 c.users_groups_array = repo_model.get_users_groups_js()
67
68
68 def _get_repo_refs(self, repo):
69 def _get_repo_refs(self, repo):
69 hist_l = []
70 hist_l = []
70
71
71 branches_group = ([('branch:%s:%s' % (k, v), k) for
72 branches_group = ([('branch:%s:%s' % (k, v), k) for
72 k, v in repo.branches.iteritems()], _("Branches"))
73 k, v in repo.branches.iteritems()], _("Branches"))
73 bookmarks_group = ([('book:%s:%s' % (k, v), k) for
74 bookmarks_group = ([('book:%s:%s' % (k, v), k) for
74 k, v in repo.bookmarks.iteritems()], _("Bookmarks"))
75 k, v in repo.bookmarks.iteritems()], _("Bookmarks"))
75 tags_group = ([('tag:%s:%s' % (k, v), k) for
76 tags_group = ([('tag:%s:%s' % (k, v), k) for
76 k, v in repo.tags.iteritems()], _("Tags"))
77 k, v in repo.tags.iteritems()], _("Tags"))
77
78
78 hist_l.append(bookmarks_group)
79 hist_l.append(bookmarks_group)
79 hist_l.append(branches_group)
80 hist_l.append(branches_group)
80 hist_l.append(tags_group)
81 hist_l.append(tags_group)
81
82
82 return hist_l
83 return hist_l
83
84
84 def _get_default_rev(self, repo):
85 def _get_default_rev(self, repo):
85 """
86 """
86 Get's default revision to do compare on pull request
87 Get's default revision to do compare on pull request
87
88
88 :param repo:
89 :param repo:
89 """
90 """
90 repo = repo.scm_instance
91 repo = repo.scm_instance
91 if 'default' in repo.branches:
92 if 'default' in repo.branches:
92 return 'default'
93 return 'default'
93 else:
94 else:
94 #if repo doesn't have default branch return first found
95 #if repo doesn't have default branch return first found
95 return repo.branches.keys()[0]
96 return repo.branches.keys()[0]
96
97
97 def show_all(self, repo_name):
98 def show_all(self, repo_name):
98 c.pull_requests = PullRequestModel().get_all(repo_name)
99 c.pull_requests = PullRequestModel().get_all(repo_name)
99 c.repo_name = repo_name
100 c.repo_name = repo_name
100 return render('/pullrequests/pullrequest_show_all.html')
101 return render('/pullrequests/pullrequest_show_all.html')
101
102
102 @NotAnonymous()
103 @NotAnonymous()
103 def index(self):
104 def index(self):
104 org_repo = c.rhodecode_db_repo
105 org_repo = c.rhodecode_db_repo
105
106
106 if org_repo.scm_instance.alias != 'hg':
107 if org_repo.scm_instance.alias != 'hg':
107 log.error('Review not available for GIT REPOS')
108 log.error('Review not available for GIT REPOS')
108 raise HTTPNotFound
109 raise HTTPNotFound
109
110
111 try:
112 org_repo.scm_instance.get_changeset()
113 except EmptyRepositoryError, e:
114 h.flash(h.literal(_('There are no changesets yet')),
115 category='warning')
116 redirect(url('summary_home', repo_name=org_repo.repo_name))
117
110 other_repos_info = {}
118 other_repos_info = {}
111
119
112 c.org_refs = self._get_repo_refs(c.rhodecode_repo)
120 c.org_refs = self._get_repo_refs(c.rhodecode_repo)
113 c.org_repos = []
121 c.org_repos = []
114 c.other_repos = []
122 c.other_repos = []
115 c.org_repos.append((org_repo.repo_name, '%s/%s' % (
123 c.org_repos.append((org_repo.repo_name, '%s/%s' % (
116 org_repo.user.username, c.repo_name))
124 org_repo.user.username, c.repo_name))
117 )
125 )
118
126
119 # add org repo to other so we can open pull request agains itself
127 # add org repo to other so we can open pull request agains itself
120 c.other_repos.extend(c.org_repos)
128 c.other_repos.extend(c.org_repos)
121
129
122 c.default_pull_request = org_repo.repo_name # repo name pre-selected
130 c.default_pull_request = org_repo.repo_name # repo name pre-selected
123 c.default_pull_request_rev = self._get_default_rev(org_repo) # revision pre-selected
131 c.default_pull_request_rev = self._get_default_rev(org_repo) # revision pre-selected
124 c.default_revs = self._get_repo_refs(org_repo.scm_instance)
132 c.default_revs = self._get_repo_refs(org_repo.scm_instance)
125 #add orginal repo
133 #add orginal repo
126 other_repos_info[org_repo.repo_name] = {
134 other_repos_info[org_repo.repo_name] = {
127 'gravatar': h.gravatar_url(org_repo.user.email, 24),
135 'gravatar': h.gravatar_url(org_repo.user.email, 24),
128 'description': org_repo.description,
136 'description': org_repo.description,
129 'revs': h.select('other_ref', '', c.default_revs, class_='refs')
137 'revs': h.select('other_ref', '', c.default_revs, class_='refs')
130 }
138 }
131
139
132 #gather forks and add to this list
140 #gather forks and add to this list
133 for fork in org_repo.forks:
141 for fork in org_repo.forks:
134 c.other_repos.append((fork.repo_name, '%s/%s' % (
142 c.other_repos.append((fork.repo_name, '%s/%s' % (
135 fork.user.username, fork.repo_name))
143 fork.user.username, fork.repo_name))
136 )
144 )
137 other_repos_info[fork.repo_name] = {
145 other_repos_info[fork.repo_name] = {
138 'gravatar': h.gravatar_url(fork.user.email, 24),
146 'gravatar': h.gravatar_url(fork.user.email, 24),
139 'description': fork.description,
147 'description': fork.description,
140 'revs': h.select('other_ref', '',
148 'revs': h.select('other_ref', '',
141 self._get_repo_refs(fork.scm_instance),
149 self._get_repo_refs(fork.scm_instance),
142 class_='refs')
150 class_='refs')
143 }
151 }
144 #add parents of this fork also
152 #add parents of this fork also
145 if org_repo.parent:
153 if org_repo.parent:
146 c.default_pull_request = org_repo.parent.repo_name
154 c.default_pull_request = org_repo.parent.repo_name
147 c.default_pull_request_rev = self._get_default_rev(org_repo.parent)
155 c.default_pull_request_rev = self._get_default_rev(org_repo.parent)
148 c.default_revs = self._get_repo_refs(org_repo.parent.scm_instance)
156 c.default_revs = self._get_repo_refs(org_repo.parent.scm_instance)
149 c.other_repos.append((org_repo.parent.repo_name, '%s/%s' % (
157 c.other_repos.append((org_repo.parent.repo_name, '%s/%s' % (
150 org_repo.parent.user.username,
158 org_repo.parent.user.username,
151 org_repo.parent.repo_name))
159 org_repo.parent.repo_name))
152 )
160 )
153 other_repos_info[org_repo.parent.repo_name] = {
161 other_repos_info[org_repo.parent.repo_name] = {
154 'gravatar': h.gravatar_url(org_repo.parent.user.email, 24),
162 'gravatar': h.gravatar_url(org_repo.parent.user.email, 24),
155 'description': org_repo.parent.description,
163 'description': org_repo.parent.description,
156 'revs': h.select('other_ref', '',
164 'revs': h.select('other_ref', '',
157 self._get_repo_refs(org_repo.parent.scm_instance),
165 self._get_repo_refs(org_repo.parent.scm_instance),
158 class_='refs')
166 class_='refs')
159 }
167 }
160
168
161 c.other_repos_info = json.dumps(other_repos_info)
169 c.other_repos_info = json.dumps(other_repos_info)
162 c.review_members = [org_repo.user]
170 c.review_members = [org_repo.user]
163 return render('/pullrequests/pullrequest.html')
171 return render('/pullrequests/pullrequest.html')
164
172
165 @NotAnonymous()
173 @NotAnonymous()
166 def create(self, repo_name):
174 def create(self, repo_name):
167 try:
175 try:
168 _form = PullRequestForm()().to_python(request.POST)
176 _form = PullRequestForm()().to_python(request.POST)
169 except formencode.Invalid, errors:
177 except formencode.Invalid, errors:
170 log.error(traceback.format_exc())
178 log.error(traceback.format_exc())
171 if errors.error_dict.get('revisions'):
179 if errors.error_dict.get('revisions'):
172 msg = 'Revisions: %s' % errors.error_dict['revisions']
180 msg = 'Revisions: %s' % errors.error_dict['revisions']
173 elif errors.error_dict.get('pullrequest_title'):
181 elif errors.error_dict.get('pullrequest_title'):
174 msg = _('Pull request requires a title with min. 3 chars')
182 msg = _('Pull request requires a title with min. 3 chars')
175 else:
183 else:
176 msg = _('error during creation of pull request')
184 msg = _('error during creation of pull request')
177
185
178 h.flash(msg, 'error')
186 h.flash(msg, 'error')
179 return redirect(url('pullrequest_home', repo_name=repo_name))
187 return redirect(url('pullrequest_home', repo_name=repo_name))
180
188
181 org_repo = _form['org_repo']
189 org_repo = _form['org_repo']
182 org_ref = _form['org_ref']
190 org_ref = _form['org_ref']
183 other_repo = _form['other_repo']
191 other_repo = _form['other_repo']
184 other_ref = _form['other_ref']
192 other_ref = _form['other_ref']
185 revisions = _form['revisions']
193 revisions = _form['revisions']
186 reviewers = _form['review_members']
194 reviewers = _form['review_members']
187
195
188 title = _form['pullrequest_title']
196 title = _form['pullrequest_title']
189 description = _form['pullrequest_desc']
197 description = _form['pullrequest_desc']
190
198
191 try:
199 try:
192 pull_request = PullRequestModel().create(
200 pull_request = PullRequestModel().create(
193 self.rhodecode_user.user_id, org_repo, org_ref, other_repo,
201 self.rhodecode_user.user_id, org_repo, org_ref, other_repo,
194 other_ref, revisions, reviewers, title, description
202 other_ref, revisions, reviewers, title, description
195 )
203 )
196 Session().commit()
204 Session().commit()
197 h.flash(_('Successfully opened new pull request'),
205 h.flash(_('Successfully opened new pull request'),
198 category='success')
206 category='success')
199 except Exception:
207 except Exception:
200 h.flash(_('Error occurred during sending pull request'),
208 h.flash(_('Error occurred during sending pull request'),
201 category='error')
209 category='error')
202 log.error(traceback.format_exc())
210 log.error(traceback.format_exc())
203 return redirect(url('pullrequest_home', repo_name=repo_name))
211 return redirect(url('pullrequest_home', repo_name=repo_name))
204
212
205 return redirect(url('pullrequest_show', repo_name=other_repo,
213 return redirect(url('pullrequest_show', repo_name=other_repo,
206 pull_request_id=pull_request.pull_request_id))
214 pull_request_id=pull_request.pull_request_id))
207
215
208 @NotAnonymous()
216 @NotAnonymous()
209 @jsonify
217 @jsonify
210 def update(self, repo_name, pull_request_id):
218 def update(self, repo_name, pull_request_id):
211 pull_request = PullRequest.get_or_404(pull_request_id)
219 pull_request = PullRequest.get_or_404(pull_request_id)
212 if pull_request.is_closed():
220 if pull_request.is_closed():
213 raise HTTPForbidden()
221 raise HTTPForbidden()
214 #only owner or admin can update it
222 #only owner or admin can update it
215 owner = pull_request.author.user_id == c.rhodecode_user.user_id
223 owner = pull_request.author.user_id == c.rhodecode_user.user_id
216 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
224 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
217 reviewers_ids = map(int, filter(lambda v: v not in [None, ''],
225 reviewers_ids = map(int, filter(lambda v: v not in [None, ''],
218 request.POST.get('reviewers_ids', '').split(',')))
226 request.POST.get('reviewers_ids', '').split(',')))
219
227
220 PullRequestModel().update_reviewers(pull_request_id, reviewers_ids)
228 PullRequestModel().update_reviewers(pull_request_id, reviewers_ids)
221 Session.commit()
229 Session.commit()
222 return True
230 return True
223 raise HTTPForbidden()
231 raise HTTPForbidden()
224
232
225 @NotAnonymous()
233 @NotAnonymous()
226 @jsonify
234 @jsonify
227 def delete(self, repo_name, pull_request_id):
235 def delete(self, repo_name, pull_request_id):
228 pull_request = PullRequest.get_or_404(pull_request_id)
236 pull_request = PullRequest.get_or_404(pull_request_id)
229 #only owner can delete it !
237 #only owner can delete it !
230 if pull_request.author.user_id == c.rhodecode_user.user_id:
238 if pull_request.author.user_id == c.rhodecode_user.user_id:
231 PullRequestModel().delete(pull_request)
239 PullRequestModel().delete(pull_request)
232 Session().commit()
240 Session().commit()
233 h.flash(_('Successfully deleted pull request'),
241 h.flash(_('Successfully deleted pull request'),
234 category='success')
242 category='success')
235 return redirect(url('admin_settings_my_account'))
243 return redirect(url('admin_settings_my_account'))
236 raise HTTPForbidden()
244 raise HTTPForbidden()
237
245
238 def _load_compare_data(self, pull_request, enable_comments=True):
246 def _load_compare_data(self, pull_request, enable_comments=True):
239 """
247 """
240 Load context data needed for generating compare diff
248 Load context data needed for generating compare diff
241
249
242 :param pull_request:
250 :param pull_request:
243 :type pull_request:
251 :type pull_request:
244 """
252 """
245
253
246 org_repo = pull_request.org_repo
254 org_repo = pull_request.org_repo
247 (org_ref_type,
255 (org_ref_type,
248 org_ref_name,
256 org_ref_name,
249 org_ref_rev) = pull_request.org_ref.split(':')
257 org_ref_rev) = pull_request.org_ref.split(':')
250
258
251 other_repo = pull_request.other_repo
259 other_repo = pull_request.other_repo
252 (other_ref_type,
260 (other_ref_type,
253 other_ref_name,
261 other_ref_name,
254 other_ref_rev) = pull_request.other_ref.split(':')
262 other_ref_rev) = pull_request.other_ref.split(':')
255
263
256 # despite opening revisions for bookmarks/branches/tags, we always
264 # despite opening revisions for bookmarks/branches/tags, we always
257 # convert this to rev to prevent changes after book or branch change
265 # convert this to rev to prevent changes after book or branch change
258 org_ref = ('rev', org_ref_rev)
266 org_ref = ('rev', org_ref_rev)
259 other_ref = ('rev', other_ref_rev)
267 other_ref = ('rev', other_ref_rev)
260
268
261 c.org_repo = org_repo
269 c.org_repo = org_repo
262 c.other_repo = other_repo
270 c.other_repo = other_repo
263
271
264 c.cs_ranges, discovery_data = PullRequestModel().get_compare_data(
272 c.cs_ranges, discovery_data = PullRequestModel().get_compare_data(
265 org_repo, org_ref, other_repo, other_ref
273 org_repo, org_ref, other_repo, other_ref
266 )
274 )
267
275
268 c.statuses = org_repo.statuses([x.raw_id for x in c.cs_ranges])
276 c.statuses = org_repo.statuses([x.raw_id for x in c.cs_ranges])
269 # defines that we need hidden inputs with changesets
277 # defines that we need hidden inputs with changesets
270 c.as_form = request.GET.get('as_form', False)
278 c.as_form = request.GET.get('as_form', False)
271
279
272 c.org_ref = org_ref[1]
280 c.org_ref = org_ref[1]
273 c.other_ref = other_ref[1]
281 c.other_ref = other_ref[1]
274 # diff needs to have swapped org with other to generate proper diff
282 # diff needs to have swapped org with other to generate proper diff
275 _diff = diffs.differ(other_repo, other_ref, org_repo, org_ref,
283 _diff = diffs.differ(other_repo, other_ref, org_repo, org_ref,
276 discovery_data)
284 discovery_data)
277 diff_processor = diffs.DiffProcessor(_diff, format='gitdiff')
285 diff_processor = diffs.DiffProcessor(_diff, format='gitdiff')
278 _parsed = diff_processor.prepare()
286 _parsed = diff_processor.prepare()
279
287
280 c.files = []
288 c.files = []
281 c.changes = {}
289 c.changes = {}
282
290
283 for f in _parsed:
291 for f in _parsed:
284 fid = h.FID('', f['filename'])
292 fid = h.FID('', f['filename'])
285 c.files.append([fid, f['operation'], f['filename'], f['stats']])
293 c.files.append([fid, f['operation'], f['filename'], f['stats']])
286 diff = diff_processor.as_html(enable_comments=enable_comments,
294 diff = diff_processor.as_html(enable_comments=enable_comments,
287 diff_lines=[f])
295 diff_lines=[f])
288 c.changes[fid] = [f['operation'], f['filename'], diff]
296 c.changes[fid] = [f['operation'], f['filename'], diff]
289
297
290 def show(self, repo_name, pull_request_id):
298 def show(self, repo_name, pull_request_id):
291 repo_model = RepoModel()
299 repo_model = RepoModel()
292 c.users_array = repo_model.get_users_js()
300 c.users_array = repo_model.get_users_js()
293 c.users_groups_array = repo_model.get_users_groups_js()
301 c.users_groups_array = repo_model.get_users_groups_js()
294 c.pull_request = PullRequest.get_or_404(pull_request_id)
302 c.pull_request = PullRequest.get_or_404(pull_request_id)
295 c.target_repo = c.pull_request.org_repo.repo_name
303 c.target_repo = c.pull_request.org_repo.repo_name
296
304
297 cc_model = ChangesetCommentsModel()
305 cc_model = ChangesetCommentsModel()
298 cs_model = ChangesetStatusModel()
306 cs_model = ChangesetStatusModel()
299 _cs_statuses = cs_model.get_statuses(c.pull_request.org_repo,
307 _cs_statuses = cs_model.get_statuses(c.pull_request.org_repo,
300 pull_request=c.pull_request,
308 pull_request=c.pull_request,
301 with_revisions=True)
309 with_revisions=True)
302
310
303 cs_statuses = defaultdict(list)
311 cs_statuses = defaultdict(list)
304 for st in _cs_statuses:
312 for st in _cs_statuses:
305 cs_statuses[st.author.username] += [st]
313 cs_statuses[st.author.username] += [st]
306
314
307 c.pull_request_reviewers = []
315 c.pull_request_reviewers = []
308 c.pull_request_pending_reviewers = []
316 c.pull_request_pending_reviewers = []
309 for o in c.pull_request.reviewers:
317 for o in c.pull_request.reviewers:
310 st = cs_statuses.get(o.user.username, None)
318 st = cs_statuses.get(o.user.username, None)
311 if st:
319 if st:
312 sorter = lambda k: k.version
320 sorter = lambda k: k.version
313 st = [(x, list(y)[0])
321 st = [(x, list(y)[0])
314 for x, y in (groupby(sorted(st, key=sorter), sorter))]
322 for x, y in (groupby(sorted(st, key=sorter), sorter))]
315 else:
323 else:
316 c.pull_request_pending_reviewers.append(o.user)
324 c.pull_request_pending_reviewers.append(o.user)
317 c.pull_request_reviewers.append([o.user, st])
325 c.pull_request_reviewers.append([o.user, st])
318
326
319 # pull_requests repo_name we opened it against
327 # pull_requests repo_name we opened it against
320 # ie. other_repo must match
328 # ie. other_repo must match
321 if repo_name != c.pull_request.other_repo.repo_name:
329 if repo_name != c.pull_request.other_repo.repo_name:
322 raise HTTPNotFound
330 raise HTTPNotFound
323
331
324 # load compare data into template context
332 # load compare data into template context
325 enable_comments = not c.pull_request.is_closed()
333 enable_comments = not c.pull_request.is_closed()
326 self._load_compare_data(c.pull_request, enable_comments=enable_comments)
334 self._load_compare_data(c.pull_request, enable_comments=enable_comments)
327
335
328 # inline comments
336 # inline comments
329 c.inline_cnt = 0
337 c.inline_cnt = 0
330 c.inline_comments = cc_model.get_inline_comments(
338 c.inline_comments = cc_model.get_inline_comments(
331 c.rhodecode_db_repo.repo_id,
339 c.rhodecode_db_repo.repo_id,
332 pull_request=pull_request_id)
340 pull_request=pull_request_id)
333 # count inline comments
341 # count inline comments
334 for __, lines in c.inline_comments:
342 for __, lines in c.inline_comments:
335 for comments in lines.values():
343 for comments in lines.values():
336 c.inline_cnt += len(comments)
344 c.inline_cnt += len(comments)
337 # comments
345 # comments
338 c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id,
346 c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id,
339 pull_request=pull_request_id)
347 pull_request=pull_request_id)
340
348
341 try:
349 try:
342 cur_status = c.statuses[c.pull_request.revisions[0]][0]
350 cur_status = c.statuses[c.pull_request.revisions[0]][0]
343 except:
351 except:
344 log.error(traceback.format_exc())
352 log.error(traceback.format_exc())
345 cur_status = 'undefined'
353 cur_status = 'undefined'
346 if c.pull_request.is_closed() and 0:
354 if c.pull_request.is_closed() and 0:
347 c.current_changeset_status = cur_status
355 c.current_changeset_status = cur_status
348 else:
356 else:
349 # changeset(pull-request) status calulation based on reviewers
357 # changeset(pull-request) status calulation based on reviewers
350 c.current_changeset_status = cs_model.calculate_status(
358 c.current_changeset_status = cs_model.calculate_status(
351 c.pull_request_reviewers,
359 c.pull_request_reviewers,
352 )
360 )
353 c.changeset_statuses = ChangesetStatus.STATUSES
361 c.changeset_statuses = ChangesetStatus.STATUSES
354
362
355 return render('/pullrequests/pullrequest_show.html')
363 return render('/pullrequests/pullrequest_show.html')
356
364
357 @NotAnonymous()
365 @NotAnonymous()
358 @jsonify
366 @jsonify
359 def comment(self, repo_name, pull_request_id):
367 def comment(self, repo_name, pull_request_id):
360 pull_request = PullRequest.get_or_404(pull_request_id)
368 pull_request = PullRequest.get_or_404(pull_request_id)
361 if pull_request.is_closed():
369 if pull_request.is_closed():
362 raise HTTPForbidden()
370 raise HTTPForbidden()
363
371
364 status = request.POST.get('changeset_status')
372 status = request.POST.get('changeset_status')
365 change_status = request.POST.get('change_changeset_status')
373 change_status = request.POST.get('change_changeset_status')
366 text = request.POST.get('text')
374 text = request.POST.get('text')
367 if status and change_status:
375 if status and change_status:
368 text = text or (_('Status change -> %s')
376 text = text or (_('Status change -> %s')
369 % ChangesetStatus.get_status_lbl(status))
377 % ChangesetStatus.get_status_lbl(status))
370 comm = ChangesetCommentsModel().create(
378 comm = ChangesetCommentsModel().create(
371 text=text,
379 text=text,
372 repo=c.rhodecode_db_repo.repo_id,
380 repo=c.rhodecode_db_repo.repo_id,
373 user=c.rhodecode_user.user_id,
381 user=c.rhodecode_user.user_id,
374 pull_request=pull_request_id,
382 pull_request=pull_request_id,
375 f_path=request.POST.get('f_path'),
383 f_path=request.POST.get('f_path'),
376 line_no=request.POST.get('line'),
384 line_no=request.POST.get('line'),
377 status_change=(ChangesetStatus.get_status_lbl(status)
385 status_change=(ChangesetStatus.get_status_lbl(status)
378 if status and change_status else None)
386 if status and change_status else None)
379 )
387 )
380
388
381 # get status if set !
389 # get status if set !
382 if status and change_status:
390 if status and change_status:
383 ChangesetStatusModel().set_status(
391 ChangesetStatusModel().set_status(
384 c.rhodecode_db_repo.repo_id,
392 c.rhodecode_db_repo.repo_id,
385 status,
393 status,
386 c.rhodecode_user.user_id,
394 c.rhodecode_user.user_id,
387 comm,
395 comm,
388 pull_request=pull_request_id
396 pull_request=pull_request_id
389 )
397 )
390 action_logger(self.rhodecode_user,
398 action_logger(self.rhodecode_user,
391 'user_commented_pull_request:%s' % pull_request_id,
399 'user_commented_pull_request:%s' % pull_request_id,
392 c.rhodecode_db_repo, self.ip_addr, self.sa)
400 c.rhodecode_db_repo, self.ip_addr, self.sa)
393
401
394 if request.POST.get('save_close'):
402 if request.POST.get('save_close'):
395 PullRequestModel().close_pull_request(pull_request_id)
403 PullRequestModel().close_pull_request(pull_request_id)
396 action_logger(self.rhodecode_user,
404 action_logger(self.rhodecode_user,
397 'user_closed_pull_request:%s' % pull_request_id,
405 'user_closed_pull_request:%s' % pull_request_id,
398 c.rhodecode_db_repo, self.ip_addr, self.sa)
406 c.rhodecode_db_repo, self.ip_addr, self.sa)
399
407
400 Session().commit()
408 Session().commit()
401
409
402 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
410 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
403 return redirect(h.url('pullrequest_show', repo_name=repo_name,
411 return redirect(h.url('pullrequest_show', repo_name=repo_name,
404 pull_request_id=pull_request_id))
412 pull_request_id=pull_request_id))
405
413
406 data = {
414 data = {
407 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
415 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
408 }
416 }
409 if comm:
417 if comm:
410 c.co = comm
418 c.co = comm
411 data.update(comm.get_dict())
419 data.update(comm.get_dict())
412 data.update({'rendered_text':
420 data.update({'rendered_text':
413 render('changeset/changeset_comment_block.html')})
421 render('changeset/changeset_comment_block.html')})
414
422
415 return data
423 return data
416
424
417 @NotAnonymous()
425 @NotAnonymous()
418 @jsonify
426 @jsonify
419 def delete_comment(self, repo_name, comment_id):
427 def delete_comment(self, repo_name, comment_id):
420 co = ChangesetComment.get(comment_id)
428 co = ChangesetComment.get(comment_id)
421 if co.pull_request.is_closed():
429 if co.pull_request.is_closed():
422 #don't allow deleting comments on closed pull request
430 #don't allow deleting comments on closed pull request
423 raise HTTPForbidden()
431 raise HTTPForbidden()
424
432
425 owner = lambda: co.author.user_id == c.rhodecode_user.user_id
433 owner = lambda: co.author.user_id == c.rhodecode_user.user_id
426 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
434 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
427 ChangesetCommentsModel().delete(comment=co)
435 ChangesetCommentsModel().delete(comment=co)
428 Session().commit()
436 Session().commit()
429 return True
437 return True
430 else:
438 else:
431 raise HTTPForbidden()
439 raise HTTPForbidden()
General Comments 0
You need to be logged in to leave comments. Login now