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