##// END OF EJS Templates
comments: view edit history of comment should be allowed for anonymous users, as they anyway see comments body.
marcink -
r4489:a9e68bf1 default
parent child Browse files
Show More
@@ -1,782 +1,781 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 import logging
22 22 import collections
23 23
24 24 from pyramid.httpexceptions import (
25 25 HTTPNotFound, HTTPBadRequest, HTTPFound, HTTPForbidden, HTTPConflict)
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 from rhodecode.lib.ext_json import json
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, CommentVersionMismatch
43 43 import rhodecode.lib.helpers as h
44 44 from rhodecode.lib.utils2 import safe_unicode, str2bool, StrictAttributeDict
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 single_commit = len(c.commit_ranges) == 1
119 119
120 120 c.changes = OrderedDict()
121 121 c.lines_added = 0
122 122 c.lines_deleted = 0
123 123
124 124 # auto collapse if we have more than limit
125 125 collapse_limit = diffs.DiffProcessor._collapse_commits_over
126 126 c.collapse_all_commits = len(c.commit_ranges) > collapse_limit
127 127
128 128 c.commit_statuses = ChangesetStatus.STATUSES
129 129 c.inline_comments = []
130 130 c.files = []
131 131
132 132 c.comments = []
133 133 c.unresolved_comments = []
134 134 c.resolved_comments = []
135 135
136 136 # Single commit
137 137 if single_commit:
138 138 commit = c.commit_ranges[0]
139 139 c.comments = CommentsModel().get_comments(
140 140 self.db_repo.repo_id,
141 141 revision=commit.raw_id)
142 142
143 143 # comments from PR
144 144 statuses = ChangesetStatusModel().get_statuses(
145 145 self.db_repo.repo_id, commit.raw_id,
146 146 with_revisions=True)
147 147
148 148 prs = set()
149 149 reviewers = list()
150 150 reviewers_duplicates = set() # to not have duplicates from multiple votes
151 151 for c_status in statuses:
152 152
153 153 # extract associated pull-requests from votes
154 154 if c_status.pull_request:
155 155 prs.add(c_status.pull_request)
156 156
157 157 # extract reviewers
158 158 _user_id = c_status.author.user_id
159 159 if _user_id not in reviewers_duplicates:
160 160 reviewers.append(
161 161 StrictAttributeDict({
162 162 'user': c_status.author,
163 163
164 164 # fake attributed for commit, page that we don't have
165 165 # but we share the display with PR page
166 166 'mandatory': False,
167 167 'reasons': [],
168 168 'rule_user_group_data': lambda: None
169 169 })
170 170 )
171 171 reviewers_duplicates.add(_user_id)
172 172
173 173 c.allowed_reviewers = reviewers
174 174 # from associated statuses, check the pull requests, and
175 175 # show comments from them
176 176 for pr in prs:
177 177 c.comments.extend(pr.comments)
178 178
179 179 c.unresolved_comments = CommentsModel()\
180 180 .get_commit_unresolved_todos(commit.raw_id)
181 181 c.resolved_comments = CommentsModel()\
182 182 .get_commit_resolved_todos(commit.raw_id)
183 183
184 184 c.inline_comments_flat = CommentsModel()\
185 185 .get_commit_inline_comments(commit.raw_id)
186 186
187 187 review_statuses = ChangesetStatusModel().aggregate_votes_by_user(
188 188 statuses, reviewers)
189 189
190 190 c.commit_review_status = ChangesetStatus.STATUS_NOT_REVIEWED
191 191
192 192 c.commit_set_reviewers_data_json = collections.OrderedDict({'reviewers': []})
193 193
194 194 for review_obj, member, reasons, mandatory, status in review_statuses:
195 195 member_reviewer = h.reviewer_as_json(
196 196 member, reasons=reasons, mandatory=mandatory,
197 197 user_group=None
198 198 )
199 199
200 200 current_review_status = status[0][1].status if status else ChangesetStatus.STATUS_NOT_REVIEWED
201 201 member_reviewer['review_status'] = current_review_status
202 202 member_reviewer['review_status_label'] = h.commit_status_lbl(current_review_status)
203 203 member_reviewer['allowed_to_update'] = False
204 204 c.commit_set_reviewers_data_json['reviewers'].append(member_reviewer)
205 205
206 206 c.commit_set_reviewers_data_json = json.dumps(c.commit_set_reviewers_data_json)
207 207
208 208 # NOTE(marcink): this uses the same voting logic as in pull-requests
209 209 c.commit_review_status = ChangesetStatusModel().calculate_status(review_statuses)
210 210 c.commit_broadcast_channel = u'/repo${}$/commit/{}'.format(
211 211 c.repo_name,
212 212 commit.raw_id
213 213 )
214 214
215 215 diff = None
216 216 # Iterate over ranges (default commit view is always one commit)
217 217 for commit in c.commit_ranges:
218 218 c.changes[commit.raw_id] = []
219 219
220 220 commit2 = commit
221 221 commit1 = commit.first_parent
222 222
223 223 if method == 'show':
224 224 inline_comments = CommentsModel().get_inline_comments(
225 225 self.db_repo.repo_id, revision=commit.raw_id)
226 226 c.inline_cnt = len(CommentsModel().get_inline_comments_as_list(
227 227 inline_comments))
228 228 c.inline_comments = inline_comments
229 229
230 230 cache_path = self.rhodecode_vcs_repo.get_create_shadow_cache_pr_path(
231 231 self.db_repo)
232 232 cache_file_path = diff_cache_exist(
233 233 cache_path, 'diff', commit.raw_id,
234 234 hide_whitespace_changes, diff_context, c.fulldiff)
235 235
236 236 caching_enabled = self._is_diff_cache_enabled(self.db_repo)
237 237 force_recache = str2bool(self.request.GET.get('force_recache'))
238 238
239 239 cached_diff = None
240 240 if caching_enabled:
241 241 cached_diff = load_cached_diff(cache_file_path)
242 242
243 243 has_proper_diff_cache = cached_diff and cached_diff.get('diff')
244 244 if not force_recache and has_proper_diff_cache:
245 245 diffset = cached_diff['diff']
246 246 else:
247 247 vcs_diff = self.rhodecode_vcs_repo.get_diff(
248 248 commit1, commit2,
249 249 ignore_whitespace=hide_whitespace_changes,
250 250 context=diff_context)
251 251
252 252 diff_processor = diffs.DiffProcessor(
253 253 vcs_diff, format='newdiff', diff_limit=diff_limit,
254 254 file_limit=file_limit, show_full_diff=c.fulldiff)
255 255
256 256 _parsed = diff_processor.prepare()
257 257
258 258 diffset = codeblocks.DiffSet(
259 259 repo_name=self.db_repo_name,
260 260 source_node_getter=codeblocks.diffset_node_getter(commit1),
261 261 target_node_getter=codeblocks.diffset_node_getter(commit2))
262 262
263 263 diffset = self.path_filter.render_patchset_filtered(
264 264 diffset, _parsed, commit1.raw_id, commit2.raw_id)
265 265
266 266 # save cached diff
267 267 if caching_enabled:
268 268 cache_diff(cache_file_path, diffset, None)
269 269
270 270 c.limited_diff = diffset.limited_diff
271 271 c.changes[commit.raw_id] = diffset
272 272 else:
273 273 # TODO(marcink): no cache usage here...
274 274 _diff = self.rhodecode_vcs_repo.get_diff(
275 275 commit1, commit2,
276 276 ignore_whitespace=hide_whitespace_changes, context=diff_context)
277 277 diff_processor = diffs.DiffProcessor(
278 278 _diff, format='newdiff', diff_limit=diff_limit,
279 279 file_limit=file_limit, show_full_diff=c.fulldiff)
280 280 # downloads/raw we only need RAW diff nothing else
281 281 diff = self.path_filter.get_raw_patch(diff_processor)
282 282 c.changes[commit.raw_id] = [None, None, None, None, diff, None, None]
283 283
284 284 # sort comments by how they were generated
285 285 c.comments = sorted(c.comments, key=lambda x: x.comment_id)
286 286 c.at_version_num = None
287 287
288 288 if len(c.commit_ranges) == 1:
289 289 c.commit = c.commit_ranges[0]
290 290 c.parent_tmpl = ''.join(
291 291 '# Parent %s\n' % x.raw_id for x in c.commit.parents)
292 292
293 293 if method == 'download':
294 294 response = Response(diff)
295 295 response.content_type = 'text/plain'
296 296 response.content_disposition = (
297 297 'attachment; filename=%s.diff' % commit_id_range[:12])
298 298 return response
299 299 elif method == 'patch':
300 300 c.diff = safe_unicode(diff)
301 301 patch = render(
302 302 'rhodecode:templates/changeset/patch_changeset.mako',
303 303 self._get_template_context(c), self.request)
304 304 response = Response(patch)
305 305 response.content_type = 'text/plain'
306 306 return response
307 307 elif method == 'raw':
308 308 response = Response(diff)
309 309 response.content_type = 'text/plain'
310 310 return response
311 311 elif method == 'show':
312 312 if len(c.commit_ranges) == 1:
313 313 html = render(
314 314 'rhodecode:templates/changeset/changeset.mako',
315 315 self._get_template_context(c), self.request)
316 316 return Response(html)
317 317 else:
318 318 c.ancestor = None
319 319 c.target_repo = self.db_repo
320 320 html = render(
321 321 'rhodecode:templates/changeset/changeset_range.mako',
322 322 self._get_template_context(c), self.request)
323 323 return Response(html)
324 324
325 325 raise HTTPBadRequest()
326 326
327 327 @LoginRequired()
328 328 @HasRepoPermissionAnyDecorator(
329 329 'repository.read', 'repository.write', 'repository.admin')
330 330 @view_config(
331 331 route_name='repo_commit', request_method='GET',
332 332 renderer=None)
333 333 def repo_commit_show(self):
334 334 commit_id = self.request.matchdict['commit_id']
335 335 return self._commit(commit_id, method='show')
336 336
337 337 @LoginRequired()
338 338 @HasRepoPermissionAnyDecorator(
339 339 'repository.read', 'repository.write', 'repository.admin')
340 340 @view_config(
341 341 route_name='repo_commit_raw', request_method='GET',
342 342 renderer=None)
343 343 @view_config(
344 344 route_name='repo_commit_raw_deprecated', request_method='GET',
345 345 renderer=None)
346 346 def repo_commit_raw(self):
347 347 commit_id = self.request.matchdict['commit_id']
348 348 return self._commit(commit_id, method='raw')
349 349
350 350 @LoginRequired()
351 351 @HasRepoPermissionAnyDecorator(
352 352 'repository.read', 'repository.write', 'repository.admin')
353 353 @view_config(
354 354 route_name='repo_commit_patch', request_method='GET',
355 355 renderer=None)
356 356 def repo_commit_patch(self):
357 357 commit_id = self.request.matchdict['commit_id']
358 358 return self._commit(commit_id, method='patch')
359 359
360 360 @LoginRequired()
361 361 @HasRepoPermissionAnyDecorator(
362 362 'repository.read', 'repository.write', 'repository.admin')
363 363 @view_config(
364 364 route_name='repo_commit_download', request_method='GET',
365 365 renderer=None)
366 366 def repo_commit_download(self):
367 367 commit_id = self.request.matchdict['commit_id']
368 368 return self._commit(commit_id, method='download')
369 369
370 370 @LoginRequired()
371 371 @NotAnonymous()
372 372 @HasRepoPermissionAnyDecorator(
373 373 'repository.read', 'repository.write', 'repository.admin')
374 374 @CSRFRequired()
375 375 @view_config(
376 376 route_name='repo_commit_comment_create', request_method='POST',
377 377 renderer='json_ext')
378 378 def repo_commit_comment_create(self):
379 379 _ = self.request.translate
380 380 commit_id = self.request.matchdict['commit_id']
381 381
382 382 c = self.load_default_context()
383 383 status = self.request.POST.get('changeset_status', None)
384 384 text = self.request.POST.get('text')
385 385 comment_type = self.request.POST.get('comment_type')
386 386 resolves_comment_id = self.request.POST.get('resolves_comment_id', None)
387 387
388 388 if status:
389 389 text = text or (_('Status change %(transition_icon)s %(status)s')
390 390 % {'transition_icon': '>',
391 391 'status': ChangesetStatus.get_status_lbl(status)})
392 392
393 393 multi_commit_ids = []
394 394 for _commit_id in self.request.POST.get('commit_ids', '').split(','):
395 395 if _commit_id not in ['', None, EmptyCommit.raw_id]:
396 396 if _commit_id not in multi_commit_ids:
397 397 multi_commit_ids.append(_commit_id)
398 398
399 399 commit_ids = multi_commit_ids or [commit_id]
400 400
401 401 comment = None
402 402 for current_id in filter(None, commit_ids):
403 403 comment = CommentsModel().create(
404 404 text=text,
405 405 repo=self.db_repo.repo_id,
406 406 user=self._rhodecode_db_user.user_id,
407 407 commit_id=current_id,
408 408 f_path=self.request.POST.get('f_path'),
409 409 line_no=self.request.POST.get('line'),
410 410 status_change=(ChangesetStatus.get_status_lbl(status)
411 411 if status else None),
412 412 status_change_type=status,
413 413 comment_type=comment_type,
414 414 resolves_comment_id=resolves_comment_id,
415 415 auth_user=self._rhodecode_user
416 416 )
417 417
418 418 # get status if set !
419 419 if status:
420 420 # if latest status was from pull request and it's closed
421 421 # disallow changing status !
422 422 # dont_allow_on_closed_pull_request = True !
423 423
424 424 try:
425 425 ChangesetStatusModel().set_status(
426 426 self.db_repo.repo_id,
427 427 status,
428 428 self._rhodecode_db_user.user_id,
429 429 comment,
430 430 revision=current_id,
431 431 dont_allow_on_closed_pull_request=True
432 432 )
433 433 except StatusChangeOnClosedPullRequestError:
434 434 msg = _('Changing the status of a commit associated with '
435 435 'a closed pull request is not allowed')
436 436 log.exception(msg)
437 437 h.flash(msg, category='warning')
438 438 raise HTTPFound(h.route_path(
439 439 'repo_commit', repo_name=self.db_repo_name,
440 440 commit_id=current_id))
441 441
442 442 commit = self.db_repo.get_commit(current_id)
443 443 CommentsModel().trigger_commit_comment_hook(
444 444 self.db_repo, self._rhodecode_user, 'create',
445 445 data={'comment': comment, 'commit': commit})
446 446
447 447 # finalize, commit and redirect
448 448 Session().commit()
449 449
450 450 data = {
451 451 'target_id': h.safeid(h.safe_unicode(
452 452 self.request.POST.get('f_path'))),
453 453 }
454 454 if comment:
455 455 c.co = comment
456 456 c.at_version_num = 0
457 457 rendered_comment = render(
458 458 'rhodecode:templates/changeset/changeset_comment_block.mako',
459 459 self._get_template_context(c), self.request)
460 460
461 461 data.update(comment.get_dict())
462 462 data.update({'rendered_text': rendered_comment})
463 463
464 464 return data
465 465
466 466 @LoginRequired()
467 467 @NotAnonymous()
468 468 @HasRepoPermissionAnyDecorator(
469 469 'repository.read', 'repository.write', 'repository.admin')
470 470 @CSRFRequired()
471 471 @view_config(
472 472 route_name='repo_commit_comment_preview', request_method='POST',
473 473 renderer='string', xhr=True)
474 474 def repo_commit_comment_preview(self):
475 475 # Technically a CSRF token is not needed as no state changes with this
476 476 # call. However, as this is a POST is better to have it, so automated
477 477 # tools don't flag it as potential CSRF.
478 478 # Post is required because the payload could be bigger than the maximum
479 479 # allowed by GET.
480 480
481 481 text = self.request.POST.get('text')
482 482 renderer = self.request.POST.get('renderer') or 'rst'
483 483 if text:
484 484 return h.render(text, renderer=renderer, mentions=True,
485 485 repo_name=self.db_repo_name)
486 486 return ''
487 487
488 488 @LoginRequired()
489 @NotAnonymous()
490 489 @HasRepoPermissionAnyDecorator(
491 490 'repository.read', 'repository.write', 'repository.admin')
492 491 @CSRFRequired()
493 492 @view_config(
494 493 route_name='repo_commit_comment_history_view', request_method='POST',
495 494 renderer='string', xhr=True)
496 495 def repo_commit_comment_history_view(self):
497 496 c = self.load_default_context()
498 497
499 498 comment_history_id = self.request.matchdict['comment_history_id']
500 499 comment_history = ChangesetCommentHistory.get_or_404(comment_history_id)
501 500 is_repo_comment = comment_history.comment.repo.repo_id == self.db_repo.repo_id
502 501
503 502 if is_repo_comment:
504 503 c.comment_history = comment_history
505 504
506 505 rendered_comment = render(
507 506 'rhodecode:templates/changeset/comment_history.mako',
508 507 self._get_template_context(c)
509 508 , self.request)
510 509 return rendered_comment
511 510 else:
512 511 log.warning('No permissions for user %s to show comment_history_id: %s',
513 512 self._rhodecode_db_user, comment_history_id)
514 513 raise HTTPNotFound()
515 514
516 515 @LoginRequired()
517 516 @NotAnonymous()
518 517 @HasRepoPermissionAnyDecorator(
519 518 'repository.read', 'repository.write', 'repository.admin')
520 519 @CSRFRequired()
521 520 @view_config(
522 521 route_name='repo_commit_comment_attachment_upload', request_method='POST',
523 522 renderer='json_ext', xhr=True)
524 523 def repo_commit_comment_attachment_upload(self):
525 524 c = self.load_default_context()
526 525 upload_key = 'attachment'
527 526
528 527 file_obj = self.request.POST.get(upload_key)
529 528
530 529 if file_obj is None:
531 530 self.request.response.status = 400
532 531 return {'store_fid': None,
533 532 'access_path': None,
534 533 'error': '{} data field is missing'.format(upload_key)}
535 534
536 535 if not hasattr(file_obj, 'filename'):
537 536 self.request.response.status = 400
538 537 return {'store_fid': None,
539 538 'access_path': None,
540 539 'error': 'filename cannot be read from the data field'}
541 540
542 541 filename = file_obj.filename
543 542 file_display_name = filename
544 543
545 544 metadata = {
546 545 'user_uploaded': {'username': self._rhodecode_user.username,
547 546 'user_id': self._rhodecode_user.user_id,
548 547 'ip': self._rhodecode_user.ip_addr}}
549 548
550 549 # TODO(marcink): allow .ini configuration for allowed_extensions, and file-size
551 550 allowed_extensions = [
552 551 'gif', '.jpeg', '.jpg', '.png', '.docx', '.gz', '.log', '.pdf',
553 552 '.pptx', '.txt', '.xlsx', '.zip']
554 553 max_file_size = 10 * 1024 * 1024 # 10MB, also validated via dropzone.js
555 554
556 555 try:
557 556 storage = store_utils.get_file_storage(self.request.registry.settings)
558 557 store_uid, metadata = storage.save_file(
559 558 file_obj.file, filename, extra_metadata=metadata,
560 559 extensions=allowed_extensions, max_filesize=max_file_size)
561 560 except FileNotAllowedException:
562 561 self.request.response.status = 400
563 562 permitted_extensions = ', '.join(allowed_extensions)
564 563 error_msg = 'File `{}` is not allowed. ' \
565 564 'Only following extensions are permitted: {}'.format(
566 565 filename, permitted_extensions)
567 566 return {'store_fid': None,
568 567 'access_path': None,
569 568 'error': error_msg}
570 569 except FileOverSizeException:
571 570 self.request.response.status = 400
572 571 limit_mb = h.format_byte_size_binary(max_file_size)
573 572 return {'store_fid': None,
574 573 'access_path': None,
575 574 'error': 'File {} is exceeding allowed limit of {}.'.format(
576 575 filename, limit_mb)}
577 576
578 577 try:
579 578 entry = FileStore.create(
580 579 file_uid=store_uid, filename=metadata["filename"],
581 580 file_hash=metadata["sha256"], file_size=metadata["size"],
582 581 file_display_name=file_display_name,
583 582 file_description=u'comment attachment `{}`'.format(safe_unicode(filename)),
584 583 hidden=True, check_acl=True, user_id=self._rhodecode_user.user_id,
585 584 scope_repo_id=self.db_repo.repo_id
586 585 )
587 586 Session().add(entry)
588 587 Session().commit()
589 588 log.debug('Stored upload in DB as %s', entry)
590 589 except Exception:
591 590 log.exception('Failed to store file %s', filename)
592 591 self.request.response.status = 400
593 592 return {'store_fid': None,
594 593 'access_path': None,
595 594 'error': 'File {} failed to store in DB.'.format(filename)}
596 595
597 596 Session().commit()
598 597
599 598 return {
600 599 'store_fid': store_uid,
601 600 'access_path': h.route_path(
602 601 'download_file', fid=store_uid),
603 602 'fqn_access_path': h.route_url(
604 603 'download_file', fid=store_uid),
605 604 'repo_access_path': h.route_path(
606 605 'repo_artifacts_get', repo_name=self.db_repo_name, uid=store_uid),
607 606 'repo_fqn_access_path': h.route_url(
608 607 'repo_artifacts_get', repo_name=self.db_repo_name, uid=store_uid),
609 608 }
610 609
611 610 @LoginRequired()
612 611 @NotAnonymous()
613 612 @HasRepoPermissionAnyDecorator(
614 613 'repository.read', 'repository.write', 'repository.admin')
615 614 @CSRFRequired()
616 615 @view_config(
617 616 route_name='repo_commit_comment_delete', request_method='POST',
618 617 renderer='json_ext')
619 618 def repo_commit_comment_delete(self):
620 619 commit_id = self.request.matchdict['commit_id']
621 620 comment_id = self.request.matchdict['comment_id']
622 621
623 622 comment = ChangesetComment.get_or_404(comment_id)
624 623 if not comment:
625 624 log.debug('Comment with id:%s not found, skipping', comment_id)
626 625 # comment already deleted in another call probably
627 626 return True
628 627
629 628 if comment.immutable:
630 629 # don't allow deleting comments that are immutable
631 630 raise HTTPForbidden()
632 631
633 632 is_repo_admin = h.HasRepoPermissionAny('repository.admin')(self.db_repo_name)
634 633 super_admin = h.HasPermissionAny('hg.admin')()
635 634 comment_owner = (comment.author.user_id == self._rhodecode_db_user.user_id)
636 635 is_repo_comment = comment.repo.repo_id == self.db_repo.repo_id
637 636 comment_repo_admin = is_repo_admin and is_repo_comment
638 637
639 638 if super_admin or comment_owner or comment_repo_admin:
640 639 CommentsModel().delete(comment=comment, auth_user=self._rhodecode_user)
641 640 Session().commit()
642 641 return True
643 642 else:
644 643 log.warning('No permissions for user %s to delete comment_id: %s',
645 644 self._rhodecode_db_user, comment_id)
646 645 raise HTTPNotFound()
647 646
648 647 @LoginRequired()
649 648 @NotAnonymous()
650 649 @HasRepoPermissionAnyDecorator(
651 650 'repository.read', 'repository.write', 'repository.admin')
652 651 @CSRFRequired()
653 652 @view_config(
654 653 route_name='repo_commit_comment_edit', request_method='POST',
655 654 renderer='json_ext')
656 655 def repo_commit_comment_edit(self):
657 656 self.load_default_context()
658 657
659 658 comment_id = self.request.matchdict['comment_id']
660 659 comment = ChangesetComment.get_or_404(comment_id)
661 660
662 661 if comment.immutable:
663 662 # don't allow deleting comments that are immutable
664 663 raise HTTPForbidden()
665 664
666 665 is_repo_admin = h.HasRepoPermissionAny('repository.admin')(self.db_repo_name)
667 666 super_admin = h.HasPermissionAny('hg.admin')()
668 667 comment_owner = (comment.author.user_id == self._rhodecode_db_user.user_id)
669 668 is_repo_comment = comment.repo.repo_id == self.db_repo.repo_id
670 669 comment_repo_admin = is_repo_admin and is_repo_comment
671 670
672 671 if super_admin or comment_owner or comment_repo_admin:
673 672 text = self.request.POST.get('text')
674 673 version = self.request.POST.get('version')
675 674 if text == comment.text:
676 675 log.warning(
677 676 'Comment(repo): '
678 677 'Trying to create new version '
679 678 'with the same comment body {}'.format(
680 679 comment_id,
681 680 )
682 681 )
683 682 raise HTTPNotFound()
684 683
685 684 if version.isdigit():
686 685 version = int(version)
687 686 else:
688 687 log.warning(
689 688 'Comment(repo): Wrong version type {} {} '
690 689 'for comment {}'.format(
691 690 version,
692 691 type(version),
693 692 comment_id,
694 693 )
695 694 )
696 695 raise HTTPNotFound()
697 696
698 697 try:
699 698 comment_history = CommentsModel().edit(
700 699 comment_id=comment_id,
701 700 text=text,
702 701 auth_user=self._rhodecode_user,
703 702 version=version,
704 703 )
705 704 except CommentVersionMismatch:
706 705 raise HTTPConflict()
707 706
708 707 if not comment_history:
709 708 raise HTTPNotFound()
710 709
711 710 commit_id = self.request.matchdict['commit_id']
712 711 commit = self.db_repo.get_commit(commit_id)
713 712 CommentsModel().trigger_commit_comment_hook(
714 713 self.db_repo, self._rhodecode_user, 'edit',
715 714 data={'comment': comment, 'commit': commit})
716 715
717 716 Session().commit()
718 717 return {
719 718 'comment_history_id': comment_history.comment_history_id,
720 719 'comment_id': comment.comment_id,
721 720 'comment_version': comment_history.version,
722 721 'comment_author_username': comment_history.author.username,
723 722 'comment_author_gravatar': h.gravatar_url(comment_history.author.email, 16),
724 723 'comment_created_on': h.age_component(comment_history.created_on,
725 724 time_is_local=True),
726 725 }
727 726 else:
728 727 log.warning('No permissions for user %s to edit comment_id: %s',
729 728 self._rhodecode_db_user, comment_id)
730 729 raise HTTPNotFound()
731 730
732 731 @LoginRequired()
733 732 @HasRepoPermissionAnyDecorator(
734 733 'repository.read', 'repository.write', 'repository.admin')
735 734 @view_config(
736 735 route_name='repo_commit_data', request_method='GET',
737 736 renderer='json_ext', xhr=True)
738 737 def repo_commit_data(self):
739 738 commit_id = self.request.matchdict['commit_id']
740 739 self.load_default_context()
741 740
742 741 try:
743 742 return self.rhodecode_vcs_repo.get_commit(commit_id=commit_id)
744 743 except CommitDoesNotExistError as e:
745 744 return EmptyCommit(message=str(e))
746 745
747 746 @LoginRequired()
748 747 @HasRepoPermissionAnyDecorator(
749 748 'repository.read', 'repository.write', 'repository.admin')
750 749 @view_config(
751 750 route_name='repo_commit_children', request_method='GET',
752 751 renderer='json_ext', xhr=True)
753 752 def repo_commit_children(self):
754 753 commit_id = self.request.matchdict['commit_id']
755 754 self.load_default_context()
756 755
757 756 try:
758 757 commit = self.rhodecode_vcs_repo.get_commit(commit_id=commit_id)
759 758 children = commit.children
760 759 except CommitDoesNotExistError:
761 760 children = []
762 761
763 762 result = {"results": children}
764 763 return result
765 764
766 765 @LoginRequired()
767 766 @HasRepoPermissionAnyDecorator(
768 767 'repository.read', 'repository.write', 'repository.admin')
769 768 @view_config(
770 769 route_name='repo_commit_parents', request_method='GET',
771 770 renderer='json_ext')
772 771 def repo_commit_parents(self):
773 772 commit_id = self.request.matchdict['commit_id']
774 773 self.load_default_context()
775 774
776 775 try:
777 776 commit = self.rhodecode_vcs_repo.get_commit(commit_id=commit_id)
778 777 parents = commit.parents
779 778 except CommitDoesNotExistError:
780 779 parents = []
781 780 result = {"results": parents}
782 781 return result
General Comments 0
You need to be logged in to leave comments. Login now