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