##// END OF EJS Templates
commits: add protection against 500 errors on commit children/parents page.
marcink -
r2150:82423302 default
parent child Browse files
Show More
@@ -1,554 +1,563 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2017 RhodeCode GmbH
3 # Copyright (C) 2010-2017 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 import logging
22 import logging
23 import collections
23 import collections
24
24
25 from pyramid.httpexceptions import HTTPNotFound, HTTPBadRequest, HTTPFound
25 from pyramid.httpexceptions import HTTPNotFound, HTTPBadRequest, HTTPFound
26 from pyramid.view import view_config
26 from pyramid.view import view_config
27 from pyramid.renderers import render
27 from pyramid.renderers import render
28 from pyramid.response import Response
28 from pyramid.response import Response
29
29
30 from rhodecode.apps._base import RepoAppView
30 from rhodecode.apps._base import RepoAppView
31
31
32 from rhodecode.lib import diffs, codeblocks
32 from rhodecode.lib import diffs, codeblocks
33 from rhodecode.lib.auth import (
33 from rhodecode.lib.auth import (
34 LoginRequired, HasRepoPermissionAnyDecorator, NotAnonymous, CSRFRequired)
34 LoginRequired, HasRepoPermissionAnyDecorator, NotAnonymous, CSRFRequired)
35
35
36 from rhodecode.lib.compat import OrderedDict
36 from rhodecode.lib.compat import OrderedDict
37 from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError
37 from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError
38 import rhodecode.lib.helpers as h
38 import rhodecode.lib.helpers as h
39 from rhodecode.lib.utils2 import safe_unicode
39 from rhodecode.lib.utils2 import safe_unicode
40 from rhodecode.lib.vcs.backends.base import EmptyCommit
40 from rhodecode.lib.vcs.backends.base import EmptyCommit
41 from rhodecode.lib.vcs.exceptions import (
41 from rhodecode.lib.vcs.exceptions import (
42 RepositoryError, CommitDoesNotExistError, NodeDoesNotExistError)
42 RepositoryError, CommitDoesNotExistError, NodeDoesNotExistError)
43 from rhodecode.model.db import ChangesetComment, ChangesetStatus
43 from rhodecode.model.db import ChangesetComment, ChangesetStatus
44 from rhodecode.model.changeset_status import ChangesetStatusModel
44 from rhodecode.model.changeset_status import ChangesetStatusModel
45 from rhodecode.model.comment import CommentsModel
45 from rhodecode.model.comment import CommentsModel
46 from rhodecode.model.meta import Session
46 from rhodecode.model.meta import Session
47
47
48
48
49 log = logging.getLogger(__name__)
49 log = logging.getLogger(__name__)
50
50
51
51
52 def _update_with_GET(params, request):
52 def _update_with_GET(params, request):
53 for k in ['diff1', 'diff2', 'diff']:
53 for k in ['diff1', 'diff2', 'diff']:
54 params[k] += request.GET.getall(k)
54 params[k] += request.GET.getall(k)
55
55
56
56
57 def get_ignore_ws(fid, request):
57 def get_ignore_ws(fid, request):
58 ig_ws_global = request.GET.get('ignorews')
58 ig_ws_global = request.GET.get('ignorews')
59 ig_ws = filter(lambda k: k.startswith('WS'), request.GET.getall(fid))
59 ig_ws = filter(lambda k: k.startswith('WS'), request.GET.getall(fid))
60 if ig_ws:
60 if ig_ws:
61 try:
61 try:
62 return int(ig_ws[0].split(':')[-1])
62 return int(ig_ws[0].split(':')[-1])
63 except Exception:
63 except Exception:
64 pass
64 pass
65 return ig_ws_global
65 return ig_ws_global
66
66
67
67
68 def _ignorews_url(request, fileid=None):
68 def _ignorews_url(request, fileid=None):
69 _ = request.translate
69 _ = request.translate
70 fileid = str(fileid) if fileid else None
70 fileid = str(fileid) if fileid else None
71 params = collections.defaultdict(list)
71 params = collections.defaultdict(list)
72 _update_with_GET(params, request)
72 _update_with_GET(params, request)
73 label = _('Show whitespace')
73 label = _('Show whitespace')
74 tooltiplbl = _('Show whitespace for all diffs')
74 tooltiplbl = _('Show whitespace for all diffs')
75 ig_ws = get_ignore_ws(fileid, request)
75 ig_ws = get_ignore_ws(fileid, request)
76 ln_ctx = get_line_ctx(fileid, request)
76 ln_ctx = get_line_ctx(fileid, request)
77
77
78 if ig_ws is None:
78 if ig_ws is None:
79 params['ignorews'] += [1]
79 params['ignorews'] += [1]
80 label = _('Ignore whitespace')
80 label = _('Ignore whitespace')
81 tooltiplbl = _('Ignore whitespace for all diffs')
81 tooltiplbl = _('Ignore whitespace for all diffs')
82 ctx_key = 'context'
82 ctx_key = 'context'
83 ctx_val = ln_ctx
83 ctx_val = ln_ctx
84
84
85 # if we have passed in ln_ctx pass it along to our params
85 # if we have passed in ln_ctx pass it along to our params
86 if ln_ctx:
86 if ln_ctx:
87 params[ctx_key] += [ctx_val]
87 params[ctx_key] += [ctx_val]
88
88
89 if fileid:
89 if fileid:
90 params['anchor'] = 'a_' + fileid
90 params['anchor'] = 'a_' + fileid
91 return h.link_to(label, request.current_route_path(_query=params),
91 return h.link_to(label, request.current_route_path(_query=params),
92 title=tooltiplbl, class_='tooltip')
92 title=tooltiplbl, class_='tooltip')
93
93
94
94
95 def get_line_ctx(fid, request):
95 def get_line_ctx(fid, request):
96 ln_ctx_global = request.GET.get('context')
96 ln_ctx_global = request.GET.get('context')
97 if fid:
97 if fid:
98 ln_ctx = filter(lambda k: k.startswith('C'), request.GET.getall(fid))
98 ln_ctx = filter(lambda k: k.startswith('C'), request.GET.getall(fid))
99 else:
99 else:
100 _ln_ctx = filter(lambda k: k.startswith('C'), request.GET)
100 _ln_ctx = filter(lambda k: k.startswith('C'), request.GET)
101 ln_ctx = request.GET.get(_ln_ctx[0]) if _ln_ctx else ln_ctx_global
101 ln_ctx = request.GET.get(_ln_ctx[0]) if _ln_ctx else ln_ctx_global
102 if ln_ctx:
102 if ln_ctx:
103 ln_ctx = [ln_ctx]
103 ln_ctx = [ln_ctx]
104
104
105 if ln_ctx:
105 if ln_ctx:
106 retval = ln_ctx[0].split(':')[-1]
106 retval = ln_ctx[0].split(':')[-1]
107 else:
107 else:
108 retval = ln_ctx_global
108 retval = ln_ctx_global
109
109
110 try:
110 try:
111 return int(retval)
111 return int(retval)
112 except Exception:
112 except Exception:
113 return 3
113 return 3
114
114
115
115
116 def _context_url(request, fileid=None):
116 def _context_url(request, fileid=None):
117 """
117 """
118 Generates a url for context lines.
118 Generates a url for context lines.
119
119
120 :param fileid:
120 :param fileid:
121 """
121 """
122
122
123 _ = request.translate
123 _ = request.translate
124 fileid = str(fileid) if fileid else None
124 fileid = str(fileid) if fileid else None
125 ig_ws = get_ignore_ws(fileid, request)
125 ig_ws = get_ignore_ws(fileid, request)
126 ln_ctx = (get_line_ctx(fileid, request) or 3) * 2
126 ln_ctx = (get_line_ctx(fileid, request) or 3) * 2
127
127
128 params = collections.defaultdict(list)
128 params = collections.defaultdict(list)
129 _update_with_GET(params, request)
129 _update_with_GET(params, request)
130
130
131 if ln_ctx > 0:
131 if ln_ctx > 0:
132 params['context'] += [ln_ctx]
132 params['context'] += [ln_ctx]
133
133
134 if ig_ws:
134 if ig_ws:
135 ig_ws_key = 'ignorews'
135 ig_ws_key = 'ignorews'
136 ig_ws_val = 1
136 ig_ws_val = 1
137 params[ig_ws_key] += [ig_ws_val]
137 params[ig_ws_key] += [ig_ws_val]
138
138
139 lbl = _('Increase context')
139 lbl = _('Increase context')
140 tooltiplbl = _('Increase context for all diffs')
140 tooltiplbl = _('Increase context for all diffs')
141
141
142 if fileid:
142 if fileid:
143 params['anchor'] = 'a_' + fileid
143 params['anchor'] = 'a_' + fileid
144 return h.link_to(lbl, request.current_route_path(_query=params),
144 return h.link_to(lbl, request.current_route_path(_query=params),
145 title=tooltiplbl, class_='tooltip')
145 title=tooltiplbl, class_='tooltip')
146
146
147
147
148 class RepoCommitsView(RepoAppView):
148 class RepoCommitsView(RepoAppView):
149 def load_default_context(self):
149 def load_default_context(self):
150 c = self._get_local_tmpl_context(include_app_defaults=True)
150 c = self._get_local_tmpl_context(include_app_defaults=True)
151 c.rhodecode_repo = self.rhodecode_vcs_repo
151 c.rhodecode_repo = self.rhodecode_vcs_repo
152
152
153 self._register_global_c(c)
153 self._register_global_c(c)
154 return c
154 return c
155
155
156 def _commit(self, commit_id_range, method):
156 def _commit(self, commit_id_range, method):
157 _ = self.request.translate
157 _ = self.request.translate
158 c = self.load_default_context()
158 c = self.load_default_context()
159 c.ignorews_url = _ignorews_url
159 c.ignorews_url = _ignorews_url
160 c.context_url = _context_url
160 c.context_url = _context_url
161 c.fulldiff = self.request.GET.get('fulldiff')
161 c.fulldiff = self.request.GET.get('fulldiff')
162
162
163 # fetch global flags of ignore ws or context lines
163 # fetch global flags of ignore ws or context lines
164 context_lcl = get_line_ctx('', self.request)
164 context_lcl = get_line_ctx('', self.request)
165 ign_whitespace_lcl = get_ignore_ws('', self.request)
165 ign_whitespace_lcl = get_ignore_ws('', self.request)
166
166
167 # diff_limit will cut off the whole diff if the limit is applied
167 # diff_limit will cut off the whole diff if the limit is applied
168 # otherwise it will just hide the big files from the front-end
168 # otherwise it will just hide the big files from the front-end
169 diff_limit = c.visual.cut_off_limit_diff
169 diff_limit = c.visual.cut_off_limit_diff
170 file_limit = c.visual.cut_off_limit_file
170 file_limit = c.visual.cut_off_limit_file
171
171
172 # get ranges of commit ids if preset
172 # get ranges of commit ids if preset
173 commit_range = commit_id_range.split('...')[:2]
173 commit_range = commit_id_range.split('...')[:2]
174
174
175 try:
175 try:
176 pre_load = ['affected_files', 'author', 'branch', 'date',
176 pre_load = ['affected_files', 'author', 'branch', 'date',
177 'message', 'parents']
177 'message', 'parents']
178
178
179 if len(commit_range) == 2:
179 if len(commit_range) == 2:
180 commits = self.rhodecode_vcs_repo.get_commits(
180 commits = self.rhodecode_vcs_repo.get_commits(
181 start_id=commit_range[0], end_id=commit_range[1],
181 start_id=commit_range[0], end_id=commit_range[1],
182 pre_load=pre_load)
182 pre_load=pre_load)
183 commits = list(commits)
183 commits = list(commits)
184 else:
184 else:
185 commits = [self.rhodecode_vcs_repo.get_commit(
185 commits = [self.rhodecode_vcs_repo.get_commit(
186 commit_id=commit_id_range, pre_load=pre_load)]
186 commit_id=commit_id_range, pre_load=pre_load)]
187
187
188 c.commit_ranges = commits
188 c.commit_ranges = commits
189 if not c.commit_ranges:
189 if not c.commit_ranges:
190 raise RepositoryError(
190 raise RepositoryError(
191 'The commit range returned an empty result')
191 'The commit range returned an empty result')
192 except CommitDoesNotExistError:
192 except CommitDoesNotExistError:
193 msg = _('No such commit exists for this repository')
193 msg = _('No such commit exists for this repository')
194 h.flash(msg, category='error')
194 h.flash(msg, category='error')
195 raise HTTPNotFound()
195 raise HTTPNotFound()
196 except Exception:
196 except Exception:
197 log.exception("General failure")
197 log.exception("General failure")
198 raise HTTPNotFound()
198 raise HTTPNotFound()
199
199
200 c.changes = OrderedDict()
200 c.changes = OrderedDict()
201 c.lines_added = 0
201 c.lines_added = 0
202 c.lines_deleted = 0
202 c.lines_deleted = 0
203
203
204 # auto collapse if we have more than limit
204 # auto collapse if we have more than limit
205 collapse_limit = diffs.DiffProcessor._collapse_commits_over
205 collapse_limit = diffs.DiffProcessor._collapse_commits_over
206 c.collapse_all_commits = len(c.commit_ranges) > collapse_limit
206 c.collapse_all_commits = len(c.commit_ranges) > collapse_limit
207
207
208 c.commit_statuses = ChangesetStatus.STATUSES
208 c.commit_statuses = ChangesetStatus.STATUSES
209 c.inline_comments = []
209 c.inline_comments = []
210 c.files = []
210 c.files = []
211
211
212 c.statuses = []
212 c.statuses = []
213 c.comments = []
213 c.comments = []
214 c.unresolved_comments = []
214 c.unresolved_comments = []
215 if len(c.commit_ranges) == 1:
215 if len(c.commit_ranges) == 1:
216 commit = c.commit_ranges[0]
216 commit = c.commit_ranges[0]
217 c.comments = CommentsModel().get_comments(
217 c.comments = CommentsModel().get_comments(
218 self.db_repo.repo_id,
218 self.db_repo.repo_id,
219 revision=commit.raw_id)
219 revision=commit.raw_id)
220 c.statuses.append(ChangesetStatusModel().get_status(
220 c.statuses.append(ChangesetStatusModel().get_status(
221 self.db_repo.repo_id, commit.raw_id))
221 self.db_repo.repo_id, commit.raw_id))
222 # comments from PR
222 # comments from PR
223 statuses = ChangesetStatusModel().get_statuses(
223 statuses = ChangesetStatusModel().get_statuses(
224 self.db_repo.repo_id, commit.raw_id,
224 self.db_repo.repo_id, commit.raw_id,
225 with_revisions=True)
225 with_revisions=True)
226 prs = set(st.pull_request for st in statuses
226 prs = set(st.pull_request for st in statuses
227 if st.pull_request is not None)
227 if st.pull_request is not None)
228 # from associated statuses, check the pull requests, and
228 # from associated statuses, check the pull requests, and
229 # show comments from them
229 # show comments from them
230 for pr in prs:
230 for pr in prs:
231 c.comments.extend(pr.comments)
231 c.comments.extend(pr.comments)
232
232
233 c.unresolved_comments = CommentsModel()\
233 c.unresolved_comments = CommentsModel()\
234 .get_commit_unresolved_todos(commit.raw_id)
234 .get_commit_unresolved_todos(commit.raw_id)
235
235
236 diff = None
236 diff = None
237 # Iterate over ranges (default commit view is always one commit)
237 # Iterate over ranges (default commit view is always one commit)
238 for commit in c.commit_ranges:
238 for commit in c.commit_ranges:
239 c.changes[commit.raw_id] = []
239 c.changes[commit.raw_id] = []
240
240
241 commit2 = commit
241 commit2 = commit
242 commit1 = commit.parents[0] if commit.parents else EmptyCommit()
242 commit1 = commit.parents[0] if commit.parents else EmptyCommit()
243
243
244 _diff = self.rhodecode_vcs_repo.get_diff(
244 _diff = self.rhodecode_vcs_repo.get_diff(
245 commit1, commit2,
245 commit1, commit2,
246 ignore_whitespace=ign_whitespace_lcl, context=context_lcl)
246 ignore_whitespace=ign_whitespace_lcl, context=context_lcl)
247 diff_processor = diffs.DiffProcessor(
247 diff_processor = diffs.DiffProcessor(
248 _diff, format='newdiff', diff_limit=diff_limit,
248 _diff, format='newdiff', diff_limit=diff_limit,
249 file_limit=file_limit, show_full_diff=c.fulldiff)
249 file_limit=file_limit, show_full_diff=c.fulldiff)
250
250
251 commit_changes = OrderedDict()
251 commit_changes = OrderedDict()
252 if method == 'show':
252 if method == 'show':
253 _parsed = diff_processor.prepare()
253 _parsed = diff_processor.prepare()
254 c.limited_diff = isinstance(_parsed, diffs.LimitedDiffContainer)
254 c.limited_diff = isinstance(_parsed, diffs.LimitedDiffContainer)
255
255
256 _parsed = diff_processor.prepare()
256 _parsed = diff_processor.prepare()
257
257
258 def _node_getter(commit):
258 def _node_getter(commit):
259 def get_node(fname):
259 def get_node(fname):
260 try:
260 try:
261 return commit.get_node(fname)
261 return commit.get_node(fname)
262 except NodeDoesNotExistError:
262 except NodeDoesNotExistError:
263 return None
263 return None
264 return get_node
264 return get_node
265
265
266 inline_comments = CommentsModel().get_inline_comments(
266 inline_comments = CommentsModel().get_inline_comments(
267 self.db_repo.repo_id, revision=commit.raw_id)
267 self.db_repo.repo_id, revision=commit.raw_id)
268 c.inline_cnt = CommentsModel().get_inline_comments_count(
268 c.inline_cnt = CommentsModel().get_inline_comments_count(
269 inline_comments)
269 inline_comments)
270
270
271 diffset = codeblocks.DiffSet(
271 diffset = codeblocks.DiffSet(
272 repo_name=self.db_repo_name,
272 repo_name=self.db_repo_name,
273 source_node_getter=_node_getter(commit1),
273 source_node_getter=_node_getter(commit1),
274 target_node_getter=_node_getter(commit2),
274 target_node_getter=_node_getter(commit2),
275 comments=inline_comments)
275 comments=inline_comments)
276 diffset = diffset.render_patchset(
276 diffset = diffset.render_patchset(
277 _parsed, commit1.raw_id, commit2.raw_id)
277 _parsed, commit1.raw_id, commit2.raw_id)
278
278
279 c.changes[commit.raw_id] = diffset
279 c.changes[commit.raw_id] = diffset
280 else:
280 else:
281 # downloads/raw we only need RAW diff nothing else
281 # downloads/raw we only need RAW diff nothing else
282 diff = diff_processor.as_raw()
282 diff = diff_processor.as_raw()
283 c.changes[commit.raw_id] = [None, None, None, None, diff, None, None]
283 c.changes[commit.raw_id] = [None, None, None, None, diff, None, None]
284
284
285 # sort comments by how they were generated
285 # sort comments by how they were generated
286 c.comments = sorted(c.comments, key=lambda x: x.comment_id)
286 c.comments = sorted(c.comments, key=lambda x: x.comment_id)
287
287
288 if len(c.commit_ranges) == 1:
288 if len(c.commit_ranges) == 1:
289 c.commit = c.commit_ranges[0]
289 c.commit = c.commit_ranges[0]
290 c.parent_tmpl = ''.join(
290 c.parent_tmpl = ''.join(
291 '# Parent %s\n' % x.raw_id for x in c.commit.parents)
291 '# Parent %s\n' % x.raw_id for x in c.commit.parents)
292
292
293 if method == 'download':
293 if method == 'download':
294 response = Response(diff)
294 response = Response(diff)
295 response.content_type = 'text/plain'
295 response.content_type = 'text/plain'
296 response.content_disposition = (
296 response.content_disposition = (
297 'attachment; filename=%s.diff' % commit_id_range[:12])
297 'attachment; filename=%s.diff' % commit_id_range[:12])
298 return response
298 return response
299 elif method == 'patch':
299 elif method == 'patch':
300 c.diff = safe_unicode(diff)
300 c.diff = safe_unicode(diff)
301 patch = render(
301 patch = render(
302 'rhodecode:templates/changeset/patch_changeset.mako',
302 'rhodecode:templates/changeset/patch_changeset.mako',
303 self._get_template_context(c), self.request)
303 self._get_template_context(c), self.request)
304 response = Response(patch)
304 response = Response(patch)
305 response.content_type = 'text/plain'
305 response.content_type = 'text/plain'
306 return response
306 return response
307 elif method == 'raw':
307 elif method == 'raw':
308 response = Response(diff)
308 response = Response(diff)
309 response.content_type = 'text/plain'
309 response.content_type = 'text/plain'
310 return response
310 return response
311 elif method == 'show':
311 elif method == 'show':
312 if len(c.commit_ranges) == 1:
312 if len(c.commit_ranges) == 1:
313 html = render(
313 html = render(
314 'rhodecode:templates/changeset/changeset.mako',
314 'rhodecode:templates/changeset/changeset.mako',
315 self._get_template_context(c), self.request)
315 self._get_template_context(c), self.request)
316 return Response(html)
316 return Response(html)
317 else:
317 else:
318 c.ancestor = None
318 c.ancestor = None
319 c.target_repo = self.db_repo
319 c.target_repo = self.db_repo
320 html = render(
320 html = render(
321 'rhodecode:templates/changeset/changeset_range.mako',
321 'rhodecode:templates/changeset/changeset_range.mako',
322 self._get_template_context(c), self.request)
322 self._get_template_context(c), self.request)
323 return Response(html)
323 return Response(html)
324
324
325 raise HTTPBadRequest()
325 raise HTTPBadRequest()
326
326
327 @LoginRequired()
327 @LoginRequired()
328 @HasRepoPermissionAnyDecorator(
328 @HasRepoPermissionAnyDecorator(
329 'repository.read', 'repository.write', 'repository.admin')
329 'repository.read', 'repository.write', 'repository.admin')
330 @view_config(
330 @view_config(
331 route_name='repo_commit', request_method='GET',
331 route_name='repo_commit', request_method='GET',
332 renderer=None)
332 renderer=None)
333 def repo_commit_show(self):
333 def repo_commit_show(self):
334 commit_id = self.request.matchdict['commit_id']
334 commit_id = self.request.matchdict['commit_id']
335 return self._commit(commit_id, method='show')
335 return self._commit(commit_id, method='show')
336
336
337 @LoginRequired()
337 @LoginRequired()
338 @HasRepoPermissionAnyDecorator(
338 @HasRepoPermissionAnyDecorator(
339 'repository.read', 'repository.write', 'repository.admin')
339 'repository.read', 'repository.write', 'repository.admin')
340 @view_config(
340 @view_config(
341 route_name='repo_commit_raw', request_method='GET',
341 route_name='repo_commit_raw', request_method='GET',
342 renderer=None)
342 renderer=None)
343 @view_config(
343 @view_config(
344 route_name='repo_commit_raw_deprecated', request_method='GET',
344 route_name='repo_commit_raw_deprecated', request_method='GET',
345 renderer=None)
345 renderer=None)
346 def repo_commit_raw(self):
346 def repo_commit_raw(self):
347 commit_id = self.request.matchdict['commit_id']
347 commit_id = self.request.matchdict['commit_id']
348 return self._commit(commit_id, method='raw')
348 return self._commit(commit_id, method='raw')
349
349
350 @LoginRequired()
350 @LoginRequired()
351 @HasRepoPermissionAnyDecorator(
351 @HasRepoPermissionAnyDecorator(
352 'repository.read', 'repository.write', 'repository.admin')
352 'repository.read', 'repository.write', 'repository.admin')
353 @view_config(
353 @view_config(
354 route_name='repo_commit_patch', request_method='GET',
354 route_name='repo_commit_patch', request_method='GET',
355 renderer=None)
355 renderer=None)
356 def repo_commit_patch(self):
356 def repo_commit_patch(self):
357 commit_id = self.request.matchdict['commit_id']
357 commit_id = self.request.matchdict['commit_id']
358 return self._commit(commit_id, method='patch')
358 return self._commit(commit_id, method='patch')
359
359
360 @LoginRequired()
360 @LoginRequired()
361 @HasRepoPermissionAnyDecorator(
361 @HasRepoPermissionAnyDecorator(
362 'repository.read', 'repository.write', 'repository.admin')
362 'repository.read', 'repository.write', 'repository.admin')
363 @view_config(
363 @view_config(
364 route_name='repo_commit_download', request_method='GET',
364 route_name='repo_commit_download', request_method='GET',
365 renderer=None)
365 renderer=None)
366 def repo_commit_download(self):
366 def repo_commit_download(self):
367 commit_id = self.request.matchdict['commit_id']
367 commit_id = self.request.matchdict['commit_id']
368 return self._commit(commit_id, method='download')
368 return self._commit(commit_id, method='download')
369
369
370 @LoginRequired()
370 @LoginRequired()
371 @NotAnonymous()
371 @NotAnonymous()
372 @HasRepoPermissionAnyDecorator(
372 @HasRepoPermissionAnyDecorator(
373 'repository.read', 'repository.write', 'repository.admin')
373 'repository.read', 'repository.write', 'repository.admin')
374 @CSRFRequired()
374 @CSRFRequired()
375 @view_config(
375 @view_config(
376 route_name='repo_commit_comment_create', request_method='POST',
376 route_name='repo_commit_comment_create', request_method='POST',
377 renderer='json_ext')
377 renderer='json_ext')
378 def repo_commit_comment_create(self):
378 def repo_commit_comment_create(self):
379 _ = self.request.translate
379 _ = self.request.translate
380 commit_id = self.request.matchdict['commit_id']
380 commit_id = self.request.matchdict['commit_id']
381
381
382 c = self.load_default_context()
382 c = self.load_default_context()
383 status = self.request.POST.get('changeset_status', None)
383 status = self.request.POST.get('changeset_status', None)
384 text = self.request.POST.get('text')
384 text = self.request.POST.get('text')
385 comment_type = self.request.POST.get('comment_type')
385 comment_type = self.request.POST.get('comment_type')
386 resolves_comment_id = self.request.POST.get('resolves_comment_id', None)
386 resolves_comment_id = self.request.POST.get('resolves_comment_id', None)
387
387
388 if status:
388 if status:
389 text = text or (_('Status change %(transition_icon)s %(status)s')
389 text = text or (_('Status change %(transition_icon)s %(status)s')
390 % {'transition_icon': '>',
390 % {'transition_icon': '>',
391 'status': ChangesetStatus.get_status_lbl(status)})
391 'status': ChangesetStatus.get_status_lbl(status)})
392
392
393 multi_commit_ids = []
393 multi_commit_ids = []
394 for _commit_id in self.request.POST.get('commit_ids', '').split(','):
394 for _commit_id in self.request.POST.get('commit_ids', '').split(','):
395 if _commit_id not in ['', None, EmptyCommit.raw_id]:
395 if _commit_id not in ['', None, EmptyCommit.raw_id]:
396 if _commit_id not in multi_commit_ids:
396 if _commit_id not in multi_commit_ids:
397 multi_commit_ids.append(_commit_id)
397 multi_commit_ids.append(_commit_id)
398
398
399 commit_ids = multi_commit_ids or [commit_id]
399 commit_ids = multi_commit_ids or [commit_id]
400
400
401 comment = None
401 comment = None
402 for current_id in filter(None, commit_ids):
402 for current_id in filter(None, commit_ids):
403 comment = CommentsModel().create(
403 comment = CommentsModel().create(
404 text=text,
404 text=text,
405 repo=self.db_repo.repo_id,
405 repo=self.db_repo.repo_id,
406 user=self._rhodecode_db_user.user_id,
406 user=self._rhodecode_db_user.user_id,
407 commit_id=current_id,
407 commit_id=current_id,
408 f_path=self.request.POST.get('f_path'),
408 f_path=self.request.POST.get('f_path'),
409 line_no=self.request.POST.get('line'),
409 line_no=self.request.POST.get('line'),
410 status_change=(ChangesetStatus.get_status_lbl(status)
410 status_change=(ChangesetStatus.get_status_lbl(status)
411 if status else None),
411 if status else None),
412 status_change_type=status,
412 status_change_type=status,
413 comment_type=comment_type,
413 comment_type=comment_type,
414 resolves_comment_id=resolves_comment_id
414 resolves_comment_id=resolves_comment_id
415 )
415 )
416
416
417 # get status if set !
417 # get status if set !
418 if status:
418 if status:
419 # if latest status was from pull request and it's closed
419 # if latest status was from pull request and it's closed
420 # disallow changing status !
420 # disallow changing status !
421 # dont_allow_on_closed_pull_request = True !
421 # dont_allow_on_closed_pull_request = True !
422
422
423 try:
423 try:
424 ChangesetStatusModel().set_status(
424 ChangesetStatusModel().set_status(
425 self.db_repo.repo_id,
425 self.db_repo.repo_id,
426 status,
426 status,
427 self._rhodecode_db_user.user_id,
427 self._rhodecode_db_user.user_id,
428 comment,
428 comment,
429 revision=current_id,
429 revision=current_id,
430 dont_allow_on_closed_pull_request=True
430 dont_allow_on_closed_pull_request=True
431 )
431 )
432 except StatusChangeOnClosedPullRequestError:
432 except StatusChangeOnClosedPullRequestError:
433 msg = _('Changing the status of a commit associated with '
433 msg = _('Changing the status of a commit associated with '
434 'a closed pull request is not allowed')
434 'a closed pull request is not allowed')
435 log.exception(msg)
435 log.exception(msg)
436 h.flash(msg, category='warning')
436 h.flash(msg, category='warning')
437 raise HTTPFound(h.route_path(
437 raise HTTPFound(h.route_path(
438 'repo_commit', repo_name=self.db_repo_name,
438 'repo_commit', repo_name=self.db_repo_name,
439 commit_id=current_id))
439 commit_id=current_id))
440
440
441 # finalize, commit and redirect
441 # finalize, commit and redirect
442 Session().commit()
442 Session().commit()
443
443
444 data = {
444 data = {
445 'target_id': h.safeid(h.safe_unicode(
445 'target_id': h.safeid(h.safe_unicode(
446 self.request.POST.get('f_path'))),
446 self.request.POST.get('f_path'))),
447 }
447 }
448 if comment:
448 if comment:
449 c.co = comment
449 c.co = comment
450 rendered_comment = render(
450 rendered_comment = render(
451 'rhodecode:templates/changeset/changeset_comment_block.mako',
451 'rhodecode:templates/changeset/changeset_comment_block.mako',
452 self._get_template_context(c), self.request)
452 self._get_template_context(c), self.request)
453
453
454 data.update(comment.get_dict())
454 data.update(comment.get_dict())
455 data.update({'rendered_text': rendered_comment})
455 data.update({'rendered_text': rendered_comment})
456
456
457 return data
457 return data
458
458
459 @LoginRequired()
459 @LoginRequired()
460 @NotAnonymous()
460 @NotAnonymous()
461 @HasRepoPermissionAnyDecorator(
461 @HasRepoPermissionAnyDecorator(
462 'repository.read', 'repository.write', 'repository.admin')
462 'repository.read', 'repository.write', 'repository.admin')
463 @CSRFRequired()
463 @CSRFRequired()
464 @view_config(
464 @view_config(
465 route_name='repo_commit_comment_preview', request_method='POST',
465 route_name='repo_commit_comment_preview', request_method='POST',
466 renderer='string', xhr=True)
466 renderer='string', xhr=True)
467 def repo_commit_comment_preview(self):
467 def repo_commit_comment_preview(self):
468 # Technically a CSRF token is not needed as no state changes with this
468 # Technically a CSRF token is not needed as no state changes with this
469 # call. However, as this is a POST is better to have it, so automated
469 # call. However, as this is a POST is better to have it, so automated
470 # tools don't flag it as potential CSRF.
470 # tools don't flag it as potential CSRF.
471 # Post is required because the payload could be bigger than the maximum
471 # Post is required because the payload could be bigger than the maximum
472 # allowed by GET.
472 # allowed by GET.
473
473
474 text = self.request.POST.get('text')
474 text = self.request.POST.get('text')
475 renderer = self.request.POST.get('renderer') or 'rst'
475 renderer = self.request.POST.get('renderer') or 'rst'
476 if text:
476 if text:
477 return h.render(text, renderer=renderer, mentions=True)
477 return h.render(text, renderer=renderer, mentions=True)
478 return ''
478 return ''
479
479
480 @LoginRequired()
480 @LoginRequired()
481 @NotAnonymous()
481 @NotAnonymous()
482 @HasRepoPermissionAnyDecorator(
482 @HasRepoPermissionAnyDecorator(
483 'repository.read', 'repository.write', 'repository.admin')
483 'repository.read', 'repository.write', 'repository.admin')
484 @CSRFRequired()
484 @CSRFRequired()
485 @view_config(
485 @view_config(
486 route_name='repo_commit_comment_delete', request_method='POST',
486 route_name='repo_commit_comment_delete', request_method='POST',
487 renderer='json_ext')
487 renderer='json_ext')
488 def repo_commit_comment_delete(self):
488 def repo_commit_comment_delete(self):
489 commit_id = self.request.matchdict['commit_id']
489 commit_id = self.request.matchdict['commit_id']
490 comment_id = self.request.matchdict['comment_id']
490 comment_id = self.request.matchdict['comment_id']
491
491
492 comment = ChangesetComment.get_or_404(comment_id)
492 comment = ChangesetComment.get_or_404(comment_id)
493 if not comment:
493 if not comment:
494 log.debug('Comment with id:%s not found, skipping', comment_id)
494 log.debug('Comment with id:%s not found, skipping', comment_id)
495 # comment already deleted in another call probably
495 # comment already deleted in another call probably
496 return True
496 return True
497
497
498 is_repo_admin = h.HasRepoPermissionAny('repository.admin')(self.db_repo_name)
498 is_repo_admin = h.HasRepoPermissionAny('repository.admin')(self.db_repo_name)
499 super_admin = h.HasPermissionAny('hg.admin')()
499 super_admin = h.HasPermissionAny('hg.admin')()
500 comment_owner = (comment.author.user_id == self._rhodecode_db_user.user_id)
500 comment_owner = (comment.author.user_id == self._rhodecode_db_user.user_id)
501 is_repo_comment = comment.repo.repo_name == self.db_repo_name
501 is_repo_comment = comment.repo.repo_name == self.db_repo_name
502 comment_repo_admin = is_repo_admin and is_repo_comment
502 comment_repo_admin = is_repo_admin and is_repo_comment
503
503
504 if super_admin or comment_owner or comment_repo_admin:
504 if super_admin or comment_owner or comment_repo_admin:
505 CommentsModel().delete(comment=comment, user=self._rhodecode_db_user)
505 CommentsModel().delete(comment=comment, user=self._rhodecode_db_user)
506 Session().commit()
506 Session().commit()
507 return True
507 return True
508 else:
508 else:
509 log.warning('No permissions for user %s to delete comment_id: %s',
509 log.warning('No permissions for user %s to delete comment_id: %s',
510 self._rhodecode_db_user, comment_id)
510 self._rhodecode_db_user, comment_id)
511 raise HTTPNotFound()
511 raise HTTPNotFound()
512
512
513 @LoginRequired()
513 @LoginRequired()
514 @HasRepoPermissionAnyDecorator(
514 @HasRepoPermissionAnyDecorator(
515 'repository.read', 'repository.write', 'repository.admin')
515 'repository.read', 'repository.write', 'repository.admin')
516 @view_config(
516 @view_config(
517 route_name='repo_commit_data', request_method='GET',
517 route_name='repo_commit_data', request_method='GET',
518 renderer='json_ext', xhr=True)
518 renderer='json_ext', xhr=True)
519 def repo_commit_data(self):
519 def repo_commit_data(self):
520 commit_id = self.request.matchdict['commit_id']
520 commit_id = self.request.matchdict['commit_id']
521 self.load_default_context()
521 self.load_default_context()
522
522
523 try:
523 try:
524 return self.rhodecode_vcs_repo.get_commit(commit_id=commit_id)
524 return self.rhodecode_vcs_repo.get_commit(commit_id=commit_id)
525 except CommitDoesNotExistError as e:
525 except CommitDoesNotExistError as e:
526 return EmptyCommit(message=str(e))
526 return EmptyCommit(message=str(e))
527
527
528 @LoginRequired()
528 @LoginRequired()
529 @HasRepoPermissionAnyDecorator(
529 @HasRepoPermissionAnyDecorator(
530 'repository.read', 'repository.write', 'repository.admin')
530 'repository.read', 'repository.write', 'repository.admin')
531 @view_config(
531 @view_config(
532 route_name='repo_commit_children', request_method='GET',
532 route_name='repo_commit_children', request_method='GET',
533 renderer='json_ext', xhr=True)
533 renderer='json_ext', xhr=True)
534 def repo_commit_children(self):
534 def repo_commit_children(self):
535 commit_id = self.request.matchdict['commit_id']
535 commit_id = self.request.matchdict['commit_id']
536 self.load_default_context()
536 self.load_default_context()
537
537
538 commit = self.rhodecode_vcs_repo.get_commit(commit_id=commit_id)
538 try:
539 result = {"results": commit.children}
539 commit = self.rhodecode_vcs_repo.get_commit(commit_id=commit_id)
540 children = commit.children
541 except CommitDoesNotExistError:
542 children = []
543
544 result = {"results": children}
540 return result
545 return result
541
546
542 @LoginRequired()
547 @LoginRequired()
543 @HasRepoPermissionAnyDecorator(
548 @HasRepoPermissionAnyDecorator(
544 'repository.read', 'repository.write', 'repository.admin')
549 'repository.read', 'repository.write', 'repository.admin')
545 @view_config(
550 @view_config(
546 route_name='repo_commit_parents', request_method='GET',
551 route_name='repo_commit_parents', request_method='GET',
547 renderer='json_ext')
552 renderer='json_ext')
548 def repo_commit_parents(self):
553 def repo_commit_parents(self):
549 commit_id = self.request.matchdict['commit_id']
554 commit_id = self.request.matchdict['commit_id']
550 self.load_default_context()
555 self.load_default_context()
551
556
552 commit = self.rhodecode_vcs_repo.get_commit(commit_id=commit_id)
557 try:
553 result = {"results": commit.parents}
558 commit = self.rhodecode_vcs_repo.get_commit(commit_id=commit_id)
559 parents = commit.parents
560 except CommitDoesNotExistError:
561 parents = []
562 result = {"results": parents}
554 return result
563 return result
General Comments 0
You need to be logged in to leave comments. Login now