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