##// END OF EJS Templates
comment-history: block viewing any comment history by narrowing to specific repository which we already have permissions to.
marcink -
r4406:11f8a530 default
parent child Browse files
Show More
@@ -1,700 +1,705 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21
22 22 import logging
23 23 import collections
24 24
25 25 from pyramid.httpexceptions import HTTPNotFound, HTTPBadRequest, HTTPFound, HTTPForbidden
26 26 from pyramid.view import view_config
27 27 from pyramid.renderers import render
28 28 from pyramid.response import Response
29 29
30 30 from rhodecode.apps._base import RepoAppView
31 31 from rhodecode.apps.file_store import utils as store_utils
32 32 from rhodecode.apps.file_store.exceptions import FileNotAllowedException, FileOverSizeException
33 33
34 34 from rhodecode.lib import diffs, codeblocks
35 35 from rhodecode.lib.auth import (
36 36 LoginRequired, HasRepoPermissionAnyDecorator, NotAnonymous, CSRFRequired)
37 37
38 38 from rhodecode.lib.compat import OrderedDict
39 39 from rhodecode.lib.diffs import (
40 40 cache_diff, load_cached_diff, diff_cache_exist, get_diff_context,
41 41 get_diff_whitespace_flag)
42 42 from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError
43 43 import rhodecode.lib.helpers as h
44 44 from rhodecode.lib.utils2 import safe_unicode, str2bool
45 45 from rhodecode.lib.vcs.backends.base import EmptyCommit
46 46 from rhodecode.lib.vcs.exceptions import (
47 47 RepositoryError, CommitDoesNotExistError)
48 48 from rhodecode.model.db import ChangesetComment, ChangesetStatus, FileStore, \
49 49 ChangesetCommentHistory
50 50 from rhodecode.model.changeset_status import ChangesetStatusModel
51 51 from rhodecode.model.comment import CommentsModel
52 52 from rhodecode.model.meta import Session
53 53 from rhodecode.model.settings import VcsSettingsModel
54 54
55 55 log = logging.getLogger(__name__)
56 56
57 57
58 58 def _update_with_GET(params, request):
59 59 for k in ['diff1', 'diff2', 'diff']:
60 60 params[k] += request.GET.getall(k)
61 61
62 62
63 63 class RepoCommitsView(RepoAppView):
64 64 def load_default_context(self):
65 65 c = self._get_local_tmpl_context(include_app_defaults=True)
66 66 c.rhodecode_repo = self.rhodecode_vcs_repo
67 67
68 68 return c
69 69
70 70 def _is_diff_cache_enabled(self, target_repo):
71 71 caching_enabled = self._get_general_setting(
72 72 target_repo, 'rhodecode_diff_cache')
73 73 log.debug('Diff caching enabled: %s', caching_enabled)
74 74 return caching_enabled
75 75
76 76 def _commit(self, commit_id_range, method):
77 77 _ = self.request.translate
78 78 c = self.load_default_context()
79 79 c.fulldiff = self.request.GET.get('fulldiff')
80 80
81 81 # fetch global flags of ignore ws or context lines
82 82 diff_context = get_diff_context(self.request)
83 83 hide_whitespace_changes = get_diff_whitespace_flag(self.request)
84 84
85 85 # diff_limit will cut off the whole diff if the limit is applied
86 86 # otherwise it will just hide the big files from the front-end
87 87 diff_limit = c.visual.cut_off_limit_diff
88 88 file_limit = c.visual.cut_off_limit_file
89 89
90 90 # get ranges of commit ids if preset
91 91 commit_range = commit_id_range.split('...')[:2]
92 92
93 93 try:
94 94 pre_load = ['affected_files', 'author', 'branch', 'date',
95 95 'message', 'parents']
96 96 if self.rhodecode_vcs_repo.alias == 'hg':
97 97 pre_load += ['hidden', 'obsolete', 'phase']
98 98
99 99 if len(commit_range) == 2:
100 100 commits = self.rhodecode_vcs_repo.get_commits(
101 101 start_id=commit_range[0], end_id=commit_range[1],
102 102 pre_load=pre_load, translate_tags=False)
103 103 commits = list(commits)
104 104 else:
105 105 commits = [self.rhodecode_vcs_repo.get_commit(
106 106 commit_id=commit_id_range, pre_load=pre_load)]
107 107
108 108 c.commit_ranges = commits
109 109 if not c.commit_ranges:
110 110 raise RepositoryError('The commit range returned an empty result')
111 111 except CommitDoesNotExistError as e:
112 112 msg = _('No such commit exists. Org exception: `{}`').format(e)
113 113 h.flash(msg, category='error')
114 114 raise HTTPNotFound()
115 115 except Exception:
116 116 log.exception("General failure")
117 117 raise HTTPNotFound()
118 118
119 119 c.changes = OrderedDict()
120 120 c.lines_added = 0
121 121 c.lines_deleted = 0
122 122
123 123 # auto collapse if we have more than limit
124 124 collapse_limit = diffs.DiffProcessor._collapse_commits_over
125 125 c.collapse_all_commits = len(c.commit_ranges) > collapse_limit
126 126
127 127 c.commit_statuses = ChangesetStatus.STATUSES
128 128 c.inline_comments = []
129 129 c.files = []
130 130
131 131 c.statuses = []
132 132 c.comments = []
133 133 c.unresolved_comments = []
134 134 c.resolved_comments = []
135 135 if len(c.commit_ranges) == 1:
136 136 commit = c.commit_ranges[0]
137 137 c.comments = CommentsModel().get_comments(
138 138 self.db_repo.repo_id,
139 139 revision=commit.raw_id)
140 140 c.statuses.append(ChangesetStatusModel().get_status(
141 141 self.db_repo.repo_id, commit.raw_id))
142 142 # comments from PR
143 143 statuses = ChangesetStatusModel().get_statuses(
144 144 self.db_repo.repo_id, commit.raw_id,
145 145 with_revisions=True)
146 146 prs = set(st.pull_request for st in statuses
147 147 if st.pull_request is not None)
148 148 # from associated statuses, check the pull requests, and
149 149 # show comments from them
150 150 for pr in prs:
151 151 c.comments.extend(pr.comments)
152 152
153 153 c.unresolved_comments = CommentsModel()\
154 154 .get_commit_unresolved_todos(commit.raw_id)
155 155 c.resolved_comments = CommentsModel()\
156 156 .get_commit_resolved_todos(commit.raw_id)
157 157
158 158 diff = None
159 159 # Iterate over ranges (default commit view is always one commit)
160 160 for commit in c.commit_ranges:
161 161 c.changes[commit.raw_id] = []
162 162
163 163 commit2 = commit
164 164 commit1 = commit.first_parent
165 165
166 166 if method == 'show':
167 167 inline_comments = CommentsModel().get_inline_comments(
168 168 self.db_repo.repo_id, revision=commit.raw_id)
169 169 c.inline_cnt = CommentsModel().get_inline_comments_count(
170 170 inline_comments)
171 171 c.inline_comments = inline_comments
172 172
173 173 cache_path = self.rhodecode_vcs_repo.get_create_shadow_cache_pr_path(
174 174 self.db_repo)
175 175 cache_file_path = diff_cache_exist(
176 176 cache_path, 'diff', commit.raw_id,
177 177 hide_whitespace_changes, diff_context, c.fulldiff)
178 178
179 179 caching_enabled = self._is_diff_cache_enabled(self.db_repo)
180 180 force_recache = str2bool(self.request.GET.get('force_recache'))
181 181
182 182 cached_diff = None
183 183 if caching_enabled:
184 184 cached_diff = load_cached_diff(cache_file_path)
185 185
186 186 has_proper_diff_cache = cached_diff and cached_diff.get('diff')
187 187 if not force_recache and has_proper_diff_cache:
188 188 diffset = cached_diff['diff']
189 189 else:
190 190 vcs_diff = self.rhodecode_vcs_repo.get_diff(
191 191 commit1, commit2,
192 192 ignore_whitespace=hide_whitespace_changes,
193 193 context=diff_context)
194 194
195 195 diff_processor = diffs.DiffProcessor(
196 196 vcs_diff, format='newdiff', diff_limit=diff_limit,
197 197 file_limit=file_limit, show_full_diff=c.fulldiff)
198 198
199 199 _parsed = diff_processor.prepare()
200 200
201 201 diffset = codeblocks.DiffSet(
202 202 repo_name=self.db_repo_name,
203 203 source_node_getter=codeblocks.diffset_node_getter(commit1),
204 204 target_node_getter=codeblocks.diffset_node_getter(commit2))
205 205
206 206 diffset = self.path_filter.render_patchset_filtered(
207 207 diffset, _parsed, commit1.raw_id, commit2.raw_id)
208 208
209 209 # save cached diff
210 210 if caching_enabled:
211 211 cache_diff(cache_file_path, diffset, None)
212 212
213 213 c.limited_diff = diffset.limited_diff
214 214 c.changes[commit.raw_id] = diffset
215 215 else:
216 216 # TODO(marcink): no cache usage here...
217 217 _diff = self.rhodecode_vcs_repo.get_diff(
218 218 commit1, commit2,
219 219 ignore_whitespace=hide_whitespace_changes, context=diff_context)
220 220 diff_processor = diffs.DiffProcessor(
221 221 _diff, format='newdiff', diff_limit=diff_limit,
222 222 file_limit=file_limit, show_full_diff=c.fulldiff)
223 223 # downloads/raw we only need RAW diff nothing else
224 224 diff = self.path_filter.get_raw_patch(diff_processor)
225 225 c.changes[commit.raw_id] = [None, None, None, None, diff, None, None]
226 226
227 227 # sort comments by how they were generated
228 228 c.comments = sorted(c.comments, key=lambda x: x.comment_id)
229 229
230 230 if len(c.commit_ranges) == 1:
231 231 c.commit = c.commit_ranges[0]
232 232 c.parent_tmpl = ''.join(
233 233 '# Parent %s\n' % x.raw_id for x in c.commit.parents)
234 234
235 235 if method == 'download':
236 236 response = Response(diff)
237 237 response.content_type = 'text/plain'
238 238 response.content_disposition = (
239 239 'attachment; filename=%s.diff' % commit_id_range[:12])
240 240 return response
241 241 elif method == 'patch':
242 242 c.diff = safe_unicode(diff)
243 243 patch = render(
244 244 'rhodecode:templates/changeset/patch_changeset.mako',
245 245 self._get_template_context(c), self.request)
246 246 response = Response(patch)
247 247 response.content_type = 'text/plain'
248 248 return response
249 249 elif method == 'raw':
250 250 response = Response(diff)
251 251 response.content_type = 'text/plain'
252 252 return response
253 253 elif method == 'show':
254 254 if len(c.commit_ranges) == 1:
255 255 html = render(
256 256 'rhodecode:templates/changeset/changeset.mako',
257 257 self._get_template_context(c), self.request)
258 258 return Response(html)
259 259 else:
260 260 c.ancestor = None
261 261 c.target_repo = self.db_repo
262 262 html = render(
263 263 'rhodecode:templates/changeset/changeset_range.mako',
264 264 self._get_template_context(c), self.request)
265 265 return Response(html)
266 266
267 267 raise HTTPBadRequest()
268 268
269 269 @LoginRequired()
270 270 @HasRepoPermissionAnyDecorator(
271 271 'repository.read', 'repository.write', 'repository.admin')
272 272 @view_config(
273 273 route_name='repo_commit', request_method='GET',
274 274 renderer=None)
275 275 def repo_commit_show(self):
276 276 commit_id = self.request.matchdict['commit_id']
277 277 return self._commit(commit_id, method='show')
278 278
279 279 @LoginRequired()
280 280 @HasRepoPermissionAnyDecorator(
281 281 'repository.read', 'repository.write', 'repository.admin')
282 282 @view_config(
283 283 route_name='repo_commit_raw', request_method='GET',
284 284 renderer=None)
285 285 @view_config(
286 286 route_name='repo_commit_raw_deprecated', request_method='GET',
287 287 renderer=None)
288 288 def repo_commit_raw(self):
289 289 commit_id = self.request.matchdict['commit_id']
290 290 return self._commit(commit_id, method='raw')
291 291
292 292 @LoginRequired()
293 293 @HasRepoPermissionAnyDecorator(
294 294 'repository.read', 'repository.write', 'repository.admin')
295 295 @view_config(
296 296 route_name='repo_commit_patch', request_method='GET',
297 297 renderer=None)
298 298 def repo_commit_patch(self):
299 299 commit_id = self.request.matchdict['commit_id']
300 300 return self._commit(commit_id, method='patch')
301 301
302 302 @LoginRequired()
303 303 @HasRepoPermissionAnyDecorator(
304 304 'repository.read', 'repository.write', 'repository.admin')
305 305 @view_config(
306 306 route_name='repo_commit_download', request_method='GET',
307 307 renderer=None)
308 308 def repo_commit_download(self):
309 309 commit_id = self.request.matchdict['commit_id']
310 310 return self._commit(commit_id, method='download')
311 311
312 312 @LoginRequired()
313 313 @NotAnonymous()
314 314 @HasRepoPermissionAnyDecorator(
315 315 'repository.read', 'repository.write', 'repository.admin')
316 316 @CSRFRequired()
317 317 @view_config(
318 318 route_name='repo_commit_comment_create', request_method='POST',
319 319 renderer='json_ext')
320 320 def repo_commit_comment_create(self):
321 321 _ = self.request.translate
322 322 commit_id = self.request.matchdict['commit_id']
323 323
324 324 c = self.load_default_context()
325 325 status = self.request.POST.get('changeset_status', None)
326 326 text = self.request.POST.get('text')
327 327 comment_type = self.request.POST.get('comment_type')
328 328 resolves_comment_id = self.request.POST.get('resolves_comment_id', None)
329 329
330 330 if status:
331 331 text = text or (_('Status change %(transition_icon)s %(status)s')
332 332 % {'transition_icon': '>',
333 333 'status': ChangesetStatus.get_status_lbl(status)})
334 334
335 335 multi_commit_ids = []
336 336 for _commit_id in self.request.POST.get('commit_ids', '').split(','):
337 337 if _commit_id not in ['', None, EmptyCommit.raw_id]:
338 338 if _commit_id not in multi_commit_ids:
339 339 multi_commit_ids.append(_commit_id)
340 340
341 341 commit_ids = multi_commit_ids or [commit_id]
342 342
343 343 comment = None
344 344 for current_id in filter(None, commit_ids):
345 345 comment = CommentsModel().create(
346 346 text=text,
347 347 repo=self.db_repo.repo_id,
348 348 user=self._rhodecode_db_user.user_id,
349 349 commit_id=current_id,
350 350 f_path=self.request.POST.get('f_path'),
351 351 line_no=self.request.POST.get('line'),
352 352 status_change=(ChangesetStatus.get_status_lbl(status)
353 353 if status else None),
354 354 status_change_type=status,
355 355 comment_type=comment_type,
356 356 resolves_comment_id=resolves_comment_id,
357 357 auth_user=self._rhodecode_user
358 358 )
359 359
360 360 # get status if set !
361 361 if status:
362 362 # if latest status was from pull request and it's closed
363 363 # disallow changing status !
364 364 # dont_allow_on_closed_pull_request = True !
365 365
366 366 try:
367 367 ChangesetStatusModel().set_status(
368 368 self.db_repo.repo_id,
369 369 status,
370 370 self._rhodecode_db_user.user_id,
371 371 comment,
372 372 revision=current_id,
373 373 dont_allow_on_closed_pull_request=True
374 374 )
375 375 except StatusChangeOnClosedPullRequestError:
376 376 msg = _('Changing the status of a commit associated with '
377 377 'a closed pull request is not allowed')
378 378 log.exception(msg)
379 379 h.flash(msg, category='warning')
380 380 raise HTTPFound(h.route_path(
381 381 'repo_commit', repo_name=self.db_repo_name,
382 382 commit_id=current_id))
383 383
384 384 commit = self.db_repo.get_commit(current_id)
385 385 CommentsModel().trigger_commit_comment_hook(
386 386 self.db_repo, self._rhodecode_user, 'create',
387 387 data={'comment': comment, 'commit': commit})
388 388
389 389 # finalize, commit and redirect
390 390 Session().commit()
391 391
392 392 data = {
393 393 'target_id': h.safeid(h.safe_unicode(
394 394 self.request.POST.get('f_path'))),
395 395 }
396 396 if comment:
397 397 c.co = comment
398 398 rendered_comment = render(
399 399 'rhodecode:templates/changeset/changeset_comment_block.mako',
400 400 self._get_template_context(c), self.request)
401 401
402 402 data.update(comment.get_dict())
403 403 data.update({'rendered_text': rendered_comment})
404 404
405 405 return data
406 406
407 407 @LoginRequired()
408 408 @NotAnonymous()
409 409 @HasRepoPermissionAnyDecorator(
410 410 'repository.read', 'repository.write', 'repository.admin')
411 411 @CSRFRequired()
412 412 @view_config(
413 413 route_name='repo_commit_comment_preview', request_method='POST',
414 414 renderer='string', xhr=True)
415 415 def repo_commit_comment_preview(self):
416 416 # Technically a CSRF token is not needed as no state changes with this
417 417 # call. However, as this is a POST is better to have it, so automated
418 418 # tools don't flag it as potential CSRF.
419 419 # Post is required because the payload could be bigger than the maximum
420 420 # allowed by GET.
421 421
422 422 text = self.request.POST.get('text')
423 423 renderer = self.request.POST.get('renderer') or 'rst'
424 424 if text:
425 425 return h.render(text, renderer=renderer, mentions=True,
426 426 repo_name=self.db_repo_name)
427 427 return ''
428 428
429 429 @LoginRequired()
430 430 @NotAnonymous()
431 431 @HasRepoPermissionAnyDecorator(
432 432 'repository.read', 'repository.write', 'repository.admin')
433 433 @CSRFRequired()
434 434 @view_config(
435 435 route_name='repo_commit_comment_history_view', request_method='POST',
436 436 renderer='string', xhr=True)
437 437 def repo_commit_comment_history_view(self):
438 commit_id = self.request.matchdict['commit_id']
438 c = self.load_default_context()
439
439 440 comment_history_id = self.request.matchdict['comment_history_id']
440 441 comment_history = ChangesetCommentHistory.get_or_404(comment_history_id)
441 c = self.load_default_context()
442 c.comment_history = comment_history
442 is_repo_comment = comment_history.comment.repo.repo_id == self.db_repo.repo_id
443
444 if is_repo_comment:
445 c.comment_history = comment_history
443 446
444 rendered_comment = render(
445 'rhodecode:templates/changeset/comment_history.mako',
446 self._get_template_context(c)
447 , self.request)
448 return rendered_comment
447 rendered_comment = render(
448 'rhodecode:templates/changeset/comment_history.mako',
449 self._get_template_context(c)
450 , self.request)
451 return rendered_comment
452 else:
453 log.warning('No permissions for user %s to show comment_history_id: %s',
454 self._rhodecode_db_user, comment_history_id)
455 raise HTTPNotFound()
449 456
450 457 @LoginRequired()
451 458 @NotAnonymous()
452 459 @HasRepoPermissionAnyDecorator(
453 460 'repository.read', 'repository.write', 'repository.admin')
454 461 @CSRFRequired()
455 462 @view_config(
456 463 route_name='repo_commit_comment_attachment_upload', request_method='POST',
457 464 renderer='json_ext', xhr=True)
458 465 def repo_commit_comment_attachment_upload(self):
459 466 c = self.load_default_context()
460 467 upload_key = 'attachment'
461 468
462 469 file_obj = self.request.POST.get(upload_key)
463 470
464 471 if file_obj is None:
465 472 self.request.response.status = 400
466 473 return {'store_fid': None,
467 474 'access_path': None,
468 475 'error': '{} data field is missing'.format(upload_key)}
469 476
470 477 if not hasattr(file_obj, 'filename'):
471 478 self.request.response.status = 400
472 479 return {'store_fid': None,
473 480 'access_path': None,
474 481 'error': 'filename cannot be read from the data field'}
475 482
476 483 filename = file_obj.filename
477 484 file_display_name = filename
478 485
479 486 metadata = {
480 487 'user_uploaded': {'username': self._rhodecode_user.username,
481 488 'user_id': self._rhodecode_user.user_id,
482 489 'ip': self._rhodecode_user.ip_addr}}
483 490
484 491 # TODO(marcink): allow .ini configuration for allowed_extensions, and file-size
485 492 allowed_extensions = [
486 493 'gif', '.jpeg', '.jpg', '.png', '.docx', '.gz', '.log', '.pdf',
487 494 '.pptx', '.txt', '.xlsx', '.zip']
488 495 max_file_size = 10 * 1024 * 1024 # 10MB, also validated via dropzone.js
489 496
490 497 try:
491 498 storage = store_utils.get_file_storage(self.request.registry.settings)
492 499 store_uid, metadata = storage.save_file(
493 500 file_obj.file, filename, extra_metadata=metadata,
494 501 extensions=allowed_extensions, max_filesize=max_file_size)
495 502 except FileNotAllowedException:
496 503 self.request.response.status = 400
497 504 permitted_extensions = ', '.join(allowed_extensions)
498 505 error_msg = 'File `{}` is not allowed. ' \
499 506 'Only following extensions are permitted: {}'.format(
500 507 filename, permitted_extensions)
501 508 return {'store_fid': None,
502 509 'access_path': None,
503 510 'error': error_msg}
504 511 except FileOverSizeException:
505 512 self.request.response.status = 400
506 513 limit_mb = h.format_byte_size_binary(max_file_size)
507 514 return {'store_fid': None,
508 515 'access_path': None,
509 516 'error': 'File {} is exceeding allowed limit of {}.'.format(
510 517 filename, limit_mb)}
511 518
512 519 try:
513 520 entry = FileStore.create(
514 521 file_uid=store_uid, filename=metadata["filename"],
515 522 file_hash=metadata["sha256"], file_size=metadata["size"],
516 523 file_display_name=file_display_name,
517 524 file_description=u'comment attachment `{}`'.format(safe_unicode(filename)),
518 525 hidden=True, check_acl=True, user_id=self._rhodecode_user.user_id,
519 526 scope_repo_id=self.db_repo.repo_id
520 527 )
521 528 Session().add(entry)
522 529 Session().commit()
523 530 log.debug('Stored upload in DB as %s', entry)
524 531 except Exception:
525 532 log.exception('Failed to store file %s', filename)
526 533 self.request.response.status = 400
527 534 return {'store_fid': None,
528 535 'access_path': None,
529 536 'error': 'File {} failed to store in DB.'.format(filename)}
530 537
531 538 Session().commit()
532 539
533 540 return {
534 541 'store_fid': store_uid,
535 542 'access_path': h.route_path(
536 543 'download_file', fid=store_uid),
537 544 'fqn_access_path': h.route_url(
538 545 'download_file', fid=store_uid),
539 546 'repo_access_path': h.route_path(
540 547 'repo_artifacts_get', repo_name=self.db_repo_name, uid=store_uid),
541 548 'repo_fqn_access_path': h.route_url(
542 549 'repo_artifacts_get', repo_name=self.db_repo_name, uid=store_uid),
543 550 }
544 551
545 552 @LoginRequired()
546 553 @NotAnonymous()
547 554 @HasRepoPermissionAnyDecorator(
548 555 'repository.read', 'repository.write', 'repository.admin')
549 556 @CSRFRequired()
550 557 @view_config(
551 558 route_name='repo_commit_comment_delete', request_method='POST',
552 559 renderer='json_ext')
553 560 def repo_commit_comment_delete(self):
554 561 commit_id = self.request.matchdict['commit_id']
555 562 comment_id = self.request.matchdict['comment_id']
556 563
557 564 comment = ChangesetComment.get_or_404(comment_id)
558 565 if not comment:
559 566 log.debug('Comment with id:%s not found, skipping', comment_id)
560 567 # comment already deleted in another call probably
561 568 return True
562 569
563 570 if comment.immutable:
564 571 # don't allow deleting comments that are immutable
565 572 raise HTTPForbidden()
566 573
567 574 is_repo_admin = h.HasRepoPermissionAny('repository.admin')(self.db_repo_name)
568 575 super_admin = h.HasPermissionAny('hg.admin')()
569 576 comment_owner = (comment.author.user_id == self._rhodecode_db_user.user_id)
570 is_repo_comment = comment.repo.repo_name == self.db_repo_name
577 is_repo_comment = comment.repo.repo_id == self.db_repo.repo_id
571 578 comment_repo_admin = is_repo_admin and is_repo_comment
572 579
573 580 if super_admin or comment_owner or comment_repo_admin:
574 581 CommentsModel().delete(comment=comment, auth_user=self._rhodecode_user)
575 582 Session().commit()
576 583 return True
577 584 else:
578 585 log.warning('No permissions for user %s to delete comment_id: %s',
579 586 self._rhodecode_db_user, comment_id)
580 587 raise HTTPNotFound()
581 588
582 589 @LoginRequired()
583 590 @NotAnonymous()
584 591 @HasRepoPermissionAnyDecorator(
585 592 'repository.read', 'repository.write', 'repository.admin')
586 593 @CSRFRequired()
587 594 @view_config(
588 595 route_name='repo_commit_comment_edit', request_method='POST',
589 596 renderer='json_ext')
590 597 def repo_commit_comment_edit(self):
591 commit_id = self.request.matchdict['commit_id']
592 598 comment_id = self.request.matchdict['comment_id']
593
594 599 comment = ChangesetComment.get_or_404(comment_id)
595 600
596 601 if comment.immutable:
597 602 # don't allow deleting comments that are immutable
598 603 raise HTTPForbidden()
599 604
600 605 is_repo_admin = h.HasRepoPermissionAny('repository.admin')(self.db_repo_name)
601 606 super_admin = h.HasPermissionAny('hg.admin')()
602 607 comment_owner = (comment.author.user_id == self._rhodecode_db_user.user_id)
603 is_repo_comment = comment.repo.repo_name == self.db_repo_name
608 is_repo_comment = comment.repo.repo_id == self.db_repo.repo_id
604 609 comment_repo_admin = is_repo_admin and is_repo_comment
605 610
606 611 if super_admin or comment_owner or comment_repo_admin:
607 612 text = self.request.POST.get('text')
608 613 version = self.request.POST.get('version')
609 614 if text == comment.text:
610 615 log.warning(
611 616 'Comment(repo): '
612 617 'Trying to create new version '
613 618 'of existing comment {}'.format(
614 619 comment_id,
615 620 )
616 621 )
617 622 raise HTTPNotFound()
618 623 if version.isdigit():
619 624 version = int(version)
620 625 else:
621 626 log.warning(
622 627 'Comment(repo): Wrong version type {} {} '
623 628 'for comment {}'.format(
624 629 version,
625 630 type(version),
626 631 comment_id,
627 632 )
628 633 )
629 634 raise HTTPNotFound()
630 635
631 636 comment_history = CommentsModel().edit(
632 637 comment_id=comment_id,
633 638 text=text,
634 639 auth_user=self._rhodecode_user,
635 640 version=version,
636 641 )
637 642 if not comment_history:
638 643 raise HTTPNotFound()
639 644 Session().commit()
640 645 return {
641 646 'comment_history_id': comment_history.comment_history_id,
642 647 'comment_id': comment.comment_id,
643 648 'comment_version': comment_history.version,
644 649 }
645 650 else:
646 651 log.warning('No permissions for user %s to edit comment_id: %s',
647 652 self._rhodecode_db_user, comment_id)
648 653 raise HTTPNotFound()
649 654
650 655 @LoginRequired()
651 656 @HasRepoPermissionAnyDecorator(
652 657 'repository.read', 'repository.write', 'repository.admin')
653 658 @view_config(
654 659 route_name='repo_commit_data', request_method='GET',
655 660 renderer='json_ext', xhr=True)
656 661 def repo_commit_data(self):
657 662 commit_id = self.request.matchdict['commit_id']
658 663 self.load_default_context()
659 664
660 665 try:
661 666 return self.rhodecode_vcs_repo.get_commit(commit_id=commit_id)
662 667 except CommitDoesNotExistError as e:
663 668 return EmptyCommit(message=str(e))
664 669
665 670 @LoginRequired()
666 671 @HasRepoPermissionAnyDecorator(
667 672 'repository.read', 'repository.write', 'repository.admin')
668 673 @view_config(
669 674 route_name='repo_commit_children', request_method='GET',
670 675 renderer='json_ext', xhr=True)
671 676 def repo_commit_children(self):
672 677 commit_id = self.request.matchdict['commit_id']
673 678 self.load_default_context()
674 679
675 680 try:
676 681 commit = self.rhodecode_vcs_repo.get_commit(commit_id=commit_id)
677 682 children = commit.children
678 683 except CommitDoesNotExistError:
679 684 children = []
680 685
681 686 result = {"results": children}
682 687 return result
683 688
684 689 @LoginRequired()
685 690 @HasRepoPermissionAnyDecorator(
686 691 'repository.read', 'repository.write', 'repository.admin')
687 692 @view_config(
688 693 route_name='repo_commit_parents', request_method='GET',
689 694 renderer='json_ext')
690 695 def repo_commit_parents(self):
691 696 commit_id = self.request.matchdict['commit_id']
692 697 self.load_default_context()
693 698
694 699 try:
695 700 commit = self.rhodecode_vcs_repo.get_commit(commit_id=commit_id)
696 701 parents = commit.parents
697 702 except CommitDoesNotExistError:
698 703 parents = []
699 704 result = {"results": parents}
700 705 return result
General Comments 0
You need to be logged in to leave comments. Login now