##// END OF EJS Templates
diffs: fix bug where relevant pull request comments were not being...
dan -
r1158:d20e7e21 default
parent child Browse files
Show More
@@ -1,465 +1,464 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 commit controller for RhodeCode showing changes between commits
22 commit controller for RhodeCode showing changes between commits
23 """
23 """
24
24
25 import logging
25 import logging
26
26
27 from collections import defaultdict
27 from collections import defaultdict
28 from webob.exc import HTTPForbidden, HTTPBadRequest, HTTPNotFound
28 from webob.exc import HTTPForbidden, HTTPBadRequest, HTTPNotFound
29
29
30 from pylons import tmpl_context as c, request, response
30 from pylons import tmpl_context as c, request, response
31 from pylons.i18n.translation import _
31 from pylons.i18n.translation import _
32 from pylons.controllers.util import redirect
32 from pylons.controllers.util import redirect
33
33
34 from rhodecode.lib import auth
34 from rhodecode.lib import auth
35 from rhodecode.lib import diffs, codeblocks
35 from rhodecode.lib import diffs, codeblocks
36 from rhodecode.lib.auth import (
36 from rhodecode.lib.auth import (
37 LoginRequired, HasRepoPermissionAnyDecorator, NotAnonymous)
37 LoginRequired, HasRepoPermissionAnyDecorator, NotAnonymous)
38 from rhodecode.lib.base import BaseRepoController, render
38 from rhodecode.lib.base import BaseRepoController, render
39 from rhodecode.lib.compat import OrderedDict
39 from rhodecode.lib.compat import OrderedDict
40 from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError
40 from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError
41 import rhodecode.lib.helpers as h
41 import rhodecode.lib.helpers as h
42 from rhodecode.lib.utils import action_logger, jsonify
42 from rhodecode.lib.utils import action_logger, jsonify
43 from rhodecode.lib.utils2 import safe_unicode
43 from rhodecode.lib.utils2 import safe_unicode
44 from rhodecode.lib.vcs.backends.base import EmptyCommit
44 from rhodecode.lib.vcs.backends.base import EmptyCommit
45 from rhodecode.lib.vcs.exceptions import (
45 from rhodecode.lib.vcs.exceptions import (
46 RepositoryError, CommitDoesNotExistError)
46 RepositoryError, CommitDoesNotExistError)
47 from rhodecode.model.db import ChangesetComment, ChangesetStatus
47 from rhodecode.model.db import ChangesetComment, ChangesetStatus
48 from rhodecode.model.changeset_status import ChangesetStatusModel
48 from rhodecode.model.changeset_status import ChangesetStatusModel
49 from rhodecode.model.comment import ChangesetCommentsModel
49 from rhodecode.model.comment import ChangesetCommentsModel
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
52
53
53
54 log = logging.getLogger(__name__)
54 log = logging.getLogger(__name__)
55
55
56
56
57 def _update_with_GET(params, GET):
57 def _update_with_GET(params, GET):
58 for k in ['diff1', 'diff2', 'diff']:
58 for k in ['diff1', 'diff2', 'diff']:
59 params[k] += GET.getall(k)
59 params[k] += GET.getall(k)
60
60
61
61
62 def get_ignore_ws(fid, GET):
62 def get_ignore_ws(fid, GET):
63 ig_ws_global = GET.get('ignorews')
63 ig_ws_global = GET.get('ignorews')
64 ig_ws = filter(lambda k: k.startswith('WS'), GET.getall(fid))
64 ig_ws = filter(lambda k: k.startswith('WS'), GET.getall(fid))
65 if ig_ws:
65 if ig_ws:
66 try:
66 try:
67 return int(ig_ws[0].split(':')[-1])
67 return int(ig_ws[0].split(':')[-1])
68 except Exception:
68 except Exception:
69 pass
69 pass
70 return ig_ws_global
70 return ig_ws_global
71
71
72
72
73 def _ignorews_url(GET, fileid=None):
73 def _ignorews_url(GET, fileid=None):
74 fileid = str(fileid) if fileid else None
74 fileid = str(fileid) if fileid else None
75 params = defaultdict(list)
75 params = defaultdict(list)
76 _update_with_GET(params, GET)
76 _update_with_GET(params, GET)
77 label = _('Show whitespace')
77 label = _('Show whitespace')
78 tooltiplbl = _('Show whitespace for all diffs')
78 tooltiplbl = _('Show whitespace for all diffs')
79 ig_ws = get_ignore_ws(fileid, GET)
79 ig_ws = get_ignore_ws(fileid, GET)
80 ln_ctx = get_line_ctx(fileid, GET)
80 ln_ctx = get_line_ctx(fileid, GET)
81
81
82 if ig_ws is None:
82 if ig_ws is None:
83 params['ignorews'] += [1]
83 params['ignorews'] += [1]
84 label = _('Ignore whitespace')
84 label = _('Ignore whitespace')
85 tooltiplbl = _('Ignore whitespace for all diffs')
85 tooltiplbl = _('Ignore whitespace for all diffs')
86 ctx_key = 'context'
86 ctx_key = 'context'
87 ctx_val = ln_ctx
87 ctx_val = ln_ctx
88
88
89 # if we have passed in ln_ctx pass it along to our params
89 # if we have passed in ln_ctx pass it along to our params
90 if ln_ctx:
90 if ln_ctx:
91 params[ctx_key] += [ctx_val]
91 params[ctx_key] += [ctx_val]
92
92
93 if fileid:
93 if fileid:
94 params['anchor'] = 'a_' + fileid
94 params['anchor'] = 'a_' + fileid
95 return h.link_to(label, h.url.current(**params), title=tooltiplbl, class_='tooltip')
95 return h.link_to(label, h.url.current(**params), title=tooltiplbl, class_='tooltip')
96
96
97
97
98 def get_line_ctx(fid, GET):
98 def get_line_ctx(fid, GET):
99 ln_ctx_global = GET.get('context')
99 ln_ctx_global = GET.get('context')
100 if fid:
100 if fid:
101 ln_ctx = filter(lambda k: k.startswith('C'), GET.getall(fid))
101 ln_ctx = filter(lambda k: k.startswith('C'), GET.getall(fid))
102 else:
102 else:
103 _ln_ctx = filter(lambda k: k.startswith('C'), GET)
103 _ln_ctx = filter(lambda k: k.startswith('C'), GET)
104 ln_ctx = GET.get(_ln_ctx[0]) if _ln_ctx else ln_ctx_global
104 ln_ctx = GET.get(_ln_ctx[0]) if _ln_ctx else ln_ctx_global
105 if ln_ctx:
105 if ln_ctx:
106 ln_ctx = [ln_ctx]
106 ln_ctx = [ln_ctx]
107
107
108 if ln_ctx:
108 if ln_ctx:
109 retval = ln_ctx[0].split(':')[-1]
109 retval = ln_ctx[0].split(':')[-1]
110 else:
110 else:
111 retval = ln_ctx_global
111 retval = ln_ctx_global
112
112
113 try:
113 try:
114 return int(retval)
114 return int(retval)
115 except Exception:
115 except Exception:
116 return 3
116 return 3
117
117
118
118
119 def _context_url(GET, fileid=None):
119 def _context_url(GET, fileid=None):
120 """
120 """
121 Generates a url for context lines.
121 Generates a url for context lines.
122
122
123 :param fileid:
123 :param fileid:
124 """
124 """
125
125
126 fileid = str(fileid) if fileid else None
126 fileid = str(fileid) if fileid else None
127 ig_ws = get_ignore_ws(fileid, GET)
127 ig_ws = get_ignore_ws(fileid, GET)
128 ln_ctx = (get_line_ctx(fileid, GET) or 3) * 2
128 ln_ctx = (get_line_ctx(fileid, GET) or 3) * 2
129
129
130 params = defaultdict(list)
130 params = defaultdict(list)
131 _update_with_GET(params, GET)
131 _update_with_GET(params, GET)
132
132
133 if ln_ctx > 0:
133 if ln_ctx > 0:
134 params['context'] += [ln_ctx]
134 params['context'] += [ln_ctx]
135
135
136 if ig_ws:
136 if ig_ws:
137 ig_ws_key = 'ignorews'
137 ig_ws_key = 'ignorews'
138 ig_ws_val = 1
138 ig_ws_val = 1
139 params[ig_ws_key] += [ig_ws_val]
139 params[ig_ws_key] += [ig_ws_val]
140
140
141 lbl = _('Increase context')
141 lbl = _('Increase context')
142 tooltiplbl = _('Increase context for all diffs')
142 tooltiplbl = _('Increase context for all diffs')
143
143
144 if fileid:
144 if fileid:
145 params['anchor'] = 'a_' + fileid
145 params['anchor'] = 'a_' + fileid
146 return h.link_to(lbl, h.url.current(**params), title=tooltiplbl, class_='tooltip')
146 return h.link_to(lbl, h.url.current(**params), title=tooltiplbl, class_='tooltip')
147
147
148
148
149 class ChangesetController(BaseRepoController):
149 class ChangesetController(BaseRepoController):
150
150
151 def __before__(self):
151 def __before__(self):
152 super(ChangesetController, self).__before__()
152 super(ChangesetController, self).__before__()
153 c.affected_files_cut_off = 60
153 c.affected_files_cut_off = 60
154
154
155 def _index(self, commit_id_range, method):
155 def _index(self, commit_id_range, method):
156 c.ignorews_url = _ignorews_url
156 c.ignorews_url = _ignorews_url
157 c.context_url = _context_url
157 c.context_url = _context_url
158 c.fulldiff = fulldiff = request.GET.get('fulldiff')
158 c.fulldiff = fulldiff = request.GET.get('fulldiff')
159
159
160 # fetch global flags of ignore ws or context lines
160 # fetch global flags of ignore ws or context lines
161 context_lcl = get_line_ctx('', request.GET)
161 context_lcl = get_line_ctx('', request.GET)
162 ign_whitespace_lcl = get_ignore_ws('', request.GET)
162 ign_whitespace_lcl = get_ignore_ws('', request.GET)
163
163
164 # diff_limit will cut off the whole diff if the limit is applied
164 # diff_limit will cut off the whole diff if the limit is applied
165 # otherwise it will just hide the big files from the front-end
165 # otherwise it will just hide the big files from the front-end
166 diff_limit = self.cut_off_limit_diff
166 diff_limit = self.cut_off_limit_diff
167 file_limit = self.cut_off_limit_file
167 file_limit = self.cut_off_limit_file
168
168
169 # get ranges of commit ids if preset
169 # get ranges of commit ids if preset
170 commit_range = commit_id_range.split('...')[:2]
170 commit_range = commit_id_range.split('...')[:2]
171
171
172 try:
172 try:
173 pre_load = ['affected_files', 'author', 'branch', 'date',
173 pre_load = ['affected_files', 'author', 'branch', 'date',
174 'message', 'parents']
174 'message', 'parents']
175
175
176 if len(commit_range) == 2:
176 if len(commit_range) == 2:
177 commits = c.rhodecode_repo.get_commits(
177 commits = c.rhodecode_repo.get_commits(
178 start_id=commit_range[0], end_id=commit_range[1],
178 start_id=commit_range[0], end_id=commit_range[1],
179 pre_load=pre_load)
179 pre_load=pre_load)
180 commits = list(commits)
180 commits = list(commits)
181 else:
181 else:
182 commits = [c.rhodecode_repo.get_commit(
182 commits = [c.rhodecode_repo.get_commit(
183 commit_id=commit_id_range, pre_load=pre_load)]
183 commit_id=commit_id_range, pre_load=pre_load)]
184
184
185 c.commit_ranges = commits
185 c.commit_ranges = commits
186 if not c.commit_ranges:
186 if not c.commit_ranges:
187 raise RepositoryError(
187 raise RepositoryError(
188 'The commit range returned an empty result')
188 'The commit range returned an empty result')
189 except CommitDoesNotExistError:
189 except CommitDoesNotExistError:
190 msg = _('No such commit exists for this repository')
190 msg = _('No such commit exists for this repository')
191 h.flash(msg, category='error')
191 h.flash(msg, category='error')
192 raise HTTPNotFound()
192 raise HTTPNotFound()
193 except Exception:
193 except Exception:
194 log.exception("General failure")
194 log.exception("General failure")
195 raise HTTPNotFound()
195 raise HTTPNotFound()
196
196
197 c.changes = OrderedDict()
197 c.changes = OrderedDict()
198 c.lines_added = 0
198 c.lines_added = 0
199 c.lines_deleted = 0
199 c.lines_deleted = 0
200
200
201 c.commit_statuses = ChangesetStatus.STATUSES
201 c.commit_statuses = ChangesetStatus.STATUSES
202 c.inline_comments = []
202 c.inline_comments = []
203 c.inline_cnt = 0
203 c.inline_cnt = 0
204 c.files = []
204 c.files = []
205
205
206 c.statuses = []
206 c.statuses = []
207 c.comments = []
207 c.comments = []
208 if len(c.commit_ranges) == 1:
208 if len(c.commit_ranges) == 1:
209 commit = c.commit_ranges[0]
209 commit = c.commit_ranges[0]
210 c.comments = ChangesetCommentsModel().get_comments(
210 c.comments = ChangesetCommentsModel().get_comments(
211 c.rhodecode_db_repo.repo_id,
211 c.rhodecode_db_repo.repo_id,
212 revision=commit.raw_id)
212 revision=commit.raw_id)
213 c.statuses.append(ChangesetStatusModel().get_status(
213 c.statuses.append(ChangesetStatusModel().get_status(
214 c.rhodecode_db_repo.repo_id, commit.raw_id))
214 c.rhodecode_db_repo.repo_id, commit.raw_id))
215 # comments from PR
215 # comments from PR
216 statuses = ChangesetStatusModel().get_statuses(
216 statuses = ChangesetStatusModel().get_statuses(
217 c.rhodecode_db_repo.repo_id, commit.raw_id,
217 c.rhodecode_db_repo.repo_id, commit.raw_id,
218 with_revisions=True)
218 with_revisions=True)
219 prs = set(st.pull_request for st in statuses
219 prs = set(st.pull_request for st in statuses
220 if st is st.pull_request is not None)
220 if st.pull_request is not None)
221
222 # from associated statuses, check the pull requests, and
221 # from associated statuses, check the pull requests, and
223 # show comments from them
222 # show comments from them
224 for pr in prs:
223 for pr in prs:
225 c.comments.extend(pr.comments)
224 c.comments.extend(pr.comments)
226
225
227 # Iterate over ranges (default commit view is always one commit)
226 # Iterate over ranges (default commit view is always one commit)
228 for commit in c.commit_ranges:
227 for commit in c.commit_ranges:
229 c.changes[commit.raw_id] = []
228 c.changes[commit.raw_id] = []
230
229
231 commit2 = commit
230 commit2 = commit
232 commit1 = commit.parents[0] if commit.parents else EmptyCommit()
231 commit1 = commit.parents[0] if commit.parents else EmptyCommit()
233
232
234 _diff = c.rhodecode_repo.get_diff(
233 _diff = c.rhodecode_repo.get_diff(
235 commit1, commit2,
234 commit1, commit2,
236 ignore_whitespace=ign_whitespace_lcl, context=context_lcl)
235 ignore_whitespace=ign_whitespace_lcl, context=context_lcl)
237 diff_processor = diffs.DiffProcessor(
236 diff_processor = diffs.DiffProcessor(
238 _diff, format='newdiff', diff_limit=diff_limit,
237 _diff, format='newdiff', diff_limit=diff_limit,
239 file_limit=file_limit, show_full_diff=fulldiff)
238 file_limit=file_limit, show_full_diff=fulldiff)
240
239
241 commit_changes = OrderedDict()
240 commit_changes = OrderedDict()
242 if method == 'show':
241 if method == 'show':
243 _parsed = diff_processor.prepare()
242 _parsed = diff_processor.prepare()
244 c.limited_diff = isinstance(_parsed, diffs.LimitedDiffContainer)
243 c.limited_diff = isinstance(_parsed, diffs.LimitedDiffContainer)
245
244
246 _parsed = diff_processor.prepare()
245 _parsed = diff_processor.prepare()
247
246
248 def _node_getter(commit):
247 def _node_getter(commit):
249 def get_node(fname):
248 def get_node(fname):
250 try:
249 try:
251 return commit.get_node(fname)
250 return commit.get_node(fname)
252 except NodeDoesNotExistError:
251 except NodeDoesNotExistError:
253 return None
252 return None
254 return get_node
253 return get_node
255
254
256 inline_comments = ChangesetCommentsModel().get_inline_comments(
255 inline_comments = ChangesetCommentsModel().get_inline_comments(
257 c.rhodecode_db_repo.repo_id, revision=commit.raw_id)
256 c.rhodecode_db_repo.repo_id, revision=commit.raw_id)
258 c.inline_cnt += len(inline_comments)
257 c.inline_cnt += len(inline_comments)
259
258
260 diffset = codeblocks.DiffSet(
259 diffset = codeblocks.DiffSet(
261 repo_name=c.repo_name,
260 repo_name=c.repo_name,
262 source_node_getter=_node_getter(commit1),
261 source_node_getter=_node_getter(commit1),
263 target_node_getter=_node_getter(commit2),
262 target_node_getter=_node_getter(commit2),
264 comments=inline_comments
263 comments=inline_comments
265 ).render_patchset(_parsed, commit1.raw_id, commit2.raw_id)
264 ).render_patchset(_parsed, commit1.raw_id, commit2.raw_id)
266 c.changes[commit.raw_id] = diffset
265 c.changes[commit.raw_id] = diffset
267 else:
266 else:
268 # downloads/raw we only need RAW diff nothing else
267 # downloads/raw we only need RAW diff nothing else
269 diff = diff_processor.as_raw()
268 diff = diff_processor.as_raw()
270 c.changes[commit.raw_id] = [None, None, None, None, diff, None, None]
269 c.changes[commit.raw_id] = [None, None, None, None, diff, None, None]
271
270
272 # sort comments by how they were generated
271 # sort comments by how they were generated
273 c.comments = sorted(c.comments, key=lambda x: x.comment_id)
272 c.comments = sorted(c.comments, key=lambda x: x.comment_id)
274
273
275
274
276 if len(c.commit_ranges) == 1:
275 if len(c.commit_ranges) == 1:
277 c.commit = c.commit_ranges[0]
276 c.commit = c.commit_ranges[0]
278 c.parent_tmpl = ''.join(
277 c.parent_tmpl = ''.join(
279 '# Parent %s\n' % x.raw_id for x in c.commit.parents)
278 '# Parent %s\n' % x.raw_id for x in c.commit.parents)
280 if method == 'download':
279 if method == 'download':
281 response.content_type = 'text/plain'
280 response.content_type = 'text/plain'
282 response.content_disposition = (
281 response.content_disposition = (
283 'attachment; filename=%s.diff' % commit_id_range[:12])
282 'attachment; filename=%s.diff' % commit_id_range[:12])
284 return diff
283 return diff
285 elif method == 'patch':
284 elif method == 'patch':
286 response.content_type = 'text/plain'
285 response.content_type = 'text/plain'
287 c.diff = safe_unicode(diff)
286 c.diff = safe_unicode(diff)
288 return render('changeset/patch_changeset.html')
287 return render('changeset/patch_changeset.html')
289 elif method == 'raw':
288 elif method == 'raw':
290 response.content_type = 'text/plain'
289 response.content_type = 'text/plain'
291 return diff
290 return diff
292 elif method == 'show':
291 elif method == 'show':
293 if len(c.commit_ranges) == 1:
292 if len(c.commit_ranges) == 1:
294 return render('changeset/changeset.html')
293 return render('changeset/changeset.html')
295 else:
294 else:
296 c.ancestor = None
295 c.ancestor = None
297 c.target_repo = c.rhodecode_db_repo
296 c.target_repo = c.rhodecode_db_repo
298 return render('changeset/changeset_range.html')
297 return render('changeset/changeset_range.html')
299
298
300 @LoginRequired()
299 @LoginRequired()
301 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
300 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
302 'repository.admin')
301 'repository.admin')
303 def index(self, revision, method='show'):
302 def index(self, revision, method='show'):
304 return self._index(revision, method=method)
303 return self._index(revision, method=method)
305
304
306 @LoginRequired()
305 @LoginRequired()
307 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
306 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
308 'repository.admin')
307 'repository.admin')
309 def changeset_raw(self, revision):
308 def changeset_raw(self, revision):
310 return self._index(revision, method='raw')
309 return self._index(revision, method='raw')
311
310
312 @LoginRequired()
311 @LoginRequired()
313 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
312 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
314 'repository.admin')
313 'repository.admin')
315 def changeset_patch(self, revision):
314 def changeset_patch(self, revision):
316 return self._index(revision, method='patch')
315 return self._index(revision, method='patch')
317
316
318 @LoginRequired()
317 @LoginRequired()
319 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
318 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
320 'repository.admin')
319 'repository.admin')
321 def changeset_download(self, revision):
320 def changeset_download(self, revision):
322 return self._index(revision, method='download')
321 return self._index(revision, method='download')
323
322
324 @LoginRequired()
323 @LoginRequired()
325 @NotAnonymous()
324 @NotAnonymous()
326 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
325 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
327 'repository.admin')
326 'repository.admin')
328 @auth.CSRFRequired()
327 @auth.CSRFRequired()
329 @jsonify
328 @jsonify
330 def comment(self, repo_name, revision):
329 def comment(self, repo_name, revision):
331 commit_id = revision
330 commit_id = revision
332 status = request.POST.get('changeset_status', None)
331 status = request.POST.get('changeset_status', None)
333 text = request.POST.get('text')
332 text = request.POST.get('text')
334 if status:
333 if status:
335 text = text or (_('Status change %(transition_icon)s %(status)s')
334 text = text or (_('Status change %(transition_icon)s %(status)s')
336 % {'transition_icon': '>',
335 % {'transition_icon': '>',
337 'status': ChangesetStatus.get_status_lbl(status)})
336 'status': ChangesetStatus.get_status_lbl(status)})
338
337
339 multi_commit_ids = filter(
338 multi_commit_ids = filter(
340 lambda s: s not in ['', None],
339 lambda s: s not in ['', None],
341 request.POST.get('commit_ids', '').split(','),)
340 request.POST.get('commit_ids', '').split(','),)
342
341
343 commit_ids = multi_commit_ids or [commit_id]
342 commit_ids = multi_commit_ids or [commit_id]
344 comment = None
343 comment = None
345 for current_id in filter(None, commit_ids):
344 for current_id in filter(None, commit_ids):
346 c.co = comment = ChangesetCommentsModel().create(
345 c.co = comment = ChangesetCommentsModel().create(
347 text=text,
346 text=text,
348 repo=c.rhodecode_db_repo.repo_id,
347 repo=c.rhodecode_db_repo.repo_id,
349 user=c.rhodecode_user.user_id,
348 user=c.rhodecode_user.user_id,
350 revision=current_id,
349 revision=current_id,
351 f_path=request.POST.get('f_path'),
350 f_path=request.POST.get('f_path'),
352 line_no=request.POST.get('line'),
351 line_no=request.POST.get('line'),
353 status_change=(ChangesetStatus.get_status_lbl(status)
352 status_change=(ChangesetStatus.get_status_lbl(status)
354 if status else None),
353 if status else None),
355 status_change_type=status
354 status_change_type=status
356 )
355 )
357 # get status if set !
356 # get status if set !
358 if status:
357 if status:
359 # if latest status was from pull request and it's closed
358 # if latest status was from pull request and it's closed
360 # disallow changing status !
359 # disallow changing status !
361 # dont_allow_on_closed_pull_request = True !
360 # dont_allow_on_closed_pull_request = True !
362
361
363 try:
362 try:
364 ChangesetStatusModel().set_status(
363 ChangesetStatusModel().set_status(
365 c.rhodecode_db_repo.repo_id,
364 c.rhodecode_db_repo.repo_id,
366 status,
365 status,
367 c.rhodecode_user.user_id,
366 c.rhodecode_user.user_id,
368 comment,
367 comment,
369 revision=current_id,
368 revision=current_id,
370 dont_allow_on_closed_pull_request=True
369 dont_allow_on_closed_pull_request=True
371 )
370 )
372 except StatusChangeOnClosedPullRequestError:
371 except StatusChangeOnClosedPullRequestError:
373 msg = _('Changing the status of a commit associated with '
372 msg = _('Changing the status of a commit associated with '
374 'a closed pull request is not allowed')
373 'a closed pull request is not allowed')
375 log.exception(msg)
374 log.exception(msg)
376 h.flash(msg, category='warning')
375 h.flash(msg, category='warning')
377 return redirect(h.url(
376 return redirect(h.url(
378 'changeset_home', repo_name=repo_name,
377 'changeset_home', repo_name=repo_name,
379 revision=current_id))
378 revision=current_id))
380
379
381 # finalize, commit and redirect
380 # finalize, commit and redirect
382 Session().commit()
381 Session().commit()
383
382
384 data = {
383 data = {
385 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
384 'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
386 }
385 }
387 if comment:
386 if comment:
388 data.update(comment.get_dict())
387 data.update(comment.get_dict())
389 data.update({'rendered_text':
388 data.update({'rendered_text':
390 render('changeset/changeset_comment_block.html')})
389 render('changeset/changeset_comment_block.html')})
391
390
392 return data
391 return data
393
392
394 @LoginRequired()
393 @LoginRequired()
395 @NotAnonymous()
394 @NotAnonymous()
396 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
395 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
397 'repository.admin')
396 'repository.admin')
398 @auth.CSRFRequired()
397 @auth.CSRFRequired()
399 def preview_comment(self):
398 def preview_comment(self):
400 # Technically a CSRF token is not needed as no state changes with this
399 # Technically a CSRF token is not needed as no state changes with this
401 # call. However, as this is a POST is better to have it, so automated
400 # call. However, as this is a POST is better to have it, so automated
402 # tools don't flag it as potential CSRF.
401 # tools don't flag it as potential CSRF.
403 # Post is required because the payload could be bigger than the maximum
402 # Post is required because the payload could be bigger than the maximum
404 # allowed by GET.
403 # allowed by GET.
405 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
404 if not request.environ.get('HTTP_X_PARTIAL_XHR'):
406 raise HTTPBadRequest()
405 raise HTTPBadRequest()
407 text = request.POST.get('text')
406 text = request.POST.get('text')
408 renderer = request.POST.get('renderer') or 'rst'
407 renderer = request.POST.get('renderer') or 'rst'
409 if text:
408 if text:
410 return h.render(text, renderer=renderer, mentions=True)
409 return h.render(text, renderer=renderer, mentions=True)
411 return ''
410 return ''
412
411
413 @LoginRequired()
412 @LoginRequired()
414 @NotAnonymous()
413 @NotAnonymous()
415 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
414 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
416 'repository.admin')
415 'repository.admin')
417 @auth.CSRFRequired()
416 @auth.CSRFRequired()
418 @jsonify
417 @jsonify
419 def delete_comment(self, repo_name, comment_id):
418 def delete_comment(self, repo_name, comment_id):
420 comment = ChangesetComment.get(comment_id)
419 comment = ChangesetComment.get(comment_id)
421 owner = (comment.author.user_id == c.rhodecode_user.user_id)
420 owner = (comment.author.user_id == c.rhodecode_user.user_id)
422 is_repo_admin = h.HasRepoPermissionAny('repository.admin')(c.repo_name)
421 is_repo_admin = h.HasRepoPermissionAny('repository.admin')(c.repo_name)
423 if h.HasPermissionAny('hg.admin')() or is_repo_admin or owner:
422 if h.HasPermissionAny('hg.admin')() or is_repo_admin or owner:
424 ChangesetCommentsModel().delete(comment=comment)
423 ChangesetCommentsModel().delete(comment=comment)
425 Session().commit()
424 Session().commit()
426 return True
425 return True
427 else:
426 else:
428 raise HTTPForbidden()
427 raise HTTPForbidden()
429
428
430 @LoginRequired()
429 @LoginRequired()
431 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
430 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
432 'repository.admin')
431 'repository.admin')
433 @jsonify
432 @jsonify
434 def changeset_info(self, repo_name, revision):
433 def changeset_info(self, repo_name, revision):
435 if request.is_xhr:
434 if request.is_xhr:
436 try:
435 try:
437 return c.rhodecode_repo.get_commit(commit_id=revision)
436 return c.rhodecode_repo.get_commit(commit_id=revision)
438 except CommitDoesNotExistError as e:
437 except CommitDoesNotExistError as e:
439 return EmptyCommit(message=str(e))
438 return EmptyCommit(message=str(e))
440 else:
439 else:
441 raise HTTPBadRequest()
440 raise HTTPBadRequest()
442
441
443 @LoginRequired()
442 @LoginRequired()
444 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
443 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
445 'repository.admin')
444 'repository.admin')
446 @jsonify
445 @jsonify
447 def changeset_children(self, repo_name, revision):
446 def changeset_children(self, repo_name, revision):
448 if request.is_xhr:
447 if request.is_xhr:
449 commit = c.rhodecode_repo.get_commit(commit_id=revision)
448 commit = c.rhodecode_repo.get_commit(commit_id=revision)
450 result = {"results": commit.children}
449 result = {"results": commit.children}
451 return result
450 return result
452 else:
451 else:
453 raise HTTPBadRequest()
452 raise HTTPBadRequest()
454
453
455 @LoginRequired()
454 @LoginRequired()
456 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
455 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
457 'repository.admin')
456 'repository.admin')
458 @jsonify
457 @jsonify
459 def changeset_parents(self, repo_name, revision):
458 def changeset_parents(self, repo_name, revision):
460 if request.is_xhr:
459 if request.is_xhr:
461 commit = c.rhodecode_repo.get_commit(commit_id=revision)
460 commit = c.rhodecode_repo.get_commit(commit_id=revision)
462 result = {"results": commit.parents}
461 result = {"results": commit.parents}
463 return result
462 return result
464 else:
463 else:
465 raise HTTPBadRequest()
464 raise HTTPBadRequest()
General Comments 0
You need to be logged in to leave comments. Login now