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