##// 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 var comment = $('#comment-'+commentId);
252 var comment = $('#comment-'+commentId);
253 var commentData = comment.data();
253 var commentData = comment.data();
254 if (commentData.commentInline) {
254 if (commentData.commentInline) {
255 this.createComment(comment, commentId)
255 this.createComment(comment, f_path, line_no, commentId)
256 } else {
256 } else {
257 Rhodecode.comments.createGeneralComment('general', "$placeholder", commentId)
257 Rhodecode.comments.createGeneralComment('general', "$placeholder", commentId)
258 }
258 }
@@ -383,6 +383,9 b' class RepoCommitsView(RepoAppView):'
383 text = self.request.POST.get('text')
383 text = self.request.POST.get('text')
384 comment_type = self.request.POST.get('comment_type')
384 comment_type = self.request.POST.get('comment_type')
385 resolves_comment_id = self.request.POST.get('resolves_comment_id', None)
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 if status:
390 if status:
388 text = text or (_('Status change %(transition_icon)s %(status)s')
391 text = text or (_('Status change %(transition_icon)s %(status)s')
@@ -404,8 +407,8 b' class RepoCommitsView(RepoAppView):'
404 repo=self.db_repo.repo_id,
407 repo=self.db_repo.repo_id,
405 user=self._rhodecode_db_user.user_id,
408 user=self._rhodecode_db_user.user_id,
406 commit_id=current_id,
409 commit_id=current_id,
407 f_path=self.request.POST.get('f_path'),
410 f_path=f_path,
408 line_no=self.request.POST.get('line'),
411 line_no=line_no,
409 status_change=(ChangesetStatus.get_status_lbl(status)
412 status_change=(ChangesetStatus.get_status_lbl(status)
410 if status else None),
413 if status else None),
411 status_change_type=status,
414 status_change_type=status,
@@ -447,19 +450,20 b' class RepoCommitsView(RepoAppView):'
447 # finalize, commit and redirect
450 # finalize, commit and redirect
448 Session().commit()
451 Session().commit()
449
452
450 data = {
453 data = {}
451 'target_id': h.safeid(h.safe_unicode(
452 self.request.POST.get('f_path'))),
453 }
454 if comment:
454 if comment:
455 comment_id = comment.comment_id
456 data[comment_id] = {
457 'target_id': target_elem_id
458 }
455 c.co = comment
459 c.co = comment
456 c.at_version_num = 0
460 c.at_version_num = 0
457 rendered_comment = render(
461 rendered_comment = render(
458 'rhodecode:templates/changeset/changeset_comment_block.mako',
462 'rhodecode:templates/changeset/changeset_comment_block.mako',
459 self._get_template_context(c), self.request)
463 self._get_template_context(c), self.request)
460
464
461 data.update(comment.get_dict())
465 data[comment_id].update(comment.get_dict())
462 data.update({'rendered_text': rendered_comment})
466 data[comment_id].update({'rendered_text': rendered_comment})
463
467
464 comment_broadcast_channel = channelstream.comment_channel(
468 comment_broadcast_channel = channelstream.comment_channel(
465 self.db_repo_name, commit_obj=commit)
469 self.db_repo_name, commit_obj=commit)
@@ -983,8 +983,9 b' class RepoPullRequestsView(RepoAppView, '
983 }
983 }
984 return data
984 return data
985
985
986 def _get_existing_ids(self, post_data):
986 @classmethod
987 return filter(lambda e: e, map(safe_int, aslist(post_data.get('comments'), ',')))
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 @LoginRequired()
990 @LoginRequired()
990 @NotAnonymous()
991 @NotAnonymous()
@@ -1022,7 +1023,7 b' class RepoPullRequestsView(RepoAppView, '
1022 self.register_comments_vars(c, pull_request_latest, versions, include_drafts=False)
1023 self.register_comments_vars(c, pull_request_latest, versions, include_drafts=False)
1023 all_comments = c.inline_comments_flat + c.comments
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 return _render('comments_table', all_comments, len(all_comments),
1027 return _render('comments_table', all_comments, len(all_comments),
1027 existing_ids=existing_ids)
1028 existing_ids=existing_ids)
1028
1029
@@ -1064,7 +1065,7 b' class RepoPullRequestsView(RepoAppView, '
1064 .get_pull_request_resolved_todos(pull_request, include_drafts=False)
1065 .get_pull_request_resolved_todos(pull_request, include_drafts=False)
1065
1066
1066 all_comments = c.unresolved_comments + c.resolved_comments
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 return _render('comments_table', all_comments, len(c.unresolved_comments),
1069 return _render('comments_table', all_comments, len(c.unresolved_comments),
1069 todo_comments=True, existing_ids=existing_ids)
1070 todo_comments=True, existing_ids=existing_ids)
1070
1071
@@ -1518,6 +1519,150 b' class RepoPullRequestsView(RepoAppView, '
1518 self._rhodecode_user)
1519 self._rhodecode_user)
1519 raise HTTPNotFound()
1520 raise HTTPNotFound()
1520
1521
1522 def _pull_request_comments_create(self, pull_request, comments):
1523 _ = self.request.translate
1524 data = {}
1525 pull_request_id = pull_request.pull_request_id
1526 if not comments:
1527 return
1528
1529 all_drafts = len([x for x in comments if str2bool(x['is_draft'])]) == len(comments)
1530
1531 for entry in comments:
1532 c = self.load_default_context()
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)))
1542
1543 # the logic here should work like following, if we submit close
1544 # pr comment, use `close_pull_request_with_comment` function
1545 # else handle regular comment logic
1546
1547 if close_pull_request:
1548 # only owner or admin or person with write permissions
1549 allowed_to_close = PullRequestModel().check_user_update(
1550 pull_request, self._rhodecode_user)
1551 if not allowed_to_close:
1552 log.debug('comment: forbidden because not allowed to close '
1553 'pull request %s', pull_request_id)
1554 raise HTTPForbidden()
1555
1556 # This also triggers `review_status_change`
1557 comment, status = PullRequestModel().close_pull_request_with_comment(
1558 pull_request, self._rhodecode_user, self.db_repo, message=text,
1559 auth_user=self._rhodecode_user)
1560 Session().flush()
1561 is_inline = comment.is_inline
1562
1563 PullRequestModel().trigger_pull_request_hook(
1564 pull_request, self._rhodecode_user, 'comment',
1565 data={'comment': comment})
1566
1567 else:
1568 # regular comment case, could be inline, or one with status.
1569 # for that one we check also permissions
1570 # Additionally ENSURE if somehow draft is sent we're then unable to change status
1571 allowed_to_change_status = PullRequestModel().check_user_change_status(
1572 pull_request, self._rhodecode_user) and not is_draft
1573
1574 if status and allowed_to_change_status:
1575 message = (_('Status change %(transition_icon)s %(status)s')
1576 % {'transition_icon': '>',
1577 'status': ChangesetStatus.get_status_lbl(status)})
1578 text = text or message
1579
1580 comment = CommentsModel().create(
1581 text=text,
1582 repo=self.db_repo.repo_id,
1583 user=self._rhodecode_user.user_id,
1584 pull_request=pull_request,
1585 f_path=f_path,
1586 line_no=line_no,
1587 status_change=(ChangesetStatus.get_status_lbl(status)
1588 if status and allowed_to_change_status else None),
1589 status_change_type=(status
1590 if status and allowed_to_change_status else None),
1591 comment_type=comment_type,
1592 is_draft=is_draft,
1593 resolves_comment_id=resolves_comment_id,
1594 auth_user=self._rhodecode_user,
1595 send_email=not is_draft, # skip notification for draft comments
1596 )
1597 is_inline = comment.is_inline
1598
1599 if allowed_to_change_status:
1600 # calculate old status before we change it
1601 old_calculated_status = pull_request.calculated_review_status()
1602
1603 # get status if set !
1604 if status:
1605 ChangesetStatusModel().set_status(
1606 self.db_repo.repo_id,
1607 status,
1608 self._rhodecode_user.user_id,
1609 comment,
1610 pull_request=pull_request
1611 )
1612
1613 Session().flush()
1614 # this is somehow required to get access to some relationship
1615 # loaded on comment
1616 Session().refresh(comment)
1617
1618 PullRequestModel().trigger_pull_request_hook(
1619 pull_request, self._rhodecode_user, 'comment',
1620 data={'comment': comment})
1621
1622 # we now calculate the status of pull request, and based on that
1623 # calculation we set the commits status
1624 calculated_status = pull_request.calculated_review_status()
1625 if old_calculated_status != calculated_status:
1626 PullRequestModel().trigger_pull_request_hook(
1627 pull_request, self._rhodecode_user, 'review_status_change',
1628 data={'status': calculated_status})
1629
1630 comment_id = comment.comment_id
1631 data[comment_id] = {
1632 'target_id': target_elem_id
1633 }
1634 Session().flush()
1635
1636 c.co = comment
1637 c.at_version_num = None
1638 c.is_new = True
1639 rendered_comment = render(
1640 'rhodecode:templates/changeset/changeset_comment_block.mako',
1641 self._get_template_context(c), self.request)
1642
1643 data[comment_id].update(comment.get_dict())
1644 data[comment_id].update({'rendered_text': rendered_comment})
1645
1646 Session().commit()
1647
1648 # skip channelstream for draft comments
1649 if all_drafts:
1650 comment_broadcast_channel = channelstream.comment_channel(
1651 self.db_repo_name, pull_request_obj=pull_request)
1652
1653 comment_data = data
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
1660 channelstream.comment_channelstream_push(
1661 self.request, comment_broadcast_channel, self._rhodecode_user, msg,
1662 comment_data=comment_data)
1663
1664 return data
1665
1521 @LoginRequired()
1666 @LoginRequired()
1522 @NotAnonymous()
1667 @NotAnonymous()
1523 @HasRepoPermissionAnyDecorator(
1668 @HasRepoPermissionAnyDecorator(
@@ -1529,9 +1674,7 b' class RepoPullRequestsView(RepoAppView, '
1529 def pull_request_comment_create(self):
1674 def pull_request_comment_create(self):
1530 _ = self.request.translate
1675 _ = self.request.translate
1531
1676
1532 pull_request = PullRequest.get_or_404(
1677 pull_request = PullRequest.get_or_404(self.request.matchdict['pull_request_id'])
1533 self.request.matchdict['pull_request_id'])
1534 pull_request_id = pull_request.pull_request_id
1535
1678
1536 if pull_request.is_closed():
1679 if pull_request.is_closed():
1537 log.debug('comment: forbidden because pull request is closed')
1680 log.debug('comment: forbidden because pull request is closed')
@@ -1543,130 +1686,17 b' class RepoPullRequestsView(RepoAppView, '
1543 log.debug('comment: forbidden because pull request is from forbidden repo')
1686 log.debug('comment: forbidden because pull request is from forbidden repo')
1544 raise HTTPForbidden()
1687 raise HTTPForbidden()
1545
1688
1546 c = self.load_default_context()
1689 comment_data = {
1547
1690 'comment_type': self.request.POST.get('comment_type'),
1548 status = self.request.POST.get('changeset_status', None)
1691 'text': self.request.POST.get('text'),
1549 text = self.request.POST.get('text')
1692 'status': self.request.POST.get('changeset_status', None),
1550 comment_type = self.request.POST.get('comment_type')
1693 'is_draft': self.request.POST.get('draft'),
1551 is_draft = str2bool(self.request.POST.get('draft'))
1694 'resolves_comment_id': self.request.POST.get('resolves_comment_id', None),
1552 resolves_comment_id = self.request.POST.get('resolves_comment_id', None)
1695 'close_pull_request': self.request.POST.get('close_pull_request'),
1553 close_pull_request = self.request.POST.get('close_pull_request')
1696 'f_path': self.request.POST.get('f_path'),
1554
1697 'line': self.request.POST.get('line'),
1555 # the logic here should work like following, if we submit close
1556 # pr comment, use `close_pull_request_with_comment` function
1557 # else handle regular comment logic
1558
1559 if close_pull_request:
1560 # only owner or admin or person with write permissions
1561 allowed_to_close = PullRequestModel().check_user_update(
1562 pull_request, self._rhodecode_user)
1563 if not allowed_to_close:
1564 log.debug('comment: forbidden because not allowed to close '
1565 'pull request %s', pull_request_id)
1566 raise HTTPForbidden()
1567
1568 # This also triggers `review_status_change`
1569 comment, status = PullRequestModel().close_pull_request_with_comment(
1570 pull_request, self._rhodecode_user, self.db_repo, message=text,
1571 auth_user=self._rhodecode_user)
1572 Session().flush()
1573 is_inline = comment.is_inline
1574
1575 PullRequestModel().trigger_pull_request_hook(
1576 pull_request, self._rhodecode_user, 'comment',
1577 data={'comment': comment})
1578
1579 else:
1580 # regular comment case, could be inline, or one with status.
1581 # for that one we check also permissions
1582 # Additionally ENSURE if somehow draft is sent we're then unable to change status
1583 allowed_to_change_status = PullRequestModel().check_user_change_status(
1584 pull_request, self._rhodecode_user) and not is_draft
1585
1586 if status and allowed_to_change_status:
1587 message = (_('Status change %(transition_icon)s %(status)s')
1588 % {'transition_icon': '>',
1589 'status': ChangesetStatus.get_status_lbl(status)})
1590 text = text or message
1591
1592 comment = CommentsModel().create(
1593 text=text,
1594 repo=self.db_repo.repo_id,
1595 user=self._rhodecode_user.user_id,
1596 pull_request=pull_request,
1597 f_path=self.request.POST.get('f_path'),
1598 line_no=self.request.POST.get('line'),
1599 status_change=(ChangesetStatus.get_status_lbl(status)
1600 if status and allowed_to_change_status else None),
1601 status_change_type=(status
1602 if status and allowed_to_change_status else None),
1603 comment_type=comment_type,
1604 is_draft=is_draft,
1605 resolves_comment_id=resolves_comment_id,
1606 auth_user=self._rhodecode_user,
1607 send_email=not is_draft, # skip notification for draft comments
1608 )
1609 is_inline = comment.is_inline
1610
1611 if allowed_to_change_status:
1612 # calculate old status before we change it
1613 old_calculated_status = pull_request.calculated_review_status()
1614
1615 # get status if set !
1616 if status:
1617 ChangesetStatusModel().set_status(
1618 self.db_repo.repo_id,
1619 status,
1620 self._rhodecode_user.user_id,
1621 comment,
1622 pull_request=pull_request
1623 )
1624
1625 Session().flush()
1626 # this is somehow required to get access to some relationship
1627 # loaded on comment
1628 Session().refresh(comment)
1629
1630 PullRequestModel().trigger_pull_request_hook(
1631 pull_request, self._rhodecode_user, 'comment',
1632 data={'comment': comment})
1633
1634 # we now calculate the status of pull request, and based on that
1635 # calculation we set the commits status
1636 calculated_status = pull_request.calculated_review_status()
1637 if old_calculated_status != calculated_status:
1638 PullRequestModel().trigger_pull_request_hook(
1639 pull_request, self._rhodecode_user, 'review_status_change',
1640 data={'status': calculated_status})
1641
1642 Session().commit()
1643
1644 data = {
1645 'target_id': h.safeid(h.safe_unicode(
1646 self.request.POST.get('f_path'))),
1647 }
1698 }
1648
1699 data = self._pull_request_comments_create(pull_request, [comment_data])
1649 if comment:
1650 c.co = comment
1651 c.at_version_num = None
1652 rendered_comment = render(
1653 'rhodecode:templates/changeset/changeset_comment_block.mako',
1654 self._get_template_context(c), self.request)
1655
1656 data.update(comment.get_dict())
1657 data.update({'rendered_text': rendered_comment})
1658
1659 # skip channelstream for draft comments
1660 if not is_draft:
1661 comment_broadcast_channel = channelstream.comment_channel(
1662 self.db_repo_name, pull_request_obj=pull_request)
1663
1664 comment_data = data
1665 comment_type = 'inline' if is_inline else 'general'
1666 channelstream.comment_channelstream_push(
1667 self.request, comment_broadcast_channel, self._rhodecode_user,
1668 _('posted a new {} comment').format(comment_type),
1669 comment_data=comment_data)
1670
1700
1671 return data
1701 return data
1672
1702
@@ -339,13 +339,12 b' def comment_channelstream_push(request, '
339
339
340 comment_data = kwargs.pop('comment_data', {})
340 comment_data = kwargs.pop('comment_data', {})
341 user_data = kwargs.pop('user_data', {})
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 user.username,
345 user.username,
346 msg,
346 msg,
347 comment_id,
347 comment_id,
348 _reload_link(_('Reload page to see new comments')),
349 )
348 )
350
349
351 message_obj = {
350 message_obj = {
@@ -1148,7 +1148,7 b' class DiffLimitExceeded(Exception):'
1148
1148
1149 # NOTE(marcink): if diffs.mako change, probably this
1149 # NOTE(marcink): if diffs.mako change, probably this
1150 # needs a bump to next version
1150 # needs a bump to next version
1151 CURRENT_DIFF_VERSION = 'v4'
1151 CURRENT_DIFF_VERSION = 'v5'
1152
1152
1153
1153
1154 def _cleanup_cache_file(cached_diff_file):
1154 def _cleanup_cache_file(cached_diff_file):
@@ -370,7 +370,7 b' input[type="button"] {'
370 color: @alert2;
370 color: @alert2;
371
371
372 &:hover {
372 &:hover {
373 color: darken(@alert2,30%);
373 color: darken(@alert2, 30%);
374 }
374 }
375
375
376 &:disabled {
376 &:disabled {
@@ -1002,7 +1002,7 b' input.filediff-collapse-state {'
1002 .nav-chunk {
1002 .nav-chunk {
1003 position: absolute;
1003 position: absolute;
1004 right: 20px;
1004 right: 20px;
1005 margin-top: -17px;
1005 margin-top: -15px;
1006 }
1006 }
1007
1007
1008 .nav-chunk.selected {
1008 .nav-chunk.selected {
@@ -4,7 +4,7 b''
4
4
5
5
6 // Comments
6 // Comments
7 @comment-outdated-opacity: 0.6;
7 @comment-outdated-opacity: 1.0;
8
8
9 .comments {
9 .comments {
10 width: 100%;
10 width: 100%;
@@ -64,32 +64,34 b' tr.inline-comments div {'
64 .comment-draft {
64 .comment-draft {
65 float: left;
65 float: left;
66 margin-right: 10px;
66 margin-right: 10px;
67 font-weight: 600;
67 font-weight: 400;
68 color: @alert3;
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 .comment-label {
78 .comment-label {
72 float: left;
79 float: left;
73
80
74 padding: 0.4em 0.4em;
81 padding: 0 8px 0 0;
75 margin: 2px 4px 0px 0px;
76 display: inline-block;
77 min-height: 0;
82 min-height: 0;
78
83
79 text-align: center;
84 text-align: center;
80 font-size: 10px;
85 font-size: 10px;
81 line-height: .8em;
82
86
83 font-family: @text-italic;
87 font-family: @text-italic;
84 font-style: italic;
88 font-style: italic;
85 background: #fff none;
89 background: #fff none;
86 color: @grey3;
90 color: @grey3;
87 border: 1px solid @grey4;
88 white-space: nowrap;
91 white-space: nowrap;
89
92
90 text-transform: uppercase;
93 text-transform: uppercase;
91 min-width: 50px;
94 min-width: 50px;
92 border-radius: 4px;
93
95
94 &.todo {
96 &.todo {
95 color: @color5;
97 color: @color5;
@@ -277,64 +279,165 b' tr.inline-comments div {'
277 .comment-outdated {
279 .comment-outdated {
278 opacity: @comment-outdated-opacity;
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 .inline-comments {
289 .inline-comments {
283 border-radius: @border-radius;
290
284 .comment {
291 .comment {
285 margin: 0;
292 margin: 0;
286 border-radius: @border-radius;
287 }
293 }
294
288 .comment-outdated {
295 .comment-outdated {
289 opacity: @comment-outdated-opacity;
296 opacity: @comment-outdated-opacity;
290 }
297 }
291
298
299 .comment-outdated-label {
300 color: @grey3;
301 padding-right: 4px;
302 }
303
292 .comment-inline {
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 background: white;
324 background: white;
294 padding: @comment-padding @comment-padding;
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 .text {
332 .text {
298 border: none;
333 border: none;
299 }
334 }
335
300 .meta {
336 .meta {
301 border-bottom: 1px solid @grey6;
337 border-bottom: 1px solid @grey6;
302 margin: -5px 0px;
338 margin: -5px 0px;
303 line-height: 24px;
339 line-height: 24px;
304 }
340 }
341
305 }
342 }
306 .comment-selected {
343 .comment-selected {
307 border-left: 6px solid @comment-highlight-color;
344 border-left: 6px solid @comment-highlight-color;
308 }
345 }
346
347 .comment-inline-form-open {
348 display: block !important;
349 }
350
309 .comment-inline-form {
351 .comment-inline-form {
310 padding: @comment-padding;
311 display: none;
352 display: none;
312 }
353 }
313 .cb-comment-add-button {
354
314 margin: @comment-padding;
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 }
316 /* hide add comment button when form is open */
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
424 /* hide add comment button when form is open */
317 .comment-inline-form-open ~ .cb-comment-add-button {
425 .comment-inline-form-open ~ .cb-comment-add-button {
318 display: none;
426 display: none;
319 }
427 }
320 .comment-inline-form-open {
428
321 display: block;
322 }
323 /* hide add comment button when form but no comments */
324 .comment-inline-form:first-child + .cb-comment-add-button {
325 display: none;
326 }
327 /* hide add comment button when no comments or form */
328 .cb-comment-add-button:first-child {
329 display: none;
330 }
331 /* hide add comment button when only comment is being deleted */
429 /* hide add comment button when only comment is being deleted */
332 .comment-deleting:first-child + .cb-comment-add-button {
430 .comment-deleting:first-child + .cb-comment-add-button {
333 display: none;
431 display: none;
334 }
432 }
433
434 /* hide add comment button when form but no comments */
435 .comment-inline-form:first-child + .cb-comment-add-button {
436 display: none;
437 }
438
335 }
439 }
336
440
337
338 .show-outdated-comments {
441 .show-outdated-comments {
339 display: inline;
442 display: inline;
340 color: @rcblue;
443 color: @rcblue;
@@ -387,23 +490,40 b' form.comment-form {'
387 }
490 }
388
491
389 .comment-footer {
492 .comment-footer {
390 position: relative;
493 display: table;
391 width: 100%;
494 width: 100%;
392 min-height: 42px;
495 height: 42px;
393
496
394 .status_box,
497 .comment-status-box,
395 .cancel-button {
498 .cancel-button {
396 float: left;
397 display: inline-block;
499 display: inline-block;
398 }
500 }
399
501
400 .status_box {
502 .comment-status-box {
401 margin-left: 10px;
503 margin-left: 10px;
402 }
504 }
403
505
404 .action-buttons {
506 .action-buttons {
405 float: left;
507 display: table-cell;
406 display: inline-block;
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 .action-buttons-extra {
529 .action-buttons-extra {
@@ -434,10 +554,6 b' form.comment-form {'
434 margin-right: 0;
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 .injected_diff .comment-inline-form,
605 .injected_diff .comment-inline-form,
490 .comment-inline-form {
606 .comment-inline-form {
491 background-color: white;
607 background-color: white;
492 margin-top: 10px;
608 margin-top: 4px;
493 margin-bottom: 20px;
609 margin-bottom: 10px;
494 }
610 }
495
611
496 .inline-form {
612 .inline-form {
@@ -526,9 +642,6 b' form.comment-form {'
526 margin: 0px;
642 margin: 0px;
527 }
643 }
528
644
529 .comment-inline-form .comment-footer {
530 margin: 10px 0px 0px 0px;
531 }
532
645
533 .hide-inline-form-button {
646 .hide-inline-form-button {
534 margin-left: 5px;
647 margin-left: 5px;
@@ -554,6 +667,7 b' comment-area-text {'
554
667
555 .comment-area-header {
668 .comment-area-header {
556 height: 35px;
669 height: 35px;
670 border-bottom: 1px solid @grey5;
557 }
671 }
558
672
559 .comment-area-header .nav-links {
673 .comment-area-header .nav-links {
@@ -561,6 +675,7 b' comment-area-text {'
561 flex-flow: row wrap;
675 flex-flow: row wrap;
562 -webkit-flex-flow: row wrap;
676 -webkit-flex-flow: row wrap;
563 width: 100%;
677 width: 100%;
678 border: none;
564 }
679 }
565
680
566 .comment-area-footer {
681 .comment-area-footer {
@@ -629,14 +744,3 b' comment-area-text {'
629 border-bottom: 2px solid transparent;
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 .sidebar-element {
3213 .sidebar-element {
3214 margin-top: 20px;
3214 margin-top: 20px;
3215 }
3215
3216 .icon-draft {
3217 color: @color-draft
3218 }
3219 }
3220
3216
3221
3217 .right-sidebar-collapsed-state {
3222 .right-sidebar-collapsed-state {
3218 display: flex;
3223 display: flex;
@@ -3235,5 +3240,4 b' details:not([open]) > :not(summary) {'
3235
3240
3236 .old-comments-marker td {
3241 .old-comments-marker td {
3237 padding-top: 15px;
3242 padding-top: 15px;
3238 border-bottom: 1px solid @grey5;
3243 }
3239 }
@@ -47,6 +47,8 b''
47
47
48 // Highlight color for lines and colors
48 // Highlight color for lines and colors
49 @comment-highlight-color: #ffd887;
49 @comment-highlight-color: #ffd887;
50 @color-draft: darken(@alert3, 30%);
51 @color-new: darken(@alert1, 5%);
50
52
51 // FONTS
53 // FONTS
52 @basefontsize: 13px;
54 @basefontsize: 13px;
@@ -71,14 +71,20 b' export class RhodecodeApp extends Polyme'
71 if (elem) {
71 if (elem) {
72 elem.handleNotification(data);
72 elem.handleNotification(data);
73 }
73 }
74
75 }
74 }
76
75
77 handleComment(data) {
76 handleComment(data) {
78 if (data.message.comment_id) {
77
78 if (data.message.comment_data.length !== 0) {
79 if (window.refreshAllComments !== undefined) {
79 if (window.refreshAllComments !== undefined) {
80 refreshAllComments()
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 postData['close_pull_request'] = true;
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 // reload page if we change status for single commit.
369 // reload page if we change status for single commit.
369 if (status && self.commitId) {
370 if (status && self.commitId) {
370 location.reload(true);
371 location.reload(true);
371 } else {
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 self.resetCommentFormState();
376 self.resetCommentFormState();
374 timeagoActivate();
377 timeagoActivate();
375 tooltipActivate();
378 tooltipActivate();
@@ -565,26 +568,6 b' var CommentsController = function() {'
565 var mainComment = '#text';
568 var mainComment = '#text';
566 var self = this;
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 this.showVersion = function (comment_id, comment_history_id) {
571 this.showVersion = function (comment_id, comment_history_id) {
589
572
590 var historyViewUrl = pyroutes.url(
573 var historyViewUrl = pyroutes.url(
@@ -682,6 +665,35 b' var CommentsController = function() {'
682 return self.scrollToComment(node, -1, true);
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 this._deleteComment = function(node) {
697 this._deleteComment = function(node) {
686 var $node = $(node);
698 var $node = $(node);
687 var $td = $node.closest('td');
699 var $td = $node.closest('td');
@@ -751,7 +763,7 b' var CommentsController = function() {'
751 this.finalizeDrafts = function(commentIds) {
763 this.finalizeDrafts = function(commentIds) {
752
764
753 SwalNoAnimation.fire({
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 icon: 'warning',
767 icon: 'warning',
756 showCancelButton: true,
768 showCancelButton: true,
757 confirmButtonText: _gettext('Yes, finalize drafts'),
769 confirmButtonText: _gettext('Yes, finalize drafts'),
@@ -764,6 +776,7 b' var CommentsController = function() {'
764 };
776 };
765
777
766 this.toggleWideMode = function (node) {
778 this.toggleWideMode = function (node) {
779
767 if ($('#content').hasClass('wrapper')) {
780 if ($('#content').hasClass('wrapper')) {
768 $('#content').removeClass("wrapper");
781 $('#content').removeClass("wrapper");
769 $('#content').addClass("wide-mode-wrapper");
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 var $filediff = $(node).closest('.filediff');
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 if (show === true) {
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 } else if (show === false) {
830 } else if (show === false) {
786 $filediff.find('.hide-line-comments').removeClass('hide-line-comments');
831 $(trElem).find('.comment-outdated').hide();
787 $filediff.addClass('hide-comments');
832 $(trElem).addClass('hide-line-comments');
788 } else {
833 } else {
789 $filediff.find('.hide-line-comments').removeClass('hide-line-comments');
834 // mark outdated comments as visible before the toggle;
790 $filediff.toggleClass('hide-comments');
835 $(trElem).find('.comment-outdated').show();
836 $(trElem).toggleClass('hide-line-comments');
791 }
837 }
792
838
793 // since we change the height of the diff container that has anchor points for upper
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 updateSticky()
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 this.createCommentForm = function(formElement, lineno, placeholderText, initAutocompleteActions, resolvesCommentId, edit, comment_id){
849 this.createCommentForm = function(formElement, lineno, placeholderText, initAutocompleteActions, resolvesCommentId, edit, comment_id){
@@ -960,64 +997,58 b' var CommentsController = function() {'
960 return commentForm;
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 var $node = $(node);
1002 var $node = $(node);
1003 var $td = $node.closest('td');
1004
965 var $comment = $(node).closest('.comment');
1005 var $comment = $(node).closest('.comment');
966 var comment_id = $($comment).data('commentId');
1006 var comment_id = $($comment).data('commentId');
967 var isDraft = $($comment).data('commentDraft');
1007 var isDraft = $($comment).data('commentDraft');
968 var $form = null
1008 var $editForm = null
969
1009
970 var $comments = $node.closest('div.inline-comments');
1010 var $comments = $node.closest('div.inline-comments');
971 var $general_comments = null;
1011 var $general_comments = null;
972 var lineno = null;
973
1012
974 if($comments.length){
1013 if($comments.length){
975 // inline comments setup
1014 // inline comments setup
976 $form = $comments.find('.comment-inline-form');
1015 $editForm = $comments.find('.comment-inline-form');
977 lineno = self.getLineNumber(node)
1016 line_no = self.getLineNumber(node)
978 }
1017 }
979 else{
1018 else{
980 // general comments setup
1019 // general comments setup
981 $comments = $('#comments');
1020 $comments = $('#comments');
982 $form = $comments.find('.comment-inline-form');
1021 $editForm = $comments.find('.comment-inline-form');
983 lineno = $comment[0].id
1022 line_no = $comment[0].id
984 $('#cb-comment-general-form-placeholder').hide();
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) {
1028 // unhide all comments if they are hidden for a proper REPLY mode
990
991 var $filediff = $node.closest('.filediff');
1029 var $filediff = $node.closest('.filediff');
992 $filediff.removeClass('hide-comments');
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();
1032 $editForm = self.createNewFormWrapper(f_path, line_no);
998 tmpl = tmpl.format(escapeHtml(f_path), lineno);
1033 if(f_path && line_no) {
999 $form = $(tmpl);
1034 $editForm.addClass('comment-inline-form-edit')
1000 $comment.after($form)
1035 }
1001
1036
1002 var _form = $($form[0]).find('form');
1037 $comment.after($editForm)
1038
1039 var _form = $($editForm[0]).find('form');
1003 var autocompleteActions = ['as_note',];
1040 var autocompleteActions = ['as_note',];
1004 var commentForm = this.createCommentForm(
1041 var commentForm = this.createCommentForm(
1005 _form, lineno, '', autocompleteActions, resolvesCommentId,
1042 _form, line_no, '', autocompleteActions, resolvesCommentId,
1006 this.edit, comment_id);
1043 this.edit, comment_id);
1007 var old_comment_text_binary = $comment.attr('data-comment-text');
1044 var old_comment_text_binary = $comment.attr('data-comment-text');
1008 var old_comment_text = b64DecodeUnicode(old_comment_text_binary);
1045 var old_comment_text = b64DecodeUnicode(old_comment_text_binary);
1009 commentForm.cm.setValue(old_comment_text);
1046 commentForm.cm.setValue(old_comment_text);
1010 $comment.hide();
1047 $comment.hide();
1048 tooltipActivate();
1011
1049
1012 $.Topic('/ui/plugins/code/comment_form_built').prepareOrPublish({
1050 // set a CUSTOM submit handler for inline comment edit action.
1013 form: _form,
1051 commentForm.setHandleFormSubmit(function(o) {
1014 parent: $comments,
1015 lineno: lineno,
1016 f_path: f_path}
1017 );
1018
1019 // set a CUSTOM submit handler for inline comments.
1020 commentForm.setHandleFormSubmit(function(o) {
1021 var text = commentForm.cm.getValue();
1052 var text = commentForm.cm.getValue();
1022 var commentType = commentForm.getCommentType();
1053 var commentType = commentForm.getCommentType();
1023
1054
@@ -1048,7 +1079,7 b' var CommentsController = function() {'
1048 var postData = {
1079 var postData = {
1049 'text': text,
1080 'text': text,
1050 'f_path': f_path,
1081 'f_path': f_path,
1051 'line': lineno,
1082 'line': line_no,
1052 'comment_type': commentType,
1083 'comment_type': commentType,
1053 'draft': isDraft,
1084 'draft': isDraft,
1054 'version': version,
1085 'version': version,
@@ -1056,7 +1087,7 b' var CommentsController = function() {'
1056 };
1087 };
1057
1088
1058 var submitSuccessCallback = function(json_data) {
1089 var submitSuccessCallback = function(json_data) {
1059 $form.remove();
1090 $editForm.remove();
1060 $comment.show();
1091 $comment.show();
1061 var postData = {
1092 var postData = {
1062 'text': text,
1093 'text': text,
@@ -1121,8 +1152,7 b' var CommentsController = function() {'
1121 'commit_id': templateContext.commit_data.commit_id});
1152 'commit_id': templateContext.commit_data.commit_id});
1122
1153
1123 _submitAjaxPOST(
1154 _submitAjaxPOST(
1124 previewUrl, postData, successRenderCommit,
1155 previewUrl, postData, successRenderCommit, failRenderCommit
1125 failRenderCommit
1126 );
1156 );
1127
1157
1128 try {
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) {
1214 this.attachComment = function(json_data) {
1185 var resolvesCommentId = resolutionComment || null;
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 var $node = $(node);
1266 var $node = $(node);
1187 var $td = $node.closest('td');
1267 var $td = $node.closest('td');
1188 var $form = $td.find('.comment-inline-form');
1268 var resolvesCommentId = resolutionComment || null;
1189 this.edit = false;
1190
1269
1191 if (!$form.length) {
1270 var $replyForm = $td.find('.comment-inline-form');
1192
1271
1193 var $filediff = $node.closest('.filediff');
1272 // if form isn't existing, we're generating a new one and injecting it.
1194 $filediff.removeClass('hide-comments');
1273 if ($replyForm.length === 0) {
1195 var f_path = $filediff.attr('data-f-path');
1274
1196 var lineno = self.getLineNumber(node);
1275 // unhide/expand all comments if they are hidden for a proper REPLY mode
1197 // create a new HTML from template
1276 self.toggleLineComments($node, true);
1198 var tmpl = $('#cb-comment-inline-form-template').html();
1277
1199 tmpl = tmpl.format(escapeHtml(f_path), lineno);
1278 $replyForm = self.createNewFormWrapper(f_path, line_no);
1200 $form = $(tmpl);
1201
1279
1202 var $comments = $td.find('.inline-comments');
1280 var $comments = $td.find('.inline-comments');
1203 if (!$comments.length) {
1281
1204 $comments = $(
1282 // There aren't any comments, we init the `.inline-comments` with `reply-thread-container` first
1205 $('#cb-comments-inline-container-template').html());
1283 if ($comments.length===0) {
1206 $td.append($comments);
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);
1296 var lastComment = $comments.find('.comment-inline').last();
1212 var _form = $($form[0]).find('form');
1297 if ($(lastComment).hasClass('comment-outdated')) {
1298 $replyWrapper.show();
1299 }
1300
1301 var _form = $($replyForm[0]).find('form');
1213 var autocompleteActions = ['as_note', 'as_todo'];
1302 var autocompleteActions = ['as_note', 'as_todo'];
1214 var comment_id=null;
1303 var comment_id=null;
1215 var commentForm = this.createCommentForm(
1304 var placeholderText = _gettext('Leave a comment on file {0} line {1}.').format(f_path, line_no);
1216 _form, lineno, placeholderText, autocompleteActions, resolvesCommentId, this.edit, comment_id);
1305 var commentForm = self.createCommentForm(
1217
1306 _form, line_no, placeholderText, autocompleteActions, resolvesCommentId,
1218 $.Topic('/ui/plugins/code/comment_form_built').prepareOrPublish({
1307 self.edit, comment_id);
1219 form: _form,
1220 parent: $td[0],
1221 lineno: lineno,
1222 f_path: f_path}
1223 );
1224
1308
1225 // set a CUSTOM submit handler for inline comments.
1309 // set a CUSTOM submit handler for inline comments.
1226 commentForm.setHandleFormSubmit(function(o) {
1310 commentForm.setHandleFormSubmit(function(o) {
@@ -1233,12 +1317,13 b' var CommentsController = function() {'
1233 return;
1317 return;
1234 }
1318 }
1235
1319
1236 if (lineno === undefined) {
1320 if (line_no === undefined) {
1237 alert('missing line !');
1321 alert('Error: unable to fetch line number for this inline comment !');
1238 return;
1322 return;
1239 }
1323 }
1324
1240 if (f_path === undefined) {
1325 if (f_path === undefined) {
1241 alert('missing file path !');
1326 alert('Error: unable to fetch file path for this inline comment !');
1242 return;
1327 return;
1243 }
1328 }
1244
1329
@@ -1249,7 +1334,7 b' var CommentsController = function() {'
1249 var postData = {
1334 var postData = {
1250 'text': text,
1335 'text': text,
1251 'f_path': f_path,
1336 'f_path': f_path,
1252 'line': lineno,
1337 'line': line_no,
1253 'comment_type': commentType,
1338 'comment_type': commentType,
1254 'draft': isDraft,
1339 'draft': isDraft,
1255 'csrf_token': CSRF_TOKEN
1340 'csrf_token': CSRF_TOKEN
@@ -1258,32 +1343,32 b' var CommentsController = function() {'
1258 postData['resolves_comment_id'] = resolvesCommentId;
1343 postData['resolves_comment_id'] = resolvesCommentId;
1259 }
1344 }
1260
1345
1346 // submitSuccess for inline commits
1261 var submitSuccessCallback = function(json_data) {
1347 var submitSuccessCallback = function(json_data) {
1262 $form.remove();
1348
1263 try {
1349 $replyForm.remove();
1264 var html = json_data.rendered_text;
1350 $td.find('.reply-thread-container-wrapper').removeClass('comment-form-active');
1265 var lineno = json_data.line_no;
1351
1266 var target_id = json_data.target_id;
1352 try {
1353
1354 // inject newly created comments, json_data is {<comment_id>: {}}
1355 self.attachInlineComment(json_data)
1267
1356
1268 $comments.find('.cb-comment-add-button').before(html);
1357 //mark visually which comment was resolved
1358 if (resolvesCommentId) {
1359 commentForm.markCommentResolved(resolvesCommentId);
1360 }
1269
1361
1270 //mark visually which comment was resolved
1362 // run global callback on submit
1271 if (resolvesCommentId) {
1363 commentForm.globalSubmitSuccessCallback({
1272 commentForm.markCommentResolved(resolvesCommentId);
1364 draft: isDraft,
1365 comment_id: comment_id
1366 });
1367
1368 } catch (e) {
1369 console.error(e);
1273 }
1370 }
1274
1371
1275 // run global callback on submit
1276 commentForm.globalSubmitSuccessCallback({draft: isDraft, comment_id: comment_id});
1277
1278 } catch (e) {
1279 console.error(e);
1280 }
1281
1282 // re trigger the linkification of next/prev navigation
1283 linkifyComments($('.inline-comment-injected'));
1284 timeagoActivate();
1285 tooltipActivate();
1286
1287 if (window.updateSticky !== undefined) {
1372 if (window.updateSticky !== undefined) {
1288 // potentially our comments change the active window size, so we
1373 // potentially our comments change the active window size, so we
1289 // notify sticky elements
1374 // notify sticky elements
@@ -1297,19 +1382,27 b' var CommentsController = function() {'
1297
1382
1298 commentForm.setActionButtonsDisabled(false);
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 var submitFailCallback = function(jqXHR, textStatus, errorThrown) {
1391 var submitFailCallback = function(jqXHR, textStatus, errorThrown) {
1302 var prefix = "Error while submitting comment.\n"
1392 var prefix = "Error while submitting comment.\n"
1303 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
1393 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
1304 ajaxErrorSwal(message);
1394 ajaxErrorSwal(message);
1305 commentForm.resetCommentFormState(text)
1395 commentForm.resetCommentFormState(text)
1306 };
1396 };
1397
1307 commentForm.submitAjaxPOST(
1398 commentForm.submitAjaxPOST(
1308 commentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback);
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 this.createResolutionComment = function(commentId){
1408 this.createResolutionComment = function(commentId){
@@ -1319,9 +1412,12 b' var CommentsController = function() {'
1319 var comment = $('#comment-'+commentId);
1412 var comment = $('#comment-'+commentId);
1320 var commentData = comment.data();
1413 var commentData = comment.data();
1321 if (commentData.commentInline) {
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 } else {
1419 } else {
1324 Rhodecode.comments.createGeneralComment('general', "$placeholder", commentId)
1420 this.createGeneralComment('general', "$placeholder", commentId)
1325 }
1421 }
1326
1422
1327 return false;
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 var $elem = $(elem);
42 var $elem = $(elem);
43 var $target = $(target);
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 $target.hide();
52 $target.hide();
47 $elem.html($elem.data('toggleOn'))
53 $elem.html($elem.data('toggleOn'))
54 $elem.addClass('toggle-on')
55 $elem.removeClass('toggle-off')
48 } else {
56 } else {
49 $target.show();
57 $target.show();
50 $elem.html($elem.data('toggleOff'))
58 $elem.html($elem.data('toggleOff'))
59 $elem.addClass('toggle-off')
60 $elem.removeClass('toggle-on')
51 }
61 }
52
62
53 return false
63 return false
@@ -1,7 +1,5 b''
1 /__MAIN_APP__ - launched when rhodecode-app element is attached to DOM
1 /__MAIN_APP__ - launched when rhodecode-app element is attached to DOM
2 /plugins/__REGISTER__ - launched after the onDomReady() code from rhodecode.js is executed
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 /notifications - shows new event notifications
3 /notifications - shows new event notifications
6 /connection_controller/subscribe - subscribes user to new channels
4 /connection_controller/subscribe - subscribes user to new channels
7 /connection_controller/presence - receives presence change messages
5 /connection_controller/presence - receives presence change messages
@@ -1,4 +1,4 b''
1 ## this is a dummy html file for partial rendering on server and sending
1 ## this is a dummy html file for partial rendering on server and sending
2 ## generated output via ajax after comment submit
2 ## generated output via ajax after comment submit
3 <%namespace name="comment" file="/changeset/changeset_file_comment.mako"/>
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 from rhodecode.model.comment import CommentsModel
16 from rhodecode.model.comment import CommentsModel
@@ -40,6 +40,7 b''
40 data-comment-draft=${h.json.dumps(comment.draft)}
40 data-comment-draft=${h.json.dumps(comment.draft)}
41 data-comment-renderer="${comment.renderer}"
41 data-comment-renderer="${comment.renderer}"
42 data-comment-text="${comment.text | html_filters.base64,n}"
42 data-comment-text="${comment.text | html_filters.base64,n}"
43 data-comment-f-path="${comment.f_path}"
43 data-comment-line-no="${comment.line_no}"
44 data-comment-line-no="${comment.line_no}"
44 data-comment-inline=${h.json.dumps(inline)}
45 data-comment-inline=${h.json.dumps(inline)}
45 style="${'display: none;' if outdated_at_ver else ''}">
46 style="${'display: none;' if outdated_at_ver else ''}">
@@ -47,8 +48,17 b''
47 <div class="meta">
48 <div class="meta">
48 <div class="comment-type-label">
49 <div class="comment-type-label">
49 % if comment.draft:
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 % endif
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 <div class="comment-label ${comment.comment_type or 'note'}" id="comment-label-${comment.comment_id}">
62 <div class="comment-label ${comment.comment_type or 'note'}" id="comment-label-${comment.comment_id}">
53
63
54 ## TODO COMMENT
64 ## TODO COMMENT
@@ -176,7 +186,7 b''
176 % if inline:
186 % if inline:
177 <a class="pr-version-inline" href="${request.current_route_path(_query=dict(version=comment.pull_request_version_id), _anchor='comment-{}'.format(comment.comment_id))}">
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 % if outdated_at_ver:
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 <code class="action-divider">|</code>
190 <code class="action-divider">|</code>
181 % elif comment_ver:
191 % elif comment_ver:
182 <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>
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 %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):
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 <div class="dropdown-divider"></div>
233 <div class="dropdown-divider"></div>
224 <div class="dropdown-item">
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 </div>
236 </div>
227 <div class="dropdown-item">
237 <div class="dropdown-item">
228 <a onclick="return Rhodecode.comments.deleteComment(this);" class="btn btn-link btn-sm btn-danger delete-comment">${_('Delete')}</a>
238 <a onclick="return Rhodecode.comments.deleteComment(this);" class="btn btn-link btn-sm btn-danger delete-comment">${_('Delete')}</a>
229 </div>
239 </div>
230 % if comment.draft:
240 ## Only available in EE edition
241 % if comment.draft and c.rhodecode_edition_id == 'EE':
231 <div class="dropdown-item">
242 <div class="dropdown-item">
232 <a onclick="return Rhodecode.comments.finalizeDrafts([${comment.comment_id}]);" class="btn btn-link btn-sm finalize-draft-comment">${_('Submit draft')}</a>
243 <a onclick="return Rhodecode.comments.finalizeDrafts([${comment.comment_id}]);" class="btn btn-link btn-sm finalize-draft-comment">${_('Submit draft')}</a>
233 </div>
244 </div>
@@ -391,7 +402,7 b''
391
402
392 <div class="comment-area-write" style="display: block;">
403 <div class="comment-area-write" style="display: block;">
393 <div id="edit-container">
404 <div id="edit-container">
394 <div style="padding: 40px 0">
405 <div style="padding: 20px 0px 0px 0;">
395 ${_('You need to be logged in to leave comments.')}
406 ${_('You need to be logged in to leave comments.')}
396 <a href="${h.route_path('login', _query={'came_from': h.current_route_path(request)})}">${_('Login now')}</a>
407 <a href="${h.route_path('login', _query={'came_from': h.current_route_path(request)})}">${_('Login now')}</a>
397 </div>
408 </div>
@@ -450,7 +461,7 b''
450 </div>
461 </div>
451
462
452 <div class="comment-area-write" style="display: block;">
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 <textarea id="text_${lineno_id}" name="text" class="comment-block-ta ac-input"></textarea>
465 <textarea id="text_${lineno_id}" name="text" class="comment-block-ta ac-input"></textarea>
455 </div>
466 </div>
456 <div id="preview-container_${lineno_id}" class="clearfix" style="display: none;">
467 <div id="preview-container_${lineno_id}" class="clearfix" style="display: none;">
@@ -500,40 +511,47 b''
500 <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')">
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 % if form_type == 'inline':
513 % if form_type == 'inline':
503 <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')">
514 % if c.rhodecode_edition_id == 'EE':
515 ## Disable the button for CE, the "real" validation is in the backend code anyway
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
520 % endif
521
522 % if review_statuses:
523 <div class="comment-status-box">
524 <select id="change_status_${lineno_id}" name="changeset_status">
525 <option></option> ## Placeholder
526 % for status, lbl in review_statuses:
527 <option value="${status}" data-status="${status}">${lbl}</option>
528 %if is_pull_request and change_status and status in ('approved', 'rejected'):
529 <option value="${status}_closed" data-status="${status}">${lbl} & ${_('Closed')}</option>
530 %endif
531 % endfor
532 </select>
533 </div>
504 % endif
534 % endif
505
535
506 ## inline for has a file, and line-number together with cancel hide button.
536 ## inline for has a file, and line-number together with cancel hide button.
507 % if form_type == 'inline':
537 % if form_type == 'inline':
508 <input type="hidden" name="f_path" value="{0}">
538 <input type="hidden" name="f_path" value="{0}">
509 <input type="hidden" name="line" value="${lineno_id}">
539 <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">
540 <button type="button" class="cb-comment-cancel" onclick="return Rhodecode.comments.cancelComment(this);">
511 <i class="icon-cancel-circled2"></i>
541 <i class="icon-cancel-circled2"></i>
512 </button>
542 </button>
513 % endif
543 % endif
514 </div>
544 </div>
515
545
516 % if review_statuses:
517 <div class="status_box">
518 <select id="change_status_${lineno_id}" name="changeset_status">
519 <option></option> ## Placeholder
520 % for status, lbl in review_statuses:
521 <option value="${status}" data-status="${status}">${lbl}</option>
522 %if is_pull_request and change_status and status in ('approved', 'rejected'):
523 <option value="${status}_closed" data-status="${status}">${lbl} & ${_('Closed')}</option>
524 %endif
525 % endfor
526 </select>
527 </div>
528 % endif
529
530 <div class="toolbar-text">
546 <div class="toolbar-text">
531 <% renderer_url = '<a href="%s">%s</a>' % (h.route_url('%s_help' % c.visual.default_renderer), c.visual.default_renderer.upper()) %>
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/>
548 <p>${_('Styling with {} is supported.').format(renderer_url)|n}
533 <span class="tooltip" title="${_('Use @username inside this text to send notification to this RhodeCode user')}">@mention</span>
549
534 ${_('and')}
550 <i class="icon-info-circled tooltip-hovercard"
535 <span class="tooltip" title="${_('Start typing with / for certain actions to be triggered via text box.')}">`/` autocomplete</span>
551 data-hovercard-alt="ALT"
536 ${_('actions supported.')}
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 </div>
555 </div>
538 </div>
556 </div>
539
557
@@ -1,3 +1,4 b''
1 <%namespace name="base" file="/base/base.mako"/>
1 <%namespace name="commentblock" file="/changeset/changeset_file_comment.mako"/>
2 <%namespace name="commentblock" file="/changeset/changeset_file_comment.mako"/>
2
3
3 <%def name="diff_line_anchor(commit, filename, line, type)"><%
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 <div class="js-template" id="cb-comment-inline-form-template">
76 <div class="js-template" id="cb-comment-inline-form-template">
76 <div class="comment-inline-form ac">
77 <div class="comment-inline-form ac">
77
78 %if not c.rhodecode_user.is_default:
78 %if c.rhodecode_user.username != h.DEFAULT_USER:
79 ## render template for inline comments
79 ## render template for inline comments
80 ${commentblock.comment_form(form_type='inline')}
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 %endif
81 %endif
96 </div>
82 </div>
97 </div>
83 </div>
@@ -327,7 +313,7 b" return '%s_%s_%i' % (h.md5_safe(commit+f"
327 </label>
313 </label>
328
314
329 ${diff_menu(filediff, use_comments=use_comments)}
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 ## new/deleted/empty content case
318 ## new/deleted/empty content case
333 % if not filediff.hunks:
319 % if not filediff.hunks:
@@ -626,8 +612,10 b" return '%s_%s_%i' % (h.md5_safe(commit+f"
626
612
627 % if use_comments:
613 % if use_comments:
628 |
614 |
629 <a href="#" onclick="return Rhodecode.comments.toggleComments(this);">
615 <a href="#" onclick="Rhodecode.comments.toggleDiffComments(this);return toggleElement(this)"
630 <span class="show-comment-button">${_('Show comments')}</span><span class="hide-comment-button">${_('Hide comments')}</span>
616 data-toggle-on="${_('Hide comments')}"
617 data-toggle-off="${_('Show comments')}">
618 <span class="hide-comment-button">${_('Hide comments')}</span>
631 </a>
619 </a>
632 % endif
620 % endif
633
621
@@ -637,23 +625,36 b" return '%s_%s_%i' % (h.md5_safe(commit+f"
637 </%def>
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 <div class="inline-comments">
630 <div class="inline-comments">
643 %for comment in comments:
631 %for comment in comments:
644 ${commentblock.comment_block(comment, inline=True, active_pattern_entries=active_pattern_entries)}
632 ${commentblock.comment_block(comment, inline=True, active_pattern_entries=active_pattern_entries)}
645 %endfor
633 %endfor
646 % if comments and comments[-1].outdated:
634
647 <span class="btn btn-secondary cb-comment-add-button comment-outdated}" style="display: none;}">
635 <%
648 ${_('Add another comment')}
636 extra_class = ''
649 </span>
637 extra_style = ''
650 % else:
638
651 <span onclick="return Rhodecode.comments.createComment(this)" class="btn btn-secondary cb-comment-add-button">
639 if comments and comments[-1].outdated:
652 ${_('Add another comment')}
640 extra_class = ' comment-outdated'
653 </span>
641 extra_style = 'display: none;'
654 % endif
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)}
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>
656 </div>
657
657 </%def>
658 </%def>
658
659
659 <%!
660 <%!
@@ -721,9 +722,9 b' def get_comments_for(diff_type, comments'
721 %endif
722 %endif
722 %if line_old_comments_no_drafts:
723 %if line_old_comments_no_drafts:
723 % if has_outdated:
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 % else:
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 % endif
728 % endif
728 %endif
729 %endif
729 </td>
730 </td>
@@ -737,16 +738,18 b' def get_comments_for(diff_type, comments'
737 <a name="${old_line_anchor}" href="#${old_line_anchor}">${line.original.lineno}</a>
738 <a name="${old_line_anchor}" href="#${old_line_anchor}">${line.original.lineno}</a>
738 %endif
739 %endif
739 </td>
740 </td>
741
742 <% line_no = 'o{}'.format(line.original.lineno) %>
740 <td class="cb-content ${action_class(line.original.action)}"
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 %if use_comments and line.original.lineno:
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 %endif
748 %endif
746 <span class="cb-code"><span class="cb-action ${action_class(line.original.action)}"></span>${line.original.content or '' | n}</span>
749 <span class="cb-code"><span class="cb-action ${action_class(line.original.action)}"></span>${line.original.content or '' | n}</span>
747
750
748 %if use_comments and line.original.lineno and line_old_comments:
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 %endif
753 %endif
751
754
752 </td>
755 </td>
@@ -766,9 +769,9 b' def get_comments_for(diff_type, comments'
766
769
767 %if line_new_comments_no_drafts:
770 %if line_new_comments_no_drafts:
768 % if has_outdated:
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 % else:
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 % endif
775 % endif
773 %endif
776 %endif
774 </div>
777 </div>
@@ -783,22 +786,25 b' def get_comments_for(diff_type, comments'
783 <a name="${new_line_anchor}" href="#${new_line_anchor}">${line.modified.lineno}</a>
786 <a name="${new_line_anchor}" href="#${new_line_anchor}">${line.modified.lineno}</a>
784 %endif
787 %endif
785 </td>
788 </td>
789
790 <% line_no = 'n{}'.format(line.modified.lineno) %>
786 <td class="cb-content ${action_class(line.modified.action)}"
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 %if use_comments and line.modified.lineno:
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 %endif
796 %endif
792 <span class="cb-code"><span class="cb-action ${action_class(line.modified.action)}"></span>${line.modified.content or '' | n}</span>
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 % if line_action in ['+', '-'] and prev_line_action not in ['+', '-']:
798 % if line_action in ['+', '-'] and prev_line_action not in ['+', '-']:
797 <div class="nav-chunk" style="visibility: hidden">
799 <div class="nav-chunk" style="visibility: hidden">
798 <i class="icon-eye" title="viewing diff hunk-${hunk.index}-${chunk_count}"></i>
800 <i class="icon-eye" title="viewing diff hunk-${hunk.index}-${chunk_count}"></i>
799 </div>
801 </div>
800 <% chunk_count +=1 %>
802 <% chunk_count +=1 %>
801 % endif
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 </td>
808 </td>
803 </tr>
809 </tr>
804 %endfor
810 %endfor
@@ -830,9 +836,9 b' def get_comments_for(diff_type, comments'
830
836
831 % if comments_no_drafts:
837 % if comments_no_drafts:
832 % if has_outdated:
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 % else:
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 % endif
842 % endif
837 % endif
843 % endif
838 </div>
844 </div>
@@ -857,15 +863,16 b' def get_comments_for(diff_type, comments'
857 <a name="${new_line_anchor}" href="#${new_line_anchor}">${new_line_no}</a>
863 <a name="${new_line_anchor}" href="#${new_line_anchor}">${new_line_no}</a>
858 %endif
864 %endif
859 </td>
865 </td>
866 <% line_no = '{}{}'.format(new_line_no and 'n' or 'o', new_line_no or old_line_no) %>
860 <td class="cb-content ${action_class(action)}"
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 %if use_comments:
870 %if use_comments:
864 ${render_add_comment_button()}
871 ${render_add_comment_button(line_no=line_no, f_path=filediff.patch['filename'])}
865 %endif
872 %endif
866 <span class="cb-code"><span class="cb-action ${action_class(action)}"></span> ${content or '' | n}</span>
873 <span class="cb-code"><span class="cb-action ${action_class(action)}"></span> ${content or '' | n}</span>
867 %if use_comments and comments:
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 %endif
876 %endif
870 </td>
877 </td>
871 </tr>
878 </tr>
@@ -886,10 +893,12 b' def get_comments_for(diff_type, comments'
886 </%def>file changes
893 </%def>file changes
887
894
888
895
889 <%def name="render_add_comment_button()">
896 <%def name="render_add_comment_button(line_no='', f_path='')">
890 <button class="btn btn-small btn-primary cb-comment-box-opener" onclick="return Rhodecode.comments.createComment(this)">
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 <span><i class="icon-comment"></i></span>
899 <span><i class="icon-comment"></i></span>
892 </button>
900 </button>
901 % endif
893 </%def>
902 </%def>
894
903
895 <%def name="render_diffset_menu(diffset, range_diff_on=None, commit=None, pull_request_menu=None)">
904 <%def name="render_diffset_menu(diffset, range_diff_on=None, commit=None, pull_request_menu=None)">
@@ -456,7 +456,7 b''
456 </div>
456 </div>
457
457
458 <div class="markup-form-area-write" style="display: block;">
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 <textarea id="${form_id}" name="${form_id}" class="comment-block-ta ac-input">${form_text if form_text else ''}</textarea>
460 <textarea id="${form_id}" name="${form_id}" class="comment-block-ta ac-input">${form_text if form_text else ''}</textarea>
461 </div>
461 </div>
462 <div id="preview-container_${form_id}" class="clearfix" style="display: none;">
462 <div id="preview-container_${form_id}" class="clearfix" style="display: none;">
@@ -237,6 +237,24 b' if (show_disabled) {'
237
237
238 </script>
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 ##// END OF EJS Templates
258 ##// END OF EJS Templates
241 </div>
259 </div>
242
260
@@ -846,6 +846,7 b' versionController.init();'
846
846
847 reviewersController = new ReviewersController();
847 reviewersController = new ReviewersController();
848 commitsController = new CommitsController();
848 commitsController = new CommitsController();
849 commentsController = new CommentsController();
849
850
850 updateController = new UpdatePrController();
851 updateController = new UpdatePrController();
851
852
@@ -1002,6 +1003,8 b' window.setObserversData = ${c.pull_reque'
1002 alert('okok !' + commentIds)
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 </script>
1010 </script>
General Comments 0
You need to be logged in to leave comments. Login now