##// END OF EJS Templates
comments: multiple changes on comments navigation/display logic...
milka -
r4543:624997f0 default
parent child Browse files
Show More
@@ -252,7 +252,7 b' But please check this code'
252 252 var comment = $('#comment-'+commentId);
253 253 var commentData = comment.data();
254 254 if (commentData.commentInline) {
255 this.createComment(comment, commentId)
255 this.createComment(comment, f_path, line_no, commentId)
256 256 } else {
257 257 Rhodecode.comments.createGeneralComment('general', "$placeholder", commentId)
258 258 }
@@ -383,6 +383,9 b' class RepoCommitsView(RepoAppView):'
383 383 text = self.request.POST.get('text')
384 384 comment_type = self.request.POST.get('comment_type')
385 385 resolves_comment_id = self.request.POST.get('resolves_comment_id', None)
386 f_path = self.request.POST.get('f_path')
387 line_no = self.request.POST.get('line')
388 target_elem_id = 'file-{}'.format(h.safeid(h.safe_unicode(f_path)))
386 389
387 390 if status:
388 391 text = text or (_('Status change %(transition_icon)s %(status)s')
@@ -404,8 +407,8 b' class RepoCommitsView(RepoAppView):'
404 407 repo=self.db_repo.repo_id,
405 408 user=self._rhodecode_db_user.user_id,
406 409 commit_id=current_id,
407 f_path=self.request.POST.get('f_path'),
408 line_no=self.request.POST.get('line'),
410 f_path=f_path,
411 line_no=line_no,
409 412 status_change=(ChangesetStatus.get_status_lbl(status)
410 413 if status else None),
411 414 status_change_type=status,
@@ -447,19 +450,20 b' class RepoCommitsView(RepoAppView):'
447 450 # finalize, commit and redirect
448 451 Session().commit()
449 452
450 data = {
451 'target_id': h.safeid(h.safe_unicode(
452 self.request.POST.get('f_path'))),
453 data = {}
454 if comment:
455 comment_id = comment.comment_id
456 data[comment_id] = {
457 'target_id': target_elem_id
453 458 }
454 if comment:
455 459 c.co = comment
456 460 c.at_version_num = 0
457 461 rendered_comment = render(
458 462 'rhodecode:templates/changeset/changeset_comment_block.mako',
459 463 self._get_template_context(c), self.request)
460 464
461 data.update(comment.get_dict())
462 data.update({'rendered_text': rendered_comment})
465 data[comment_id].update(comment.get_dict())
466 data[comment_id].update({'rendered_text': rendered_comment})
463 467
464 468 comment_broadcast_channel = channelstream.comment_channel(
465 469 self.db_repo_name, commit_obj=commit)
@@ -983,8 +983,9 b' class RepoPullRequestsView(RepoAppView, '
983 983 }
984 984 return data
985 985
986 def _get_existing_ids(self, post_data):
987 return filter(lambda e: e, map(safe_int, aslist(post_data.get('comments'), ',')))
986 @classmethod
987 def get_comment_ids(cls, post_data):
988 return filter(lambda e: e > 0, map(safe_int, aslist(post_data.get('comments'), ',')))
988 989
989 990 @LoginRequired()
990 991 @NotAnonymous()
@@ -1022,7 +1023,7 b' class RepoPullRequestsView(RepoAppView, '
1022 1023 self.register_comments_vars(c, pull_request_latest, versions, include_drafts=False)
1023 1024 all_comments = c.inline_comments_flat + c.comments
1024 1025
1025 existing_ids = self._get_existing_ids(self.request.POST)
1026 existing_ids = self.get_comment_ids(self.request.POST)
1026 1027 return _render('comments_table', all_comments, len(all_comments),
1027 1028 existing_ids=existing_ids)
1028 1029
@@ -1064,7 +1065,7 b' class RepoPullRequestsView(RepoAppView, '
1064 1065 .get_pull_request_resolved_todos(pull_request, include_drafts=False)
1065 1066
1066 1067 all_comments = c.unresolved_comments + c.resolved_comments
1067 existing_ids = self._get_existing_ids(self.request.POST)
1068 existing_ids = self.get_comment_ids(self.request.POST)
1068 1069 return _render('comments_table', all_comments, len(c.unresolved_comments),
1069 1070 todo_comments=True, existing_ids=existing_ids)
1070 1071
@@ -1518,39 +1519,26 b' class RepoPullRequestsView(RepoAppView, '
1518 1519 self._rhodecode_user)
1519 1520 raise HTTPNotFound()
1520 1521
1521 @LoginRequired()
1522 @NotAnonymous()
1523 @HasRepoPermissionAnyDecorator(
1524 'repository.read', 'repository.write', 'repository.admin')
1525 @CSRFRequired()
1526 @view_config(
1527 route_name='pullrequest_comment_create', request_method='POST',
1528 renderer='json_ext')
1529 def pull_request_comment_create(self):
1522 def _pull_request_comments_create(self, pull_request, comments):
1530 1523 _ = self.request.translate
1524 data = {}
1525 pull_request_id = pull_request.pull_request_id
1526 if not comments:
1527 return
1531 1528
1532 pull_request = PullRequest.get_or_404(
1533 self.request.matchdict['pull_request_id'])
1534 pull_request_id = pull_request.pull_request_id
1529 all_drafts = len([x for x in comments if str2bool(x['is_draft'])]) == len(comments)
1535 1530
1536 if pull_request.is_closed():
1537 log.debug('comment: forbidden because pull request is closed')
1538 raise HTTPForbidden()
1539
1540 allowed_to_comment = PullRequestModel().check_user_comment(
1541 pull_request, self._rhodecode_user)
1542 if not allowed_to_comment:
1543 log.debug('comment: forbidden because pull request is from forbidden repo')
1544 raise HTTPForbidden()
1545
1531 for entry in comments:
1546 1532 c = self.load_default_context()
1547
1548 status = self.request.POST.get('changeset_status', None)
1549 text = self.request.POST.get('text')
1550 comment_type = self.request.POST.get('comment_type')
1551 is_draft = str2bool(self.request.POST.get('draft'))
1552 resolves_comment_id = self.request.POST.get('resolves_comment_id', None)
1553 close_pull_request = self.request.POST.get('close_pull_request')
1533 comment_type = entry['comment_type']
1534 text = entry['text']
1535 status = entry['status']
1536 is_draft = str2bool(entry['is_draft'])
1537 resolves_comment_id = entry['resolves_comment_id']
1538 close_pull_request = entry['close_pull_request']
1539 f_path = entry['f_path']
1540 line_no = entry['line']
1541 target_elem_id = 'file-{}'.format(h.safeid(h.safe_unicode(f_path)))
1554 1542
1555 1543 # the logic here should work like following, if we submit close
1556 1544 # pr comment, use `close_pull_request_with_comment` function
@@ -1594,8 +1582,8 b' class RepoPullRequestsView(RepoAppView, '
1594 1582 repo=self.db_repo.repo_id,
1595 1583 user=self._rhodecode_user.user_id,
1596 1584 pull_request=pull_request,
1597 f_path=self.request.POST.get('f_path'),
1598 line_no=self.request.POST.get('line'),
1585 f_path=f_path,
1586 line_no=line_no,
1599 1587 status_change=(ChangesetStatus.get_status_lbl(status)
1600 1588 if status and allowed_to_change_status else None),
1601 1589 status_change_type=(status
@@ -1639,33 +1627,38 b' class RepoPullRequestsView(RepoAppView, '
1639 1627 pull_request, self._rhodecode_user, 'review_status_change',
1640 1628 data={'status': calculated_status})
1641 1629
1642 Session().commit()
1630 comment_id = comment.comment_id
1631 data[comment_id] = {
1632 'target_id': target_elem_id
1633 }
1634 Session().flush()
1643 1635
1644 data = {
1645 'target_id': h.safeid(h.safe_unicode(
1646 self.request.POST.get('f_path'))),
1647 }
1648
1649 if comment:
1650 1636 c.co = comment
1651 1637 c.at_version_num = None
1638 c.is_new = True
1652 1639 rendered_comment = render(
1653 1640 'rhodecode:templates/changeset/changeset_comment_block.mako',
1654 1641 self._get_template_context(c), self.request)
1655 1642
1656 data.update(comment.get_dict())
1657 data.update({'rendered_text': rendered_comment})
1643 data[comment_id].update(comment.get_dict())
1644 data[comment_id].update({'rendered_text': rendered_comment})
1645
1646 Session().commit()
1658 1647
1659 1648 # skip channelstream for draft comments
1660 if not is_draft:
1649 if all_drafts:
1661 1650 comment_broadcast_channel = channelstream.comment_channel(
1662 1651 self.db_repo_name, pull_request_obj=pull_request)
1663 1652
1664 1653 comment_data = data
1665 1654 comment_type = 'inline' if is_inline else 'general'
1655 if len(data) == 1:
1656 msg = _('posted {} new {} comment').format(len(data), comment_type)
1657 else:
1658 msg = _('posted {} new {} comments').format(len(data), comment_type)
1659
1666 1660 channelstream.comment_channelstream_push(
1667 self.request, comment_broadcast_channel, self._rhodecode_user,
1668 _('posted a new {} comment').format(comment_type),
1661 self.request, comment_broadcast_channel, self._rhodecode_user, msg,
1669 1662 comment_data=comment_data)
1670 1663
1671 1664 return data
@@ -1676,6 +1669,43 b' class RepoPullRequestsView(RepoAppView, '
1676 1669 'repository.read', 'repository.write', 'repository.admin')
1677 1670 @CSRFRequired()
1678 1671 @view_config(
1672 route_name='pullrequest_comment_create', request_method='POST',
1673 renderer='json_ext')
1674 def pull_request_comment_create(self):
1675 _ = self.request.translate
1676
1677 pull_request = PullRequest.get_or_404(self.request.matchdict['pull_request_id'])
1678
1679 if pull_request.is_closed():
1680 log.debug('comment: forbidden because pull request is closed')
1681 raise HTTPForbidden()
1682
1683 allowed_to_comment = PullRequestModel().check_user_comment(
1684 pull_request, self._rhodecode_user)
1685 if not allowed_to_comment:
1686 log.debug('comment: forbidden because pull request is from forbidden repo')
1687 raise HTTPForbidden()
1688
1689 comment_data = {
1690 'comment_type': self.request.POST.get('comment_type'),
1691 'text': self.request.POST.get('text'),
1692 'status': self.request.POST.get('changeset_status', None),
1693 'is_draft': self.request.POST.get('draft'),
1694 'resolves_comment_id': self.request.POST.get('resolves_comment_id', None),
1695 'close_pull_request': self.request.POST.get('close_pull_request'),
1696 'f_path': self.request.POST.get('f_path'),
1697 'line': self.request.POST.get('line'),
1698 }
1699 data = self._pull_request_comments_create(pull_request, [comment_data])
1700
1701 return data
1702
1703 @LoginRequired()
1704 @NotAnonymous()
1705 @HasRepoPermissionAnyDecorator(
1706 'repository.read', 'repository.write', 'repository.admin')
1707 @CSRFRequired()
1708 @view_config(
1679 1709 route_name='pullrequest_comment_delete', request_method='POST',
1680 1710 renderer='json_ext')
1681 1711 def pull_request_comment_delete(self):
@@ -339,13 +339,12 b' def comment_channelstream_push(request, '
339 339
340 340 comment_data = kwargs.pop('comment_data', {})
341 341 user_data = kwargs.pop('user_data', {})
342 comment_id = comment_data.get('comment_id')
342 comment_id = comment_data.keys()[0] if comment_data else ''
343 343
344 message = '<strong>{}</strong> {} #{}, {}'.format(
344 message = '<strong>{}</strong> {} #{}'.format(
345 345 user.username,
346 346 msg,
347 347 comment_id,
348 _reload_link(_('Reload page to see new comments')),
349 348 )
350 349
351 350 message_obj = {
@@ -1148,7 +1148,7 b' class DiffLimitExceeded(Exception):'
1148 1148
1149 1149 # NOTE(marcink): if diffs.mako change, probably this
1150 1150 # needs a bump to next version
1151 CURRENT_DIFF_VERSION = 'v4'
1151 CURRENT_DIFF_VERSION = 'v5'
1152 1152
1153 1153
1154 1154 def _cleanup_cache_file(cached_diff_file):
@@ -1002,7 +1002,7 b' input.filediff-collapse-state {'
1002 1002 .nav-chunk {
1003 1003 position: absolute;
1004 1004 right: 20px;
1005 margin-top: -17px;
1005 margin-top: -15px;
1006 1006 }
1007 1007
1008 1008 .nav-chunk.selected {
@@ -4,7 +4,7 b''
4 4
5 5
6 6 // Comments
7 @comment-outdated-opacity: 0.6;
7 @comment-outdated-opacity: 1.0;
8 8
9 9 .comments {
10 10 width: 100%;
@@ -64,32 +64,34 b' tr.inline-comments div {'
64 64 .comment-draft {
65 65 float: left;
66 66 margin-right: 10px;
67 font-weight: 600;
68 color: @alert3;
67 font-weight: 400;
68 color: @color-draft;
69 }
70
71 .comment-new {
72 float: left;
73 margin-right: 10px;
74 font-weight: 400;
75 color: @color-new;
69 76 }
70 77
71 78 .comment-label {
72 79 float: left;
73 80
74 padding: 0.4em 0.4em;
75 margin: 2px 4px 0px 0px;
76 display: inline-block;
81 padding: 0 8px 0 0;
77 82 min-height: 0;
78 83
79 84 text-align: center;
80 85 font-size: 10px;
81 line-height: .8em;
82 86
83 87 font-family: @text-italic;
84 88 font-style: italic;
85 89 background: #fff none;
86 90 color: @grey3;
87 border: 1px solid @grey4;
88 91 white-space: nowrap;
89 92
90 93 text-transform: uppercase;
91 94 min-width: 50px;
92 border-radius: 4px;
93 95
94 96 &.todo {
95 97 color: @color5;
@@ -277,63 +279,164 b' tr.inline-comments div {'
277 279 .comment-outdated {
278 280 opacity: @comment-outdated-opacity;
279 281 }
282
283 .comment-outdated-label {
284 color: @grey3;
285 padding-right: 4px;
286 }
280 287 }
281 288
282 289 .inline-comments {
283 border-radius: @border-radius;
290
284 291 .comment {
285 292 margin: 0;
286 border-radius: @border-radius;
287 293 }
294
288 295 .comment-outdated {
289 296 opacity: @comment-outdated-opacity;
290 297 }
291 298
299 .comment-outdated-label {
300 color: @grey3;
301 padding-right: 4px;
302 }
303
292 304 .comment-inline {
305
306 &:first-child {
307 margin: 4px 4px 0 4px;
308 border-top: 1px solid @grey5;
309 border-bottom: 0 solid @grey5;
310 border-left: 1px solid @grey5;
311 border-right: 1px solid @grey5;
312 .border-radius-top(4px);
313 }
314
315 &:only-child {
316 margin: 4px 4px 0 4px;
317 border-top: 1px solid @grey5;
318 border-bottom: 0 solid @grey5;
319 border-left: 1px solid @grey5;
320 border-right: 1px solid @grey5;
321 .border-radius-top(4px);
322 }
323
293 324 background: white;
294 325 padding: @comment-padding @comment-padding;
295 border: @comment-padding solid @grey6;
326 margin: 0 4px 0 4px;
327 border-top: 0 solid @grey5;
328 border-bottom: 0 solid @grey5;
329 border-left: 1px solid @grey5;
330 border-right: 1px solid @grey5;
296 331
297 332 .text {
298 333 border: none;
299 334 }
335
300 336 .meta {
301 337 border-bottom: 1px solid @grey6;
302 338 margin: -5px 0px;
303 339 line-height: 24px;
304 340 }
341
305 342 }
306 343 .comment-selected {
307 344 border-left: 6px solid @comment-highlight-color;
308 345 }
346
347 .comment-inline-form-open {
348 display: block !important;
349 }
350
309 351 .comment-inline-form {
310 padding: @comment-padding;
311 352 display: none;
312 353 }
313 .cb-comment-add-button {
314 margin: @comment-padding;
354
355 .comment-inline-form-edit {
356 padding: 0;
357 margin: 0px 4px 2px 4px;
358 }
359
360 .reply-thread-container {
361 display: table;
362 width: 100%;
363 padding: 0px 4px 4px 4px;
364 }
365
366 .reply-thread-container-wrapper {
367 margin: 0 4px 4px 4px;
368 border-top: 0 solid @grey5;
369 border-bottom: 1px solid @grey5;
370 border-left: 1px solid @grey5;
371 border-right: 1px solid @grey5;
372 .border-radius-bottom(4px);
373 }
374
375 .reply-thread-gravatar {
376 display: table-cell;
377 width: 24px;
378 height: 24px;
379 padding-top: 10px;
380 padding-left: 10px;
381 background-color: #eeeeee;
382 vertical-align: top;
315 383 }
384
385 .reply-thread-reply-button {
386 display: table-cell;
387 width: 100%;
388 height: 33px;
389 padding: 3px 8px;
390 margin-left: 8px;
391 background-color: #eeeeee;
392 }
393
394 .reply-thread-reply-button .cb-comment-add-button {
395 border-radius: 4px;
396 width: 100%;
397 padding: 6px 2px;
398 text-align: left;
399 cursor: text;
400 color: @grey3;
401 }
402 .reply-thread-reply-button .cb-comment-add-button:hover {
403 background-color: white;
404 color: @grey2;
405 }
406
407 .reply-thread-last {
408 display: table-cell;
409 width: 10px;
410 }
411
412 /* Hide reply box when it's a first element,
413 can happen when drafts are saved but not shown to specific user,
414 or there are outdated comments hidden
415 */
416 .reply-thread-container-wrapper:first-child:not(.comment-form-active) {
417 display: none;
418 }
419
420 .reply-thread-container-wrapper.comment-outdated {
421 display: none
422 }
423
316 424 /* hide add comment button when form is open */
317 425 .comment-inline-form-open ~ .cb-comment-add-button {
318 426 display: none;
319 427 }
320 .comment-inline-form-open {
321 display: block;
428
429 /* hide add comment button when only comment is being deleted */
430 .comment-deleting:first-child + .cb-comment-add-button {
431 display: none;
322 432 }
433
323 434 /* hide add comment button when form but no comments */
324 435 .comment-inline-form:first-child + .cb-comment-add-button {
325 436 display: none;
326 437 }
327 /* hide add comment button when no comments or form */
328 .cb-comment-add-button:first-child {
329 display: none;
438
330 439 }
331 /* hide add comment button when only comment is being deleted */
332 .comment-deleting:first-child + .cb-comment-add-button {
333 display: none;
334 }
335 }
336
337 440
338 441 .show-outdated-comments {
339 442 display: inline;
@@ -387,23 +490,40 b' form.comment-form {'
387 490 }
388 491
389 492 .comment-footer {
390 position: relative;
493 display: table;
391 494 width: 100%;
392 min-height: 42px;
495 height: 42px;
393 496
394 .status_box,
497 .comment-status-box,
395 498 .cancel-button {
396 float: left;
397 499 display: inline-block;
398 500 }
399 501
400 .status_box {
502 .comment-status-box {
401 503 margin-left: 10px;
402 504 }
403 505
404 506 .action-buttons {
405 float: left;
406 display: inline-block;
507 display: table-cell;
508 padding: 5px 0 5px 2px;
509 }
510
511 .toolbar-text {
512 height: 42px;
513 display: table-cell;
514 vertical-align: bottom;
515 font-size: 11px;
516 color: @grey4;
517 text-align: right;
518
519 a {
520 color: @grey4;
521 }
522
523 p {
524 padding: 0;
525 margin: 0;
526 }
407 527 }
408 528
409 529 .action-buttons-extra {
@@ -434,10 +554,6 b' form.comment-form {'
434 554 margin-right: 0;
435 555 }
436 556
437 .comment-footer {
438 margin-bottom: 50px;
439 margin-top: 10px;
440 }
441 557 }
442 558
443 559
@@ -489,8 +605,8 b' form.comment-form {'
489 605 .injected_diff .comment-inline-form,
490 606 .comment-inline-form {
491 607 background-color: white;
492 margin-top: 10px;
493 margin-bottom: 20px;
608 margin-top: 4px;
609 margin-bottom: 10px;
494 610 }
495 611
496 612 .inline-form {
@@ -526,9 +642,6 b' form.comment-form {'
526 642 margin: 0px;
527 643 }
528 644
529 .comment-inline-form .comment-footer {
530 margin: 10px 0px 0px 0px;
531 }
532 645
533 646 .hide-inline-form-button {
534 647 margin-left: 5px;
@@ -554,6 +667,7 b' comment-area-text {'
554 667
555 668 .comment-area-header {
556 669 height: 35px;
670 border-bottom: 1px solid @grey5;
557 671 }
558 672
559 673 .comment-area-header .nav-links {
@@ -561,6 +675,7 b' comment-area-text {'
561 675 flex-flow: row wrap;
562 676 -webkit-flex-flow: row wrap;
563 677 width: 100%;
678 border: none;
564 679 }
565 680
566 681 .comment-area-footer {
@@ -629,14 +744,3 b' comment-area-text {'
629 744 border-bottom: 2px solid transparent;
630 745 }
631 746
632 .toolbar-text {
633 float: right;
634 font-size: 11px;
635 color: @grey4;
636 text-align: right;
637
638 a {
639 color: @grey4;
640 }
641 }
642
@@ -3212,7 +3212,12 b' details:not([open]) > :not(summary) {'
3212 3212
3213 3213 .sidebar-element {
3214 3214 margin-top: 20px;
3215 }
3215
3216 .icon-draft {
3217 color: @color-draft
3218 }
3219 }
3220
3216 3221
3217 3222 .right-sidebar-collapsed-state {
3218 3223 display: flex;
@@ -3235,5 +3240,4 b' details:not([open]) > :not(summary) {'
3235 3240
3236 3241 .old-comments-marker td {
3237 3242 padding-top: 15px;
3238 border-bottom: 1px solid @grey5;
3239 }
3243 }
@@ -47,6 +47,8 b''
47 47
48 48 // Highlight color for lines and colors
49 49 @comment-highlight-color: #ffd887;
50 @color-draft: darken(@alert3, 30%);
51 @color-new: darken(@alert1, 5%);
50 52
51 53 // FONTS
52 54 @basefontsize: 13px;
@@ -71,14 +71,20 b' export class RhodecodeApp extends Polyme'
71 71 if (elem) {
72 72 elem.handleNotification(data);
73 73 }
74
75 74 }
76 75
77 76 handleComment(data) {
78 if (data.message.comment_id) {
77
78 if (data.message.comment_data.length !== 0) {
79 79 if (window.refreshAllComments !== undefined) {
80 80 refreshAllComments()
81 81 }
82 var json_data = data.message.comment_data;
83
84 if (window.commentsController !== undefined) {
85
86 window.commentsController.attachComment(json_data)
87 }
82 88 }
83 89 }
84 90
@@ -364,12 +364,15 b' var _submitAjaxPOST = function(url, post'
364 364 postData['close_pull_request'] = true;
365 365 }
366 366
367 var submitSuccessCallback = function(o) {
367 // submitSuccess for general comments
368 var submitSuccessCallback = function(json_data) {
368 369 // reload page if we change status for single commit.
369 370 if (status && self.commitId) {
370 371 location.reload(true);
371 372 } else {
372 $('#injected_page_comments').append(o.rendered_text);
373 // inject newly created comments, json_data is {<comment_id>: {}}
374 self.attachGeneralComment(json_data)
375
373 376 self.resetCommentFormState();
374 377 timeagoActivate();
375 378 tooltipActivate();
@@ -565,26 +568,6 b' var CommentsController = function() {'
565 568 var mainComment = '#text';
566 569 var self = this;
567 570
568 this.cancelComment = function (node) {
569 var $node = $(node);
570 var edit = $(this).attr('edit');
571 if (edit) {
572 var $general_comments = null;
573 var $inline_comments = $node.closest('div.inline-comments');
574 if (!$inline_comments.length) {
575 $general_comments = $('#comments');
576 var $comment = $general_comments.parent().find('div.comment:hidden');
577 // show hidden general comment form
578 $('#cb-comment-general-form-placeholder').show();
579 } else {
580 var $comment = $inline_comments.find('div.comment:hidden');
581 }
582 $comment.show();
583 }
584 $node.closest('.comment-inline-form').remove();
585 return false;
586 };
587
588 571 this.showVersion = function (comment_id, comment_history_id) {
589 572
590 573 var historyViewUrl = pyroutes.url(
@@ -682,6 +665,35 b' var CommentsController = function() {'
682 665 return self.scrollToComment(node, -1, true);
683 666 };
684 667
668 this.cancelComment = function (node) {
669 var $node = $(node);
670 var edit = $(this).attr('edit');
671 var $inlineComments = $node.closest('div.inline-comments');
672
673 if (edit) {
674 var $general_comments = null;
675 if (!$inlineComments.length) {
676 $general_comments = $('#comments');
677 var $comment = $general_comments.parent().find('div.comment:hidden');
678 // show hidden general comment form
679 $('#cb-comment-general-form-placeholder').show();
680 } else {
681 var $comment = $inlineComments.find('div.comment:hidden');
682 }
683 $comment.show();
684 }
685 var $replyWrapper = $node.closest('.comment-inline-form').closest('.reply-thread-container-wrapper')
686 $replyWrapper.removeClass('comment-form-active');
687
688 var lastComment = $inlineComments.find('.comment-inline').last();
689 if ($(lastComment).hasClass('comment-outdated')) {
690 $replyWrapper.hide();
691 }
692
693 $node.closest('.comment-inline-form').remove();
694 return false;
695 };
696
685 697 this._deleteComment = function(node) {
686 698 var $node = $(node);
687 699 var $td = $node.closest('td');
@@ -751,7 +763,7 b' var CommentsController = function() {'
751 763 this.finalizeDrafts = function(commentIds) {
752 764
753 765 SwalNoAnimation.fire({
754 title: _ngettext('Submit {0} draft comment', 'Submit {0} draft comments', commentIds.length).format(commentIds.length),
766 title: _ngettext('Submit {0} draft comment.', 'Submit {0} draft comments.', commentIds.length).format(commentIds.length),
755 767 icon: 'warning',
756 768 showCancelButton: true,
757 769 confirmButtonText: _gettext('Yes, finalize drafts'),
@@ -764,6 +776,7 b' var CommentsController = function() {'
764 776 };
765 777
766 778 this.toggleWideMode = function (node) {
779
767 780 if ($('#content').hasClass('wrapper')) {
768 781 $('#content').removeClass("wrapper");
769 782 $('#content').addClass("wide-mode-wrapper");
@@ -778,16 +791,49 b' var CommentsController = function() {'
778 791
779 792 };
780 793
781 this.toggleComments = function(node, show) {
794 /**
795 * Turn off/on all comments in file diff
796 */
797 this.toggleDiffComments = function(node) {
798 // Find closes filediff container
782 799 var $filediff = $(node).closest('.filediff');
800 if ($(node).hasClass('toggle-on')) {
801 var show = false;
802 } else if ($(node).hasClass('toggle-off')) {
803 var show = true;
804 }
805
806 // Toggle each individual comment block, so we can un-toggle single ones
807 $.each($filediff.find('.toggle-comment-action'), function(idx, val) {
808 self.toggleLineComments($(val), show)
809 })
810
811 // since we change the height of the diff container that has anchor points for upper
812 // sticky header, we need to tell it to re-calculate those
813 if (window.updateSticky !== undefined) {
814 // potentially our comments change the active window size, so we
815 // notify sticky elements
816 updateSticky()
817 }
818
819 return false;
820 }
821
822 this.toggleLineComments = function(node, show) {
823
824 var trElem = $(node).closest('tr')
825
783 826 if (show === true) {
784 $filediff.removeClass('hide-comments');
827 // mark outdated comments as visible before the toggle;
828 $(trElem).find('.comment-outdated').show();
829 $(trElem).removeClass('hide-line-comments');
785 830 } else if (show === false) {
786 $filediff.find('.hide-line-comments').removeClass('hide-line-comments');
787 $filediff.addClass('hide-comments');
831 $(trElem).find('.comment-outdated').hide();
832 $(trElem).addClass('hide-line-comments');
788 833 } else {
789 $filediff.find('.hide-line-comments').removeClass('hide-line-comments');
790 $filediff.toggleClass('hide-comments');
834 // mark outdated comments as visible before the toggle;
835 $(trElem).find('.comment-outdated').show();
836 $(trElem).toggleClass('hide-line-comments');
791 837 }
792 838
793 839 // since we change the height of the diff container that has anchor points for upper
@@ -798,15 +844,6 b' var CommentsController = function() {'
798 844 updateSticky()
799 845 }
800 846
801 return false;
802 };
803
804 this.toggleLineComments = function(node) {
805 self.toggleComments(node, true);
806 var $node = $(node);
807 // mark outdated comments as visible before the toggle;
808 $(node.closest('tr')).find('.comment-outdated').show();
809 $node.closest('tr').toggleClass('hide-line-comments');
810 847 };
811 848
812 849 this.createCommentForm = function(formElement, lineno, placeholderText, initAutocompleteActions, resolvesCommentId, edit, comment_id){
@@ -960,63 +997,57 b' var CommentsController = function() {'
960 997 return commentForm;
961 998 };
962 999
963 this.editComment = function(node) {
1000 this.editComment = function(node, line_no, f_path) {
1001 self.edit = true;
964 1002 var $node = $(node);
1003 var $td = $node.closest('td');
1004
965 1005 var $comment = $(node).closest('.comment');
966 1006 var comment_id = $($comment).data('commentId');
967 1007 var isDraft = $($comment).data('commentDraft');
968 var $form = null
1008 var $editForm = null
969 1009
970 1010 var $comments = $node.closest('div.inline-comments');
971 1011 var $general_comments = null;
972 var lineno = null;
973 1012
974 1013 if($comments.length){
975 1014 // inline comments setup
976 $form = $comments.find('.comment-inline-form');
977 lineno = self.getLineNumber(node)
1015 $editForm = $comments.find('.comment-inline-form');
1016 line_no = self.getLineNumber(node)
978 1017 }
979 1018 else{
980 1019 // general comments setup
981 1020 $comments = $('#comments');
982 $form = $comments.find('.comment-inline-form');
983 lineno = $comment[0].id
1021 $editForm = $comments.find('.comment-inline-form');
1022 line_no = $comment[0].id
984 1023 $('#cb-comment-general-form-placeholder').hide();
985 1024 }
986 1025
987 this.edit = true;
1026 if ($editForm.length === 0) {
988 1027
989 if (!$form.length) {
990
1028 // unhide all comments if they are hidden for a proper REPLY mode
991 1029 var $filediff = $node.closest('.filediff');
992 1030 $filediff.removeClass('hide-comments');
993 var f_path = $filediff.attr('data-f-path');
994
995 // create a new HTML from template
996 1031
997 var tmpl = $('#cb-comment-inline-form-template').html();
998 tmpl = tmpl.format(escapeHtml(f_path), lineno);
999 $form = $(tmpl);
1000 $comment.after($form)
1032 $editForm = self.createNewFormWrapper(f_path, line_no);
1033 if(f_path && line_no) {
1034 $editForm.addClass('comment-inline-form-edit')
1035 }
1001 1036
1002 var _form = $($form[0]).find('form');
1037 $comment.after($editForm)
1038
1039 var _form = $($editForm[0]).find('form');
1003 1040 var autocompleteActions = ['as_note',];
1004 1041 var commentForm = this.createCommentForm(
1005 _form, lineno, '', autocompleteActions, resolvesCommentId,
1042 _form, line_no, '', autocompleteActions, resolvesCommentId,
1006 1043 this.edit, comment_id);
1007 1044 var old_comment_text_binary = $comment.attr('data-comment-text');
1008 1045 var old_comment_text = b64DecodeUnicode(old_comment_text_binary);
1009 1046 commentForm.cm.setValue(old_comment_text);
1010 1047 $comment.hide();
1048 tooltipActivate();
1011 1049
1012 $.Topic('/ui/plugins/code/comment_form_built').prepareOrPublish({
1013 form: _form,
1014 parent: $comments,
1015 lineno: lineno,
1016 f_path: f_path}
1017 );
1018
1019 // set a CUSTOM submit handler for inline comments.
1050 // set a CUSTOM submit handler for inline comment edit action.
1020 1051 commentForm.setHandleFormSubmit(function(o) {
1021 1052 var text = commentForm.cm.getValue();
1022 1053 var commentType = commentForm.getCommentType();
@@ -1048,7 +1079,7 b' var CommentsController = function() {'
1048 1079 var postData = {
1049 1080 'text': text,
1050 1081 'f_path': f_path,
1051 'line': lineno,
1082 'line': line_no,
1052 1083 'comment_type': commentType,
1053 1084 'draft': isDraft,
1054 1085 'version': version,
@@ -1056,7 +1087,7 b' var CommentsController = function() {'
1056 1087 };
1057 1088
1058 1089 var submitSuccessCallback = function(json_data) {
1059 $form.remove();
1090 $editForm.remove();
1060 1091 $comment.show();
1061 1092 var postData = {
1062 1093 'text': text,
@@ -1121,8 +1152,7 b' var CommentsController = function() {'
1121 1152 'commit_id': templateContext.commit_data.commit_id});
1122 1153
1123 1154 _submitAjaxPOST(
1124 previewUrl, postData, successRenderCommit,
1125 failRenderCommit
1155 previewUrl, postData, successRenderCommit, failRenderCommit
1126 1156 );
1127 1157
1128 1158 try {
@@ -1178,49 +1208,103 b' var CommentsController = function() {'
1178 1208 });
1179 1209 }
1180 1210
1181 $form.addClass('comment-inline-form-open');
1211 $editForm.addClass('comment-inline-form-open');
1182 1212 };
1183 1213
1184 this.createComment = function(node, resolutionComment) {
1185 var resolvesCommentId = resolutionComment || null;
1214 this.attachComment = function(json_data) {
1215 var self = this;
1216 $.each(json_data, function(idx, val) {
1217 var json_data_elem = [val]
1218 var isInline = val.comment_f_path && val.comment_lineno
1219
1220 if (isInline) {
1221 self.attachInlineComment(json_data_elem)
1222 } else {
1223 self.attachGeneralComment(json_data_elem)
1224 }
1225 })
1226
1227 }
1228
1229 this.attachGeneralComment = function(json_data) {
1230 $.each(json_data, function(idx, val) {
1231 $('#injected_page_comments').append(val.rendered_text);
1232 })
1233 }
1234
1235 this.attachInlineComment = function(json_data) {
1236
1237 $.each(json_data, function (idx, val) {
1238 var line_qry = '*[data-line-no="{0}"]'.format(val.line_no);
1239 var html = val.rendered_text;
1240 var $inlineComments = $('#' + val.target_id)
1241 .find(line_qry)
1242 .find('.inline-comments');
1243
1244 var lastComment = $inlineComments.find('.comment-inline').last();
1245
1246 if (lastComment.length === 0) {
1247 // first comment, we append simply
1248 $inlineComments.find('.reply-thread-container-wrapper').before(html);
1249 } else {
1250 $(lastComment).after(html)
1251 }
1252
1253 })
1254
1255 };
1256
1257 this.createNewFormWrapper = function(f_path, line_no) {
1258 // create a new reply HTML form from template
1259 var tmpl = $('#cb-comment-inline-form-template').html();
1260 tmpl = tmpl.format(escapeHtml(f_path), line_no);
1261 return $(tmpl);
1262 }
1263
1264 this.createComment = function(node, f_path, line_no, resolutionComment) {
1265 self.edit = false;
1186 1266 var $node = $(node);
1187 1267 var $td = $node.closest('td');
1188 var $form = $td.find('.comment-inline-form');
1189 this.edit = false;
1268 var resolvesCommentId = resolutionComment || null;
1190 1269
1191 if (!$form.length) {
1270 var $replyForm = $td.find('.comment-inline-form');
1192 1271
1193 var $filediff = $node.closest('.filediff');
1194 $filediff.removeClass('hide-comments');
1195 var f_path = $filediff.attr('data-f-path');
1196 var lineno = self.getLineNumber(node);
1197 // create a new HTML from template
1198 var tmpl = $('#cb-comment-inline-form-template').html();
1199 tmpl = tmpl.format(escapeHtml(f_path), lineno);
1200 $form = $(tmpl);
1272 // if form isn't existing, we're generating a new one and injecting it.
1273 if ($replyForm.length === 0) {
1274
1275 // unhide/expand all comments if they are hidden for a proper REPLY mode
1276 self.toggleLineComments($node, true);
1277
1278 $replyForm = self.createNewFormWrapper(f_path, line_no);
1201 1279
1202 1280 var $comments = $td.find('.inline-comments');
1203 if (!$comments.length) {
1204 $comments = $(
1205 $('#cb-comments-inline-container-template').html());
1206 $td.append($comments);
1281
1282 // There aren't any comments, we init the `.inline-comments` with `reply-thread-container` first
1283 if ($comments.length===0) {
1284 var replBtn = '<button class="cb-comment-add-button" onclick="return Rhodecode.comments.createComment(this, \'{0}\', \'{1}\', null)">Reply...</button>'.format(f_path, line_no)
1285 var $reply_container = $('#cb-comments-inline-container-template')
1286 $reply_container.find('button.cb-comment-add-button').replaceWith(replBtn);
1287 $td.append($($reply_container).html());
1207 1288 }
1208 1289
1209 $td.find('.cb-comment-add-button').before($form);
1290 // default comment button exists, so we prepend the form for leaving initial comment
1291 $td.find('.cb-comment-add-button').before($replyForm);
1292 // set marker, that we have a open form
1293 var $replyWrapper = $td.find('.reply-thread-container-wrapper')
1294 $replyWrapper.addClass('comment-form-active');
1210 1295
1211 var placeholderText = _gettext('Leave a comment on line {0}.').format(lineno);
1212 var _form = $($form[0]).find('form');
1296 var lastComment = $comments.find('.comment-inline').last();
1297 if ($(lastComment).hasClass('comment-outdated')) {
1298 $replyWrapper.show();
1299 }
1300
1301 var _form = $($replyForm[0]).find('form');
1213 1302 var autocompleteActions = ['as_note', 'as_todo'];
1214 1303 var comment_id=null;
1215 var commentForm = this.createCommentForm(
1216 _form, lineno, placeholderText, autocompleteActions, resolvesCommentId, this.edit, comment_id);
1217
1218 $.Topic('/ui/plugins/code/comment_form_built').prepareOrPublish({
1219 form: _form,
1220 parent: $td[0],
1221 lineno: lineno,
1222 f_path: f_path}
1223 );
1304 var placeholderText = _gettext('Leave a comment on file {0} line {1}.').format(f_path, line_no);
1305 var commentForm = self.createCommentForm(
1306 _form, line_no, placeholderText, autocompleteActions, resolvesCommentId,
1307 self.edit, comment_id);
1224 1308
1225 1309 // set a CUSTOM submit handler for inline comments.
1226 1310 commentForm.setHandleFormSubmit(function(o) {
@@ -1233,12 +1317,13 b' var CommentsController = function() {'
1233 1317 return;
1234 1318 }
1235 1319
1236 if (lineno === undefined) {
1237 alert('missing line !');
1320 if (line_no === undefined) {
1321 alert('Error: unable to fetch line number for this inline comment !');
1238 1322 return;
1239 1323 }
1324
1240 1325 if (f_path === undefined) {
1241 alert('missing file path !');
1326 alert('Error: unable to fetch file path for this inline comment !');
1242 1327 return;
1243 1328 }
1244 1329
@@ -1249,7 +1334,7 b' var CommentsController = function() {'
1249 1334 var postData = {
1250 1335 'text': text,
1251 1336 'f_path': f_path,
1252 'line': lineno,
1337 'line': line_no,
1253 1338 'comment_type': commentType,
1254 1339 'draft': isDraft,
1255 1340 'csrf_token': CSRF_TOKEN
@@ -1258,14 +1343,16 b' var CommentsController = function() {'
1258 1343 postData['resolves_comment_id'] = resolvesCommentId;
1259 1344 }
1260 1345
1346 // submitSuccess for inline commits
1261 1347 var submitSuccessCallback = function(json_data) {
1262 $form.remove();
1348
1349 $replyForm.remove();
1350 $td.find('.reply-thread-container-wrapper').removeClass('comment-form-active');
1351
1263 1352 try {
1264 var html = json_data.rendered_text;
1265 var lineno = json_data.line_no;
1266 var target_id = json_data.target_id;
1267 1353
1268 $comments.find('.cb-comment-add-button').before(html);
1354 // inject newly created comments, json_data is {<comment_id>: {}}
1355 self.attachInlineComment(json_data)
1269 1356
1270 1357 //mark visually which comment was resolved
1271 1358 if (resolvesCommentId) {
@@ -1273,17 +1360,15 b' var CommentsController = function() {'
1273 1360 }
1274 1361
1275 1362 // run global callback on submit
1276 commentForm.globalSubmitSuccessCallback({draft: isDraft, comment_id: comment_id});
1363 commentForm.globalSubmitSuccessCallback({
1364 draft: isDraft,
1365 comment_id: comment_id
1366 });
1277 1367
1278 1368 } catch (e) {
1279 1369 console.error(e);
1280 1370 }
1281 1371
1282 // re trigger the linkification of next/prev navigation
1283 linkifyComments($('.inline-comment-injected'));
1284 timeagoActivate();
1285 tooltipActivate();
1286
1287 1372 if (window.updateSticky !== undefined) {
1288 1373 // potentially our comments change the active window size, so we
1289 1374 // notify sticky elements
@@ -1297,19 +1382,27 b' var CommentsController = function() {'
1297 1382
1298 1383 commentForm.setActionButtonsDisabled(false);
1299 1384
1385 // re trigger the linkification of next/prev navigation
1386 linkifyComments($('.inline-comment-injected'));
1387 timeagoActivate();
1388 tooltipActivate();
1300 1389 };
1390
1301 1391 var submitFailCallback = function(jqXHR, textStatus, errorThrown) {
1302 1392 var prefix = "Error while submitting comment.\n"
1303 1393 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
1304 1394 ajaxErrorSwal(message);
1305 1395 commentForm.resetCommentFormState(text)
1306 1396 };
1397
1307 1398 commentForm.submitAjaxPOST(
1308 1399 commentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback);
1309 1400 });
1310 1401 }
1311 1402
1312 $form.addClass('comment-inline-form-open');
1403 // Finally "open" our reply form, since we know there are comments and we have the "attached" old form
1404 $replyForm.addClass('comment-inline-form-open');
1405 tooltipActivate();
1313 1406 };
1314 1407
1315 1408 this.createResolutionComment = function(commentId){
@@ -1319,9 +1412,12 b' var CommentsController = function() {'
1319 1412 var comment = $('#comment-'+commentId);
1320 1413 var commentData = comment.data();
1321 1414 if (commentData.commentInline) {
1322 this.createComment(comment, commentId)
1415 var f_path = commentData.fPath;
1416 var line_no = commentData.lineNo;
1417 //TODO check this if we need to give f_path/line_no
1418 this.createComment(comment, f_path, line_no, commentId)
1323 1419 } else {
1324 Rhodecode.comments.createGeneralComment('general', "$placeholder", commentId)
1420 this.createGeneralComment('general', "$placeholder", commentId)
1325 1421 }
1326 1422
1327 1423 return false;
@@ -1347,3 +1443,8 b' var CommentsController = function() {'
1347 1443 };
1348 1444
1349 1445 };
1446
1447 window.commentHelp = function(renderer) {
1448 var funcData = {'renderer': renderer}
1449 return renderTemplate('commentHelpHovercard', funcData)
1450 } No newline at end of file
@@ -42,12 +42,22 b' window.toggleElement = function (elem, t'
42 42 var $elem = $(elem);
43 43 var $target = $(target);
44 44
45 if ($target.is(':visible') || $target.length === 0) {
45 if (target !== undefined) {
46 var show = $target.is(':visible') || $target.length === 0;
47 } else {
48 var show = $elem.hasClass('toggle-off')
49 }
50
51 if (show) {
46 52 $target.hide();
47 53 $elem.html($elem.data('toggleOn'))
54 $elem.addClass('toggle-on')
55 $elem.removeClass('toggle-off')
48 56 } else {
49 57 $target.show();
50 58 $elem.html($elem.data('toggleOff'))
59 $elem.addClass('toggle-off')
60 $elem.removeClass('toggle-on')
51 61 }
52 62
53 63 return false
@@ -1,7 +1,5 b''
1 1 /__MAIN_APP__ - launched when rhodecode-app element is attached to DOM
2 2 /plugins/__REGISTER__ - launched after the onDomReady() code from rhodecode.js is executed
3 /ui/plugins/code/anchor_focus - launched when rc starts to scroll on load to anchor on PR/Codeview
4 /ui/plugins/code/comment_form_built - launched when injectInlineForm() is executed and the form object is created
5 3 /notifications - shows new event notifications
6 4 /connection_controller/subscribe - subscribes user to new channels
7 5 /connection_controller/presence - receives presence change messages
@@ -1,4 +1,4 b''
1 1 ## this is a dummy html file for partial rendering on server and sending
2 2 ## generated output via ajax after comment submit
3 3 <%namespace name="comment" file="/changeset/changeset_file_comment.mako"/>
4 ${comment.comment_block(c.co, inline=c.co.is_inline)}
4 ${comment.comment_block(c.co, inline=c.co.is_inline, is_new=c.is_new)}
@@ -10,7 +10,7 b''
10 10 %>
11 11
12 12
13 <%def name="comment_block(comment, inline=False, active_pattern_entries=None)">
13 <%def name="comment_block(comment, inline=False, active_pattern_entries=None, is_new=False)">
14 14
15 15 <%
16 16 from rhodecode.model.comment import CommentsModel
@@ -40,6 +40,7 b''
40 40 data-comment-draft=${h.json.dumps(comment.draft)}
41 41 data-comment-renderer="${comment.renderer}"
42 42 data-comment-text="${comment.text | html_filters.base64,n}"
43 data-comment-f-path="${comment.f_path}"
43 44 data-comment-line-no="${comment.line_no}"
44 45 data-comment-inline=${h.json.dumps(inline)}
45 46 style="${'display: none;' if outdated_at_ver else ''}">
@@ -47,8 +48,17 b''
47 48 <div class="meta">
48 49 <div class="comment-type-label">
49 50 % if comment.draft:
50 <div class="tooltip comment-draft" title="${_('Draft comments are only visible to the author until submitted')}.">DRAFT</div>
51 <div class="tooltip comment-draft" title="${_('Draft comments are only visible to the author until submitted')}.">
52 DRAFT
53 </div>
51 54 % endif
55
56 % if is_new:
57 <div class="tooltip comment-new" title="${_('This comment was added while you browsed this page')}.">
58 NEW
59 </div>
60 % endif
61
52 62 <div class="comment-label ${comment.comment_type or 'note'}" id="comment-label-${comment.comment_id}">
53 63
54 64 ## TODO COMMENT
@@ -176,7 +186,7 b''
176 186 % if inline:
177 187 <a class="pr-version-inline" href="${request.current_route_path(_query=dict(version=comment.pull_request_version_id), _anchor='comment-{}'.format(comment.comment_id))}">
178 188 % if outdated_at_ver:
179 <code class="tooltip pr-version-num" title="${_('Outdated comment from pull request version v{0}, latest v{1}').format(comment_ver, latest_ver)}">outdated ${'v{}'.format(comment_ver)}</code>
189 <strong class="comment-outdated-label">outdated</strong> <code class="tooltip pr-version-num" title="${_('Outdated comment from pull request version v{0}, latest v{1}').format(comment_ver, latest_ver)}">${'v{}'.format(comment_ver)}</code>
180 190 <code class="action-divider">|</code>
181 191 % elif comment_ver:
182 192 <code class="tooltip pr-version-num" title="${_('Comment from pull request version v{0}, latest v{1}').format(comment_ver, latest_ver)}">${'v{}'.format(comment_ver)}</code>
@@ -222,12 +232,13 b''
222 232 %if comment.immutable is False and (c.is_super_admin or h.HasRepoPermissionAny('repository.admin')(c.repo_name) or comment.author.user_id == c.rhodecode_user.user_id):
223 233 <div class="dropdown-divider"></div>
224 234 <div class="dropdown-item">
225 <a onclick="return Rhodecode.comments.editComment(this);" class="btn btn-link btn-sm edit-comment">${_('Edit')}</a>
235 <a onclick="return Rhodecode.comments.editComment(this, '${comment.line_no}', '${comment.f_path}');" class="btn btn-link btn-sm edit-comment">${_('Edit')}</a>
226 236 </div>
227 237 <div class="dropdown-item">
228 238 <a onclick="return Rhodecode.comments.deleteComment(this);" class="btn btn-link btn-sm btn-danger delete-comment">${_('Delete')}</a>
229 239 </div>
230 % if comment.draft:
240 ## Only available in EE edition
241 % if comment.draft and c.rhodecode_edition_id == 'EE':
231 242 <div class="dropdown-item">
232 243 <a onclick="return Rhodecode.comments.finalizeDrafts([${comment.comment_id}]);" class="btn btn-link btn-sm finalize-draft-comment">${_('Submit draft')}</a>
233 244 </div>
@@ -391,7 +402,7 b''
391 402
392 403 <div class="comment-area-write" style="display: block;">
393 404 <div id="edit-container">
394 <div style="padding: 40px 0">
405 <div style="padding: 20px 0px 0px 0;">
395 406 ${_('You need to be logged in to leave comments.')}
396 407 <a href="${h.route_path('login', _query={'came_from': h.current_route_path(request)})}">${_('Login now')}</a>
397 408 </div>
@@ -450,7 +461,7 b''
450 461 </div>
451 462
452 463 <div class="comment-area-write" style="display: block;">
453 <div id="edit-container_${lineno_id}">
464 <div id="edit-container_${lineno_id}" style="margin-top: -1px">
454 465 <textarea id="text_${lineno_id}" name="text" class="comment-block-ta ac-input"></textarea>
455 466 </div>
456 467 <div id="preview-container_${lineno_id}" class="clearfix" style="display: none;">
@@ -500,21 +511,16 b''
500 511 <input class="btn btn-success comment-button-input submit-comment-action" id="save_${lineno_id}" name="save" type="submit" value="${_('Add comment')}" data-is-draft=false onclick="$(this).addClass('submitter')">
501 512
502 513 % if form_type == 'inline':
514 % if c.rhodecode_edition_id == 'EE':
515 ## Disable the button for CE, the "real" validation is in the backend code anyway
503 516 <input class="btn btn-warning comment-button-input submit-draft-action" id="save_draft_${lineno_id}" name="save_draft" type="submit" value="${_('Add draft')}" data-is-draft=true onclick="$(this).addClass('submitter')">
517 % else:
518 <input class="tooltip btn btn-warning comment-button-input submit-draft-action disabled" type="submit" disabled="disabled" value="${_('Add draft')}" onclick="return false;" title="Draft comments only available in EE edition of RhodeCode">
519 % endif
504 520 % endif
505 521
506 ## inline for has a file, and line-number together with cancel hide button.
507 % if form_type == 'inline':
508 <input type="hidden" name="f_path" value="{0}">
509 <input type="hidden" name="line" value="${lineno_id}">
510 <button type="button" class="tooltip cb-comment-cancel" onclick="return Rhodecode.comments.cancelComment(this);" title="Hide comment box">
511 <i class="icon-cancel-circled2"></i>
512 </button>
513 % endif
514 </div>
515
516 522 % if review_statuses:
517 <div class="status_box">
523 <div class="comment-status-box">
518 524 <select id="change_status_${lineno_id}" name="changeset_status">
519 525 <option></option> ## Placeholder
520 526 % for status, lbl in review_statuses:
@@ -527,13 +533,25 b''
527 533 </div>
528 534 % endif
529 535
536 ## inline for has a file, and line-number together with cancel hide button.
537 % if form_type == 'inline':
538 <input type="hidden" name="f_path" value="{0}">
539 <input type="hidden" name="line" value="${lineno_id}">
540 <button type="button" class="cb-comment-cancel" onclick="return Rhodecode.comments.cancelComment(this);">
541 <i class="icon-cancel-circled2"></i>
542 </button>
543 % endif
544 </div>
545
530 546 <div class="toolbar-text">
531 547 <% renderer_url = '<a href="%s">%s</a>' % (h.route_url('%s_help' % c.visual.default_renderer), c.visual.default_renderer.upper()) %>
532 ${_('Comments parsed using {} syntax.').format(renderer_url)|n} <br/>
533 <span class="tooltip" title="${_('Use @username inside this text to send notification to this RhodeCode user')}">@mention</span>
534 ${_('and')}
535 <span class="tooltip" title="${_('Start typing with / for certain actions to be triggered via text box.')}">`/` autocomplete</span>
536 ${_('actions supported.')}
548 <p>${_('Styling with {} is supported.').format(renderer_url)|n}
549
550 <i class="icon-info-circled tooltip-hovercard"
551 data-hovercard-alt="ALT"
552 data-hovercard-url="javascript:commentHelp('${c.visual.default_renderer.upper()}')"
553 data-comment-json-b64='${h.b64(h.json.dumps({}))}'></i>
554 </p>
537 555 </div>
538 556 </div>
539 557
@@ -1,3 +1,4 b''
1 <%namespace name="base" file="/base/base.mako"/>
1 2 <%namespace name="commentblock" file="/changeset/changeset_file_comment.mako"/>
2 3
3 4 <%def name="diff_line_anchor(commit, filename, line, type)"><%
@@ -74,24 +75,9 b" return '%s_%s_%i' % (h.md5_safe(commit+f"
74 75
75 76 <div class="js-template" id="cb-comment-inline-form-template">
76 77 <div class="comment-inline-form ac">
77
78 %if c.rhodecode_user.username != h.DEFAULT_USER:
78 %if not c.rhodecode_user.is_default:
79 79 ## render template for inline comments
80 80 ${commentblock.comment_form(form_type='inline')}
81 %else:
82 ${h.form('', class_='inline-form comment-form-login', method='get')}
83 <div class="pull-left">
84 <div class="comment-help pull-right">
85 ${_('You need to be logged in to leave comments.')} <a href="${h.route_path('login', _query={'came_from': h.current_route_path(request)})}">${_('Login now')}</a>
86 </div>
87 </div>
88 <div class="comment-button pull-right">
89 <button type="button" class="cb-comment-cancel" onclick="return Rhodecode.comments.cancelComment(this);">
90 ${_('Cancel')}
91 </button>
92 </div>
93 <div class="clearfix"></div>
94 ${h.end_form()}
95 81 %endif
96 82 </div>
97 83 </div>
@@ -327,7 +313,7 b" return '%s_%s_%i' % (h.md5_safe(commit+f"
327 313 </label>
328 314
329 315 ${diff_menu(filediff, use_comments=use_comments)}
330 <table data-f-path="${filediff.patch['filename']}" data-anchor-id="${h.FID(filediff.raw_id, filediff.patch['filename'])}" class="code-visible-block cb cb-diff-${c.user_session_attrs["diffmode"]} code-highlight ${(over_lines_changed_limit and 'cb-collapsed' or '')}">
316 <table id="file-${h.safeid(h.safe_unicode(filediff.patch['filename']))}" data-f-path="${filediff.patch['filename']}" data-anchor-id="${h.FID(filediff.raw_id, filediff.patch['filename'])}" class="code-visible-block cb cb-diff-${c.user_session_attrs["diffmode"]} code-highlight ${(over_lines_changed_limit and 'cb-collapsed' or '')}">
331 317
332 318 ## new/deleted/empty content case
333 319 % if not filediff.hunks:
@@ -626,8 +612,10 b" return '%s_%s_%i' % (h.md5_safe(commit+f"
626 612
627 613 % if use_comments:
628 614 |
629 <a href="#" onclick="return Rhodecode.comments.toggleComments(this);">
630 <span class="show-comment-button">${_('Show comments')}</span><span class="hide-comment-button">${_('Hide comments')}</span>
615 <a href="#" onclick="Rhodecode.comments.toggleDiffComments(this);return toggleElement(this)"
616 data-toggle-on="${_('Hide comments')}"
617 data-toggle-off="${_('Show comments')}">
618 <span class="hide-comment-button">${_('Hide comments')}</span>
631 619 </a>
632 620 % endif
633 621
@@ -637,23 +625,36 b" return '%s_%s_%i' % (h.md5_safe(commit+f"
637 625 </%def>
638 626
639 627
640 <%def name="inline_comments_container(comments, active_pattern_entries=None)">
628 <%def name="inline_comments_container(comments, active_pattern_entries=None, line_no='', f_path='')">
641 629
642 630 <div class="inline-comments">
643 631 %for comment in comments:
644 632 ${commentblock.comment_block(comment, inline=True, active_pattern_entries=active_pattern_entries)}
645 633 %endfor
646 % if comments and comments[-1].outdated:
647 <span class="btn btn-secondary cb-comment-add-button comment-outdated}" style="display: none;}">
648 ${_('Add another comment')}
649 </span>
650 % else:
651 <span onclick="return Rhodecode.comments.createComment(this)" class="btn btn-secondary cb-comment-add-button">
652 ${_('Add another comment')}
653 </span>
654 % endif
634
635 <%
636 extra_class = ''
637 extra_style = ''
638
639 if comments and comments[-1].outdated:
640 extra_class = ' comment-outdated'
641 extra_style = 'display: none;'
655 642
643 %>
644 <div class="reply-thread-container-wrapper${extra_class}" style="${extra_style}">
645 <div class="reply-thread-container${extra_class}">
646 <div class="reply-thread-gravatar">
647 ${base.gravatar(c.rhodecode_user.email, 20, tooltip=True, user=c.rhodecode_user)}
656 648 </div>
649 <div class="reply-thread-reply-button">
650 ## initial reply button, some JS logic can append here a FORM to leave a first comment.
651 <button class="cb-comment-add-button" onclick="return Rhodecode.comments.createComment(this, '${f_path}', '${line_no}', null)">Reply...</button>
652 </div>
653 <div class="reply-thread-last"></div>
654 </div>
655 </div>
656 </div>
657
657 658 </%def>
658 659
659 660 <%!
@@ -721,9 +722,9 b' def get_comments_for(diff_type, comments'
721 722 %endif
722 723 %if line_old_comments_no_drafts:
723 724 % if has_outdated:
724 <i class="tooltip icon-comment-toggle" title="${_('comments including outdated: {}. Click here to display them.').format(len(line_old_comments_no_drafts))}" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
725 <i class="tooltip toggle-comment-action icon-comment-toggle" title="${_('Comments including outdated: {}. Click here to toggle them.').format(len(line_old_comments_no_drafts))}" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
725 726 % else:
726 <i class="tooltip icon-comment" title="${_('comments: {}. Click to toggle them.').format(len(line_old_comments_no_drafts))}" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
727 <i class="tooltip toggle-comment-action icon-comment" title="${_('Comments: {}. Click to toggle them.').format(len(line_old_comments_no_drafts))}" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
727 728 % endif
728 729 %endif
729 730 </td>
@@ -737,16 +738,18 b' def get_comments_for(diff_type, comments'
737 738 <a name="${old_line_anchor}" href="#${old_line_anchor}">${line.original.lineno}</a>
738 739 %endif
739 740 </td>
741
742 <% line_no = 'o{}'.format(line.original.lineno) %>
740 743 <td class="cb-content ${action_class(line.original.action)}"
741 data-line-no="o${line.original.lineno}"
744 data-line-no="${line_no}"
742 745 >
743 746 %if use_comments and line.original.lineno:
744 ${render_add_comment_button()}
747 ${render_add_comment_button(line_no=line_no, f_path=filediff.patch['filename'])}
745 748 %endif
746 749 <span class="cb-code"><span class="cb-action ${action_class(line.original.action)}"></span>${line.original.content or '' | n}</span>
747 750
748 751 %if use_comments and line.original.lineno and line_old_comments:
749 ${inline_comments_container(line_old_comments, active_pattern_entries=active_pattern_entries)}
752 ${inline_comments_container(line_old_comments, active_pattern_entries=active_pattern_entries, line_no=line_no, f_path=filediff.patch['filename'])}
750 753 %endif
751 754
752 755 </td>
@@ -766,9 +769,9 b' def get_comments_for(diff_type, comments'
766 769
767 770 %if line_new_comments_no_drafts:
768 771 % if has_outdated:
769 <i class="tooltip icon-comment-toggle" title="${_('comments including outdated: {}. Click here to display them.').format(len(line_new_comments_no_drafts))}" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
772 <i class="tooltip toggle-comment-action icon-comment-toggle" title="${_('Comments including outdated: {}. Click here to toggle them.').format(len(line_new_comments_no_drafts))}" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
770 773 % else:
771 <i class="tooltip icon-comment" title="${_('comments: {}. Click to toggle them.').format(len(line_new_comments_no_drafts))}" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
774 <i class="tooltip toggle-comment-action icon-comment" title="${_('Comments: {}. Click to toggle them.').format(len(line_new_comments_no_drafts))}" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
772 775 % endif
773 776 %endif
774 777 </div>
@@ -783,22 +786,25 b' def get_comments_for(diff_type, comments'
783 786 <a name="${new_line_anchor}" href="#${new_line_anchor}">${line.modified.lineno}</a>
784 787 %endif
785 788 </td>
789
790 <% line_no = 'n{}'.format(line.modified.lineno) %>
786 791 <td class="cb-content ${action_class(line.modified.action)}"
787 data-line-no="n${line.modified.lineno}"
792 data-line-no="${line_no}"
788 793 >
789 794 %if use_comments and line.modified.lineno:
790 ${render_add_comment_button()}
795 ${render_add_comment_button(line_no=line_no, f_path=filediff.patch['filename'])}
791 796 %endif
792 797 <span class="cb-code"><span class="cb-action ${action_class(line.modified.action)}"></span>${line.modified.content or '' | n}</span>
793 %if use_comments and line.modified.lineno and line_new_comments:
794 ${inline_comments_container(line_new_comments, active_pattern_entries=active_pattern_entries)}
795 %endif
796 798 % if line_action in ['+', '-'] and prev_line_action not in ['+', '-']:
797 799 <div class="nav-chunk" style="visibility: hidden">
798 800 <i class="icon-eye" title="viewing diff hunk-${hunk.index}-${chunk_count}"></i>
799 801 </div>
800 802 <% chunk_count +=1 %>
801 803 % endif
804 %if use_comments and line.modified.lineno and line_new_comments:
805 ${inline_comments_container(line_new_comments, active_pattern_entries=active_pattern_entries, line_no=line_no, f_path=filediff.patch['filename'])}
806 %endif
807
802 808 </td>
803 809 </tr>
804 810 %endfor
@@ -830,9 +836,9 b' def get_comments_for(diff_type, comments'
830 836
831 837 % if comments_no_drafts:
832 838 % if has_outdated:
833 <i class="tooltip icon-comment-toggle" title="${_('comments including outdated: {}. Click here to display them.').format(len(comments_no_drafts))}" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
839 <i class="tooltip toggle-comment-action icon-comment-toggle" title="${_('Comments including outdated: {}. Click here to toggle them.').format(len(comments_no_drafts))}" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
834 840 % else:
835 <i class="tooltip icon-comment" title="${_('comments: {}. Click to toggle them.').format(len(comments_no_drafts))}" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
841 <i class="tooltip toggle-comment-action icon-comment" title="${_('Comments: {}. Click to toggle them.').format(len(comments_no_drafts))}" onclick="return Rhodecode.comments.toggleLineComments(this)"></i>
836 842 % endif
837 843 % endif
838 844 </div>
@@ -857,15 +863,16 b' def get_comments_for(diff_type, comments'
857 863 <a name="${new_line_anchor}" href="#${new_line_anchor}">${new_line_no}</a>
858 864 %endif
859 865 </td>
866 <% line_no = '{}{}'.format(new_line_no and 'n' or 'o', new_line_no or old_line_no) %>
860 867 <td class="cb-content ${action_class(action)}"
861 data-line-no="${(new_line_no and 'n' or 'o')}${(new_line_no or old_line_no)}"
868 data-line-no="${line_no}"
862 869 >
863 870 %if use_comments:
864 ${render_add_comment_button()}
871 ${render_add_comment_button(line_no=line_no, f_path=filediff.patch['filename'])}
865 872 %endif
866 873 <span class="cb-code"><span class="cb-action ${action_class(action)}"></span> ${content or '' | n}</span>
867 874 %if use_comments and comments:
868 ${inline_comments_container(comments, active_pattern_entries=active_pattern_entries)}
875 ${inline_comments_container(comments, active_pattern_entries=active_pattern_entries, line_no=line_no, f_path=filediff.patch['filename'])}
869 876 %endif
870 877 </td>
871 878 </tr>
@@ -886,10 +893,12 b' def get_comments_for(diff_type, comments'
886 893 </%def>file changes
887 894
888 895
889 <%def name="render_add_comment_button()">
890 <button class="btn btn-small btn-primary cb-comment-box-opener" onclick="return Rhodecode.comments.createComment(this)">
896 <%def name="render_add_comment_button(line_no='', f_path='')">
897 % if not c.rhodecode_user.is_default:
898 <button class="btn btn-small btn-primary cb-comment-box-opener" onclick="return Rhodecode.comments.createComment(this, '${f_path}', '${line_no}', null)">
891 899 <span><i class="icon-comment"></i></span>
892 900 </button>
901 % endif
893 902 </%def>
894 903
895 904 <%def name="render_diffset_menu(diffset, range_diff_on=None, commit=None, pull_request_menu=None)">
@@ -456,7 +456,7 b''
456 456 </div>
457 457
458 458 <div class="markup-form-area-write" style="display: block;">
459 <div id="edit-container_${form_id}">
459 <div id="edit-container_${form_id}" style="margin-top: -1px">
460 460 <textarea id="${form_id}" name="${form_id}" class="comment-block-ta ac-input">${form_text if form_text else ''}</textarea>
461 461 </div>
462 462 <div id="preview-container_${form_id}" class="clearfix" style="display: none;">
@@ -237,6 +237,24 b' if (show_disabled) {'
237 237
238 238 </script>
239 239
240 <script id="ejs_commentHelpHovercard" type="text/template" class="ejsTemplate">
241
242 <div>
243 Use <strong>@username</strong> mention syntax to send direct notification to this RhodeCode user.<br/>
244 Typing / starts autocomplete for certain action, e.g set review status, or comment type. <br/>
245 <br/>
246 Use <strong>Cmd/ctrl+enter</strong> to submit comment, or <strong>Shift+Cmd/ctrl+enter</strong> to submit a draft.<br/>
247 <br/>
248 <strong>Draft comments</strong> are private to the author, and trigger no notification to others.<br/>
249 They are permanent until deleted, or converted to regular comments.<br/>
250 <br/>
251 <br/>
252 </div>
253
254 </script>
255
256
257
240 258 ##// END OF EJS Templates
241 259 </div>
242 260
@@ -846,6 +846,7 b' versionController.init();'
846 846
847 847 reviewersController = new ReviewersController();
848 848 commitsController = new CommitsController();
849 commentsController = new CommentsController();
849 850
850 851 updateController = new UpdatePrController();
851 852
@@ -1002,6 +1003,8 b' window.setObserversData = ${c.pull_reque'
1002 1003 alert('okok !' + commentIds)
1003 1004
1004 1005 }
1006 // register globally so inject comment logic can re-use it.
1007 window.commentsController = commentsController;
1005 1008
1006 1009 })
1007 1010 </script>
General Comments 0
You need to be logged in to leave comments. Login now