##// END OF EJS Templates
Opening pull request shouldn't be accessible by anonymous users
marcink -
r2627:6bd62617 beta
parent child Browse files
Show More
@@ -1,353 +1,356
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.pullrequests
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 pull requests controller for rhodecode for initializing pull requests
7 7
8 8 :created_on: May 7, 2012
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25 import logging
26 26 import traceback
27 27
28 28 from webob.exc import HTTPNotFound, HTTPForbidden
29 29 from collections import defaultdict
30 30 from itertools import groupby
31 31
32 32 from pylons import request, response, session, tmpl_context as c, url
33 33 from pylons.controllers.util import abort, redirect
34 34 from pylons.i18n.translation import _
35 35 from pylons.decorators import jsonify
36 36
37 37 from rhodecode.lib.compat import json
38 38 from rhodecode.lib.base import BaseRepoController, render
39 39 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator,\
40 40 NotAnonymous
41 41 from rhodecode.lib import helpers as h
42 42 from rhodecode.lib import diffs
43 43 from rhodecode.lib.utils import action_logger
44 44 from rhodecode.model.db import User, PullRequest, ChangesetStatus,\
45 45 ChangesetComment
46 46 from rhodecode.model.pull_request import PullRequestModel
47 47 from rhodecode.model.meta import Session
48 48 from rhodecode.model.repo import RepoModel
49 49 from rhodecode.model.comment import ChangesetCommentsModel
50 50 from rhodecode.model.changeset_status import ChangesetStatusModel
51 51
52 52 log = logging.getLogger(__name__)
53 53
54 54
55 55 class PullrequestsController(BaseRepoController):
56 56
57 57 @LoginRequired()
58 58 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
59 59 'repository.admin')
60 60 def __before__(self):
61 61 super(PullrequestsController, self).__before__()
62 62 repo_model = RepoModel()
63 63 c.users_array = repo_model.get_users_js()
64 64 c.users_groups_array = repo_model.get_users_groups_js()
65 65
66 66 def _get_repo_refs(self, repo):
67 67 hist_l = []
68 68
69 69 branches_group = ([('branch:%s:%s' % (k, v), k) for
70 70 k, v in repo.branches.iteritems()], _("Branches"))
71 71 bookmarks_group = ([('book:%s:%s' % (k, v), k) for
72 72 k, v in repo.bookmarks.iteritems()], _("Bookmarks"))
73 73 tags_group = ([('tag:%s:%s' % (k, v), k) for
74 74 k, v in repo.tags.iteritems()], _("Tags"))
75 75
76 76 hist_l.append(bookmarks_group)
77 77 hist_l.append(branches_group)
78 78 hist_l.append(tags_group)
79 79
80 80 return hist_l
81 81
82 82 def show_all(self, repo_name):
83 83 c.pull_requests = PullRequestModel().get_all(repo_name)
84 84 c.repo_name = repo_name
85 85 return render('/pullrequests/pullrequest_show_all.html')
86 86
87 @NotAnonymous()
87 88 def index(self):
88 89 org_repo = c.rhodecode_db_repo
89 90
90 91 if org_repo.scm_instance.alias != 'hg':
91 92 log.error('Review not available for GIT REPOS')
92 93 raise HTTPNotFound
93 94
94 95 other_repos_info = {}
95 96
96 97 c.org_refs = self._get_repo_refs(c.rhodecode_repo)
97 98 c.org_repos = []
98 99 c.other_repos = []
99 100 c.org_repos.append((org_repo.repo_name, '%s/%s' % (
100 101 org_repo.user.username, c.repo_name))
101 102 )
102 103
103 104 c.other_refs = c.org_refs
104 105 c.other_repos.extend(c.org_repos)
105 106
106 107 #add orginal repo
107 108 other_repos_info[org_repo.repo_name] = {
108 109 'gravatar': h.gravatar_url(org_repo.user.email, 24),
109 110 'description': org_repo.description
110 111 }
111 112
112 113 c.default_pull_request = org_repo.repo_name
113 114 #gather forks and add to this list
114 115 for fork in org_repo.forks:
115 116 c.other_repos.append((fork.repo_name, '%s/%s' % (
116 117 fork.user.username, fork.repo_name))
117 118 )
118 119 other_repos_info[fork.repo_name] = {
119 120 'gravatar': h.gravatar_url(fork.user.email, 24),
120 121 'description': fork.description
121 122 }
122 123 #add parents of this fork also
123 124 if org_repo.parent:
124 125 c.default_pull_request = org_repo.parent.repo_name
125 126 c.other_repos.append((org_repo.parent.repo_name, '%s/%s' % (
126 127 org_repo.parent.user.username,
127 128 org_repo.parent.repo_name))
128 129 )
129 130 other_repos_info[org_repo.parent.repo_name] = {
130 131 'gravatar': h.gravatar_url(org_repo.parent.user.email, 24),
131 132 'description': org_repo.parent.description
132 133 }
133 134
134 135 c.other_repos_info = json.dumps(other_repos_info)
135 136 c.review_members = [org_repo.user]
136 137 return render('/pullrequests/pullrequest.html')
137 138
138 139 @NotAnonymous()
139 140 def create(self, repo_name):
140 141 req_p = request.POST
141 142 org_repo = req_p['org_repo']
142 143 org_ref = req_p['org_ref']
143 144 other_repo = req_p['other_repo']
144 145 other_ref = req_p['other_ref']
145 146 revisions = req_p.getall('revisions')
146 147 reviewers = req_p.getall('review_members')
147 148
148 149 #TODO: wrap this into a FORM !!!
149 150
150 151 title = req_p['pullrequest_title']
151 152 description = req_p['pullrequest_desc']
152 153
153 154 try:
154 155 pull_request = PullRequestModel().create(
155 156 self.rhodecode_user.user_id, org_repo, org_ref, other_repo,
156 157 other_ref, revisions, reviewers, title, description
157 158 )
158 159 Session().commit()
159 160 h.flash(_('Successfully opened new pull request'),
160 161 category='success')
161 162 except Exception:
162 163 h.flash(_('Error occurred during sending pull request'),
163 164 category='error')
164 165 log.error(traceback.format_exc())
165 166 return redirect(url('changelog_home', repo_name=org_repo,))
166 167
167 168 return redirect(url('pullrequest_show', repo_name=other_repo,
168 169 pull_request_id=pull_request.pull_request_id))
169 170
170 171 @NotAnonymous()
171 172 @jsonify
172 173 def update(self, repo_name, pull_request_id):
173 174 pull_request = PullRequest.get_or_404(pull_request_id)
174 175 if pull_request.is_closed():
175 176 raise HTTPForbidden()
176 177
177 178 reviewers_ids = map(int, filter(lambda v: v not in [None, ''],
178 179 request.POST.get('reviewers_ids', '').split(',')))
179 180 PullRequestModel().update_reviewers(pull_request_id, reviewers_ids)
180 181 Session.commit()
181 182 return True
182 183
183 184 def _load_compare_data(self, pull_request, enable_comments=True):
184 185 """
185 186 Load context data needed for generating compare diff
186 187
187 188 :param pull_request:
188 189 :type pull_request:
189 190 """
190 191
191 192 org_repo = pull_request.org_repo
192 193 org_ref_type, org_ref_, org_ref = pull_request.org_ref.split(':')
193 194 other_repo = pull_request.other_repo
194 195 other_ref_type, other_ref, other_ref_ = pull_request.other_ref.split(':')
195 196
196 197 org_ref = (org_ref_type, org_ref)
197 198 other_ref = (other_ref_type, other_ref)
198 199
199 200 c.org_repo = org_repo
200 201 c.other_repo = other_repo
201 202
202 203 c.cs_ranges, discovery_data = PullRequestModel().get_compare_data(
203 204 org_repo, org_ref, other_repo, other_ref
204 205 )
205 206
206 207 c.statuses = c.rhodecode_db_repo.statuses([x.raw_id for x in
207 208 c.cs_ranges])
208 209 # defines that we need hidden inputs with changesets
209 210 c.as_form = request.GET.get('as_form', False)
210 211
211 212 c.org_ref = org_ref[1]
212 213 c.other_ref = other_ref[1]
213 214 # diff needs to have swapped org with other to generate proper diff
214 215 _diff = diffs.differ(other_repo, other_ref, org_repo, org_ref,
215 216 discovery_data)
216 217 diff_processor = diffs.DiffProcessor(_diff, format='gitdiff')
217 218 _parsed = diff_processor.prepare()
218 219
219 220 c.files = []
220 221 c.changes = {}
221 222
222 223 for f in _parsed:
223 224 fid = h.FID('', f['filename'])
224 225 c.files.append([fid, f['operation'], f['filename'], f['stats']])
225 226 diff = diff_processor.as_html(enable_comments=enable_comments,
226 227 diff_lines=[f])
227 228 c.changes[fid] = [f['operation'], f['filename'], diff]
228 229
229 230 def show(self, repo_name, pull_request_id):
230 231 repo_model = RepoModel()
231 232 c.users_array = repo_model.get_users_js()
232 233 c.users_groups_array = repo_model.get_users_groups_js()
233 234 c.pull_request = PullRequest.get_or_404(pull_request_id)
234 235
235 236 cc_model = ChangesetCommentsModel()
236 237 cs_model = ChangesetStatusModel()
237 238 _cs_statuses = cs_model.get_statuses(c.pull_request.org_repo,
238 239 pull_request=c.pull_request,
239 240 with_revisions=True)
240 241
241 242 cs_statuses = defaultdict(list)
242 243 for st in _cs_statuses:
243 244 cs_statuses[st.author.username] += [st]
244 245
245 246 c.pull_request_reviewers = []
246 247 for o in c.pull_request.reviewers:
247 248 st = cs_statuses.get(o.user.username, None)
248 249 if st:
249 250 sorter = lambda k: k.version
250 251 st = [(x, list(y)[0])
251 252 for x, y in (groupby(sorted(st, key=sorter), sorter))]
252 253 c.pull_request_reviewers.append([o.user, st])
253 254
254 255 # pull_requests repo_name we opened it against
255 256 # ie. other_repo must match
256 257 if repo_name != c.pull_request.other_repo.repo_name:
257 258 raise HTTPNotFound
258 259
259 260 # load compare data into template context
260 261 enable_comments = not c.pull_request.is_closed()
261 262 self._load_compare_data(c.pull_request, enable_comments=enable_comments)
262 263
263 264 # inline comments
264 265 c.inline_cnt = 0
265 266 c.inline_comments = cc_model.get_inline_comments(
266 267 c.rhodecode_db_repo.repo_id,
267 268 pull_request=pull_request_id)
268 269 # count inline comments
269 270 for __, lines in c.inline_comments:
270 271 for comments in lines.values():
271 272 c.inline_cnt += len(comments)
272 273 # comments
273 274 c.comments = cc_model.get_comments(c.rhodecode_db_repo.repo_id,
274 275 pull_request=pull_request_id)
275 276
276 277 # changeset(pull-request) status
277 278 c.current_changeset_status = cs_model.calculate_status(
278 279 c.pull_request_reviewers
279 280 )
280 281 c.changeset_statuses = ChangesetStatus.STATUSES
281 282 c.target_repo = c.pull_request.org_repo.repo_name
282 283 return render('/pullrequests/pullrequest_show.html')
283 284
285 @NotAnonymous()
284 286 @jsonify
285 287 def comment(self, repo_name, pull_request_id):
286 288 pull_request = PullRequest.get_or_404(pull_request_id)
287 289 if pull_request.is_closed():
288 290 raise HTTPForbidden()
289 291
290 292 status = request.POST.get('changeset_status')
291 293 change_status = request.POST.get('change_changeset_status')
292 294
293 295 comm = ChangesetCommentsModel().create(
294 296 text=request.POST.get('text'),
295 297 repo=c.rhodecode_db_repo.repo_id,
296 298 user=c.rhodecode_user.user_id,
297 299 pull_request=pull_request_id,
298 300 f_path=request.POST.get('f_path'),
299 301 line_no=request.POST.get('line'),
300 302 status_change=(ChangesetStatus.get_status_lbl(status)
301 303 if status and change_status else None)
302 304 )
303 305
304 306 # get status if set !
305 307 if status and change_status:
306 308 ChangesetStatusModel().set_status(
307 309 c.rhodecode_db_repo.repo_id,
308 310 status,
309 311 c.rhodecode_user.user_id,
310 312 comm,
311 313 pull_request=pull_request_id
312 314 )
313 315 action_logger(self.rhodecode_user,
314 316 'user_commented_pull_request:%s' % pull_request_id,
315 317 c.rhodecode_db_repo, self.ip_addr, self.sa)
316 318
317 319 if request.POST.get('save_close'):
318 320 PullRequestModel().close_pull_request(pull_request_id)
319 321 action_logger(self.rhodecode_user,
320 322 'user_closed_pull_request:%s' % pull_request_id,
321 323 c.rhodecode_db_repo, self.ip_addr, self.sa)
322 324
323 325 Session().commit()
324 326
325 327 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
326 328 return redirect(h.url('pullrequest_show', repo_name=repo_name,
327 329 pull_request_id=pull_request_id))
328 330
329 331 data = {
330 332 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
331 333 }
332 334 if comm:
333 335 c.co = comm
334 336 data.update(comm.get_dict())
335 337 data.update({'rendered_text':
336 338 render('changeset/changeset_comment_block.html')})
337 339
338 340 return data
339 341
342 @NotAnonymous()
340 343 @jsonify
341 344 def delete_comment(self, repo_name, comment_id):
342 345 co = ChangesetComment.get(comment_id)
343 346 if co.pull_request.is_closed():
344 347 #don't allow deleting comments on closed pull request
345 348 raise HTTPForbidden()
346 349
347 350 owner = lambda: co.author.user_id == c.rhodecode_user.user_id
348 351 if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
349 352 ChangesetCommentsModel().delete(comment=co)
350 353 Session().commit()
351 354 return True
352 355 else:
353 356 raise HTTPForbidden()
General Comments 0
You need to be logged in to leave comments. Login now