pull-requests: added observers, and fix few problems with versioned comments

This commit is contained in:
Marcin Kuzminski 2020-08-21 20:56:59 +02:00
parent 246e0968cd
commit 9106e99baf
12 changed files with 6259 additions and 227 deletions

View file

@ -48,7 +48,7 @@ PYRAMID_SETTINGS = {}
EXTENSIONS = {}
__version__ = ('.'.join((str(each) for each in VERSION[:3])))
__dbversion__ = 108 # defines current db version for migrations
__dbversion__ = 109 # defines current db version for migrations
__platform__ = platform.system()
__license__ = 'AGPLv3, and Commercial License'
__author__ = 'RhodeCode GmbH'

View file

@ -166,8 +166,8 @@ class RepoCommitsView(RepoAppView):
if method == 'show':
inline_comments = CommentsModel().get_inline_comments(
self.db_repo.repo_id, revision=commit.raw_id)
c.inline_cnt = CommentsModel().get_inline_comments_count(
inline_comments)
c.inline_cnt = len(CommentsModel().get_inline_comments_as_list(
inline_comments))
c.inline_comments = inline_comments
cache_path = self.rhodecode_vcs_repo.get_create_shadow_cache_pr_path(

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
import logging
from sqlalchemy import *
from alembic.migration import MigrationContext
from alembic.operations import Operations
from rhodecode.lib.dbmigrate.versions import _reset_base
from rhodecode.model import meta, init_model_encryption
log = logging.getLogger(__name__)
def upgrade(migrate_engine):
"""
Upgrade operations go here.
Don't create your own engine; bind migrate_engine to your metadata
"""
_reset_base(migrate_engine)
from rhodecode.lib.dbmigrate.schema import db_4_20_0_0 as db
init_model_encryption(db)
context = MigrationContext.configure(migrate_engine.connect())
op = Operations(context)
table = db.PullRequestReviewers.__table__
with op.batch_alter_table(table.name) as batch_op:
new_column = Column('role', Unicode(255), nullable=True)
batch_op.add_column(new_column)
_fill_reviewers_role(db, op, meta.Session)
def downgrade(migrate_engine):
meta = MetaData()
meta.bind = migrate_engine
def fixups(models, _SESSION):
pass
def _fill_reviewers_role(models, op, session):
params = {'role': 'reviewer'}
query = text(
'UPDATE pull_request_reviewers SET role = :role'
).bindparams(**params)
op.execute(query)
session().commit()

View file

@ -641,7 +641,7 @@ class CommentsModel(BaseModel):
q = self._get_inline_comments_query(repo_id, revision, pull_request)
return self._group_comments_by_path_and_line_number(q)
def get_inline_comments_count(self, inline_comments, skip_outdated=True,
def get_inline_comments_as_list(self, inline_comments, skip_outdated=True,
version=None):
inline_cnt = 0
for fname, per_line_comments in inline_comments.iteritems():

View file

@ -3809,6 +3809,10 @@ class ChangesetComment(Base, BaseModel):
def outdated(self):
return self.display_state == self.COMMENT_OUTDATED
@property
def outdated_js(self):
return json.dumps(self.display_state == self.COMMENT_OUTDATED)
@property
def immutable(self):
return self.immutable_state == self.OP_IMMUTABLE
@ -4492,6 +4496,8 @@ class PullRequestReviewers(Base, BaseModel):
__table_args__ = (
base_table_args,
)
ROLE_REVIEWER = u'reviewer'
ROLE_OBSERVER = u'observer'
@hybrid_property
def reasons(self):
@ -4519,6 +4525,8 @@ class PullRequestReviewers(Base, BaseModel):
JsonType('list', dialect_map=dict(mysql=UnicodeText(16384)))))
mandatory = Column("mandatory", Boolean(), nullable=False, default=False)
role = Column('role', Unicode(255), nullable=True, default=ROLE_REVIEWER)
user = relationship('User')
pull_request = relationship('PullRequest')

View file

@ -484,10 +484,6 @@ ul.auth_plugins {
white-space: pre-line;
}
.pr-details-title {
height: 16px
}
.pr-details-title-author-pref {
padding-right: 10px
}
@ -1492,26 +1488,17 @@ table.integrations {
// Pull Requests
.summary-details {
width: 72%;
width: 100%;
}
.pr-summary {
border-bottom: @border-thickness solid @grey5;
margin-bottom: @space;
}
.reviewers-title {
width: 25%;
min-width: 200px;
&.first-panel {
margin-top: 34px;
}
}
.reviewers {
width: 25%;
min-width: 200px;
width: 98%;
}
.reviewers ul li {
position: relative;
width: 100%;
@ -1593,6 +1580,9 @@ table.integrations {
cursor: pointer;
}
.pr-details-title {
height: 20px;
line-height: 20px;
padding-bottom: 8px;
border-bottom: @border-thickness solid @grey5;
@ -1617,7 +1607,7 @@ table.integrations {
text-decoration: line-through;
}
.todo-table {
.todo-table, .comments-table {
width: 100%;
td {

View file

@ -38,10 +38,12 @@
<div class="main">
${next.main()}
</div>
</div>
<!-- END CONTENT -->
</div>
<!-- FOOTER -->
<div id="footer">
<div id="footer-inner" class="title wrapper">

View file

@ -159,45 +159,45 @@ return '%s_%s_%i' % (h.md5_safe(commit+filename), type, line)
</div>
% endif
## comments
<div class="pull-right">
<div class="comments-number" style="padding-left: 10px">
% if hasattr(c, 'comments') and hasattr(c, 'inline_cnt'):
<i class="icon-comment" style="color: #949494">COMMENTS:</i>
% if c.comments:
<a href="#comments">${_ungettext("{} General", "{} General", len(c.comments)).format(len(c.comments))}</a>,
% else:
${_('0 General')}
% endif
% if c.inline_cnt:
<a href="#" onclick="return Rhodecode.comments.nextComment();"
id="inline-comments-counter">${_ungettext("{} Inline", "{} Inline", c.inline_cnt).format(c.inline_cnt)}
</a>
% else:
${_('0 Inline')}
% endif
% endif
% if pull_request_menu:
<%
outdated_comm_count_ver = pull_request_menu['outdated_comm_count_ver']
%>
% if outdated_comm_count_ver:
<a href="#" onclick="showOutdated(); Rhodecode.comments.nextOutdatedComment(); return false;">
(${_("{} Outdated").format(outdated_comm_count_ver)})
</a>
<a href="#" class="showOutdatedComments" onclick="showOutdated(this); return false;"> | ${_('show outdated')}</a>
<a href="#" class="hideOutdatedComments" style="display: none" onclick="hideOutdated(this); return false;"> | ${_('hide outdated')}</a>
% else:
(${_("{} Outdated").format(outdated_comm_count_ver)})
% endif
% endif
</div>
</div>
## ## comments
## <div class="pull-right">
## <div class="comments-number" style="padding-left: 10px">
## % if hasattr(c, 'comments') and hasattr(c, 'inline_cnt'):
## <i class="icon-comment" style="color: #949494">COMMENTS:</i>
## % if c.comments:
## <a href="#comments">${_ungettext("{} General", "{} General", len(c.comments)).format(len(c.comments))}</a>,
## % else:
## ${_('0 General')}
## % endif
##
## % if c.inline_cnt:
## <a href="#" onclick="return Rhodecode.comments.nextComment();"
## id="inline-comments-counter">${_ungettext("{} Inline", "{} Inline", c.inline_cnt).format(c.inline_cnt)}
## </a>
## % else:
## ${_('0 Inline')}
## % endif
## % endif
##
## % if pull_request_menu:
## <%
## outdated_comm_count_ver = pull_request_menu['outdated_comm_count_ver']
## %>
##
## % if outdated_comm_count_ver:
## <a href="#" onclick="showOutdated(); Rhodecode.comments.nextOutdatedComment(); return false;">
## (${_("{} Outdated").format(outdated_comm_count_ver)})
## </a>
## <a href="#" class="showOutdatedComments" onclick="showOutdated(this); return false;"> | ${_('show outdated')}</a>
## <a href="#" class="hideOutdatedComments" style="display: none" onclick="hideOutdated(this); return false;"> | ${_('hide outdated')}</a>
## % else:
## (${_("{} Outdated").format(outdated_comm_count_ver)})
## % endif
##
## % endif
##
## </div>
## </div>
</div>
@ -934,7 +934,7 @@ def get_comments_for(diff_type, comments, filename, line_version, line_number):
</span>
%endif
% if commit or pull_request_menu:
<span id="diff_nav">Loading diff...:</span>
<span class="tooltip" title="Navigate to previous or next change inside files." id="diff_nav">Loading diff...:</span>
<span class="cursor-pointer" onclick="scrollToPrevChunk(); return false">
<i class="icon-angle-up"></i>
</span>

View file

@ -39,25 +39,26 @@ var CG = new ColorGenerator();
</script>
<script id="ejs_reviewMemberEntry" type="text/template" class="ejsTemplate">
<%
if (create) {
var edit_visibility = 'visible';
} else {
var edit_visibility = 'hidden';
}
<li id="reviewer_<%= member.user_id %>" class="reviewer_entry">
<%
if (create) {
var edit_visibility = 'visible';
} else {
var edit_visibility = 'hidden';
}
if (member.user_group && member.user_group.vote_rule) {
var groupStyle = 'border-right: 2px solid '+CG.asRGB(CG.getColor(member.user_group.vote_rule));
} else {
var groupStyle = 'border-right: 2px solid transparent';
}
%>
if (member.user_group && member.user_group.vote_rule) {
var groupStyle = 'border-left: 1px solid '+CG.asRGB(CG.getColor(member.user_group.vote_rule));
} else {
var groupStyle = 'border-left: 1px solid white';
}
%>
<li id="reviewer_<%= member.user_id %>" class="reviewer_entry" style="<%= groupStyle%>" tooltip="Review Group">
<div class="reviewers_member" style="<%= groupStyle%>" >
<div class="reviewers_member">
<div class="reviewer_status tooltip" title="<%= review_status_label %>">
<i class="icon-circle review-status-<%= review_status %>"></i>
</div>
<div id="reviewer_<%= member.user_id %>_name" class="reviewer_name">
<% if (mandatory) { %>

View file

@ -280,159 +280,14 @@
</div>
## REVIEW RULES
<div id="review_rules" style="display: none" class="reviewers-title block-right">
<div class="pr-details-title">
${_('Reviewer rules')}
%if c.allowed_to_update:
<span id="close_edit_reviewers" class="block-right action_button last-item" style="display: none;">${_('Close')}</span>
%endif
</div>
<div class="pr-reviewer-rules">
## review rules will be appended here, by default reviewers logic
</div>
<input id="review_data" type="hidden" name="review_data" value="">
</div>
## REVIEWERS
<div class="reviewers-title first-panel block-right">
<div class="pr-details-title">
${_('Pull request reviewers')}
%if c.allowed_to_update:
<span id="open_edit_reviewers" class="block-right action_button last-item">${_('Edit')}</span>
%endif
</div>
</div>
<div id="reviewers" class="block-right pr-details-content reviewers">
## members redering block
<input type="hidden" name="__start__" value="review_members:sequence">
<ul id="review_members" class="group_members">
% for review_obj, member, reasons, mandatory, status in c.pull_request_reviewers:
<script>
var member = ${h.json.dumps(h.reviewer_as_json(member, reasons=reasons, mandatory=mandatory, user_group=review_obj.rule_user_group_data()))|n};
var status = "${(status[0][1].status if status else 'not_reviewed')}";
var status_lbl = "${h.commit_status_lbl(status[0][1].status if status else 'not_reviewed')}";
var allowed_to_update = ${h.json.dumps(c.allowed_to_update)};
var entry = renderTemplate('reviewMemberEntry', {
'member': member,
'mandatory': member.mandatory,
'reasons': member.reasons,
'allowed_to_update': allowed_to_update,
'review_status': status,
'review_status_label': status_lbl,
'user_group': member.user_group,
'create': false
});
$('#review_members').append(entry)
</script>
% endfor
</ul>
<input type="hidden" name="__end__" value="review_members:sequence">
## end members redering block
%if not c.pull_request.is_closed():
<div id="add_reviewer" class="ac" style="display: none;">
%if c.allowed_to_update:
% if not c.forbid_adding_reviewers:
<div id="add_reviewer_input" class="reviewer_ac">
${h.text('user', class_='ac-input', placeholder=_('Add reviewer or reviewer group'))}
<div id="reviewers_container"></div>
</div>
% endif
<div class="pull-right">
<button id="update_pull_request" class="btn btn-small no-margin">${_('Save Changes')}</button>
</div>
%endif
</div>
%endif
</div>
## TODOs will be listed here
<div class="reviewers-title block-right">
<div class="pr-details-title">
## Only show unresolved, that is only what matters
TODO Comments - ${len(c.unresolved_comments)} / ${(len(c.unresolved_comments) + len(c.resolved_comments))}
% if not c.at_version:
% if c.resolved_comments:
<span class="block-right action_button last-item noselect" onclick="$('.unresolved-todo-text').toggle(); return versionController.toggleElement(this, '.unresolved-todo');" data-toggle-on="Show resolved" data-toggle-off="Hide resolved">Show resolved</span>
% else:
<span class="block-right last-item noselect">Show resolved</span>
% endif
% endif
</div>
</div>
<div class="block-right pr-details-content reviewers">
<table class="todo-table">
<%
def sorter(entry):
user_id = entry.author.user_id
resolved = '1' if entry.resolved else '0'
if user_id == c.rhodecode_user.user_id:
# own comments first
user_id = 0
return '{}_{}_{}'.format(resolved, user_id, str(entry.comment_id).zfill(100))
%>
% if c.at_version:
<tr>
<td class="unresolved-todo-text">${_('unresolved TODOs unavailable in this view')}.</td>
</tr>
% else:
% for todo_comment in sorted(c.unresolved_comments + c.resolved_comments, key=sorter):
<% resolved = todo_comment.resolved %>
% if inline:
<% outdated_at_ver = todo_comment.outdated_at_version(getattr(c, 'at_version_num', None)) %>
% else:
<% outdated_at_ver = todo_comment.older_than_version(getattr(c, 'at_version_num', None)) %>
% endif
<tr ${('class="unresolved-todo" style="display: none"' if resolved else '') |n}>
<td class="td-todo-number">
% if resolved:
<a class="permalink todo-resolved tooltip" title="${_('Resolved by comment #{}').format(todo_comment.resolved.comment_id)}" href="#comment-${todo_comment.comment_id}" onclick="return Rhodecode.comments.scrollToComment($('#comment-${todo_comment.comment_id}'), 0, ${h.json.dumps(outdated_at_ver)})">
<i class="icon-flag-filled"></i> ${todo_comment.comment_id}</a>
% else:
<a class="permalink" href="#comment-${todo_comment.comment_id}" onclick="return Rhodecode.comments.scrollToComment($('#comment-${todo_comment.comment_id}'), 0, ${h.json.dumps(outdated_at_ver)})">
<i class="icon-flag-filled"></i> ${todo_comment.comment_id}</a>
% endif
</td>
<td class="td-todo-gravatar">
${base.gravatar(todo_comment.author.email, 16, user=todo_comment.author, tooltip=True, extra_class=['no-margin'])}
</td>
<td class="todo-comment-text-wrapper">
<div class="todo-comment-text">
<code>${h.chop_at_smart(todo_comment.text, '\n', suffix_if_chopped='...')}</code>
</div>
</td>
</tr>
% endfor
% if len(c.unresolved_comments) == 0:
<tr>
<td class="unresolved-todo-text">${_('No unresolved TODOs')}.</td>
</tr>
% endif
% endif
</table>
</div>
</div>
</div>
<div class="box">
</div>
<div class="box">
% if c.state_progressing:
@ -616,9 +471,11 @@
<%namespace name="cbdiffs" file="/codeblocks/diffs.mako"/>
% if c.at_version:
<% c.inline_cnt = len(c.inline_versions[c.at_version_num]['display']) %>
<% c.inline_comments_flat = c.inline_versions[c.at_version_num]['display'] %>
<% c.comments = c.comment_versions[c.at_version_num]['display'] %>
% else:
<% c.inline_cnt = len(c.inline_versions[c.at_version_num]['until']) %>
<% c.inline_comments_flat = c.inline_versions[c.at_version_num]['until'] %>
<% c.comments = c.comment_versions[c.at_version_num]['until'] %>
% endif
@ -704,7 +561,7 @@
% endif
</div>
<script type="text/javascript">
<script type="text/javascript">
versionController = new VersionController();
versionController.init();
@ -916,6 +773,439 @@
</script>
</div>
### NAVBOG RIGHT
<style>
.right-sidebar {
position: fixed;
top: 0px;
bottom: 0;
right: 0;
background: #fafafa;
z-index: 200;
}
.right-sidebar {
border-left: 1px solid #dbdbdb;
}
.right-sidebar.right-sidebar-expanded {
width: 320px;
overflow: scroll;
}
.right-sidebar.right-sidebar-collapsed {
width: 62px;
padding: 0;
display: block;
overflow: hidden;
}
.sidenav {
float: right;
will-change: min-height;
background: #fafafa;
width: 100%;
}
.sidebar-toggle {
height: 30px;
text-align: center;
margin: 15px 0 0 0;
}
.sidebar-toggle a {
}
.sidebar-content {
margin-left: 5px;
margin-right: 5px;
}
.sidebar-heading {
font-size: 1.2em;
font-weight: 700;
margin-top: 10px;
}
.sidebar-element {
margin-top: 20px;
}
.right-sidebar-collapsed-state {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 0 10px;
cursor: pointer;
font-size: 1.3em;
margin: 0 -10px;
}
.right-sidebar-collapsed-state:hover {
background-color: #dbd9da;
}
.navbar__inner {
height: 100%;
background: #fafafa;
position: relative;
}
</style>
<aside class="right-sidebar right-sidebar-expanded">
<div class="sidenav">
## TOGGLE
<div class="sidebar-toggle" onclick="toggleSidebar(); return false">
<a href="#toggleSidebar">
</a>
</div>
## CONTENT
<div class="sidebar-content">
## RULES SUMMARY/RULES
<div class="sidebar-element clear-both">
<div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="${_('Reviewers')}">
<i class="icon-circle review-status-${c.pull_request_review_status}"></i>
<br/>${len(c.pull_request_reviewers)}
</div>
## REVIEW RULES
<div id="review_rules" style="display: none" class="">
<div class="pr-details-title">
<span class="sidebar-heading">
${_('Reviewer rules')}
</span>
</div>
<div class="pr-reviewer-rules">
## review rules will be appended here, by default reviewers logic
</div>
<input id="review_data" type="hidden" name="review_data" value="">
</div>
## REVIEWERS
<div class="right-sidebar-expanded-state pr-details-title">
<span class="sidebar-heading">
<i class="icon-circle review-status-${c.pull_request_review_status}"></i>
${_('Reviewers')} - ${len(c.pull_request_reviewers)}
</span>
%if c.allowed_to_update:
<span id="open_edit_reviewers" class="block-right action_button last-item">${_('Edit')}</span>
<span id="close_edit_reviewers" class="block-right action_button last-item" style="display: none;">${_('Close')}</span>
%endif
</div>
<div id="reviewers" class="right-sidebar-expanded-state pr-details-content reviewers">
## members redering block
<input type="hidden" name="__start__" value="review_members:sequence">
<ul id="review_members" class="group_members">
% for review_obj, member, reasons, mandatory, status in c.pull_request_reviewers:
<script>
var member = ${h.json.dumps(h.reviewer_as_json(member, reasons=reasons, mandatory=mandatory, user_group=review_obj.rule_user_group_data()))|n};
var status = "${(status[0][1].status if status else 'not_reviewed')}";
var status_lbl = "${h.commit_status_lbl(status[0][1].status if status else 'not_reviewed')}";
var allowed_to_update = ${h.json.dumps(c.allowed_to_update)};
var entry = renderTemplate('reviewMemberEntry', {
'member': member,
'mandatory': member.mandatory,
'reasons': member.reasons,
'allowed_to_update': allowed_to_update,
'review_status': status,
'review_status_label': status_lbl,
'user_group': member.user_group,
'create': false
});
$('#review_members').append(entry)
</script>
% endfor
</ul>
<input type="hidden" name="__end__" value="review_members:sequence">
## end members redering block
%if not c.pull_request.is_closed():
<div id="add_reviewer" class="ac" style="display: none;">
%if c.allowed_to_update:
% if not c.forbid_adding_reviewers:
<div id="add_reviewer_input" class="reviewer_ac">
${h.text('user', class_='ac-input', placeholder=_('Add reviewer or reviewer group'))}
<div id="reviewers_container"></div>
</div>
% endif
<div class="pull-right">
<button id="update_pull_request" class="btn btn-small no-margin">${_('Save Changes')}</button>
</div>
%endif
</div>
%endif
</div>
</div>
## OBSERVERS
<div class="sidebar-element clear-both">
<div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="${_('Observers')}">
<i class="icon-eye"></i>
<br/> 0
</div>
<div class="right-sidebar-expanded-state pr-details-title">
<span class="sidebar-heading">
<i class="icon-eye"></i>
${_('Observers')}
</span>
</div>
<div class="right-sidebar-expanded-state pr-details-content">
No observers - 0
</div>
</div>
## TODOs
<div class="sidebar-element clear-both">
<div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="TODOs">
<i class="icon-flag-filled"></i>
<br/> ${len(c.unresolved_comments)}
</div>
## TODOs will be listed here
<div class="right-sidebar-expanded-state pr-details-title">
## Only show unresolved, that is only what matters
<span class="sidebar-heading">
<i class="icon-flag-filled"></i>
TODOs - ${len(c.unresolved_comments)}
##/ ${(len(c.unresolved_comments) + len(c.resolved_comments))}
</span>
% if not c.at_version:
% if c.resolved_comments:
<span class="block-right action_button last-item noselect" onclick="$('.unresolved-todo-text').toggle(); return versionController.toggleElement(this, '.unresolved-todo');" data-toggle-on="Show resolved" data-toggle-off="Hide resolved">Show resolved</span>
% else:
<span class="block-right last-item noselect">Show resolved</span>
% endif
% endif
</div>
<div class="right-sidebar-expanded-state pr-details-content">
<table class="todo-table">
<%
def sorter(entry):
user_id = entry.author.user_id
resolved = '1' if entry.resolved else '0'
if user_id == c.rhodecode_user.user_id:
# own comments first
user_id = 0
return '{}_{}_{}'.format(resolved, user_id, str(entry.comment_id).zfill(100))
%>
% if c.at_version:
<tr>
<td class="unresolved-todo-text">${_('unresolved TODOs unavailable in this view')}.</td>
</tr>
% else:
% for todo_comment in sorted(c.unresolved_comments + c.resolved_comments, key=sorter):
<% resolved = todo_comment.resolved %>
% if inline:
<% outdated_at_ver = todo_comment.outdated_at_version(getattr(c, 'at_version_num', None)) %>
% else:
<% outdated_at_ver = todo_comment.older_than_version(getattr(c, 'at_version_num', None)) %>
% endif
<tr ${('class="unresolved-todo" style="display: none"' if resolved else '') |n}>
<td class="td-todo-number">
% if resolved:
<a class="permalink todo-resolved tooltip" title="${_('Resolved by comment #{}').format(todo_comment.resolved.comment_id)}" href="#comment-${todo_comment.comment_id}" onclick="return Rhodecode.comments.scrollToComment($('#comment-${todo_comment.comment_id}'), 0, ${h.json.dumps(outdated_at_ver)})">
<i class="icon-flag-filled"></i> ${todo_comment.comment_id}</a>
% else:
<a class="permalink" href="#comment-${todo_comment.comment_id}" onclick="return Rhodecode.comments.scrollToComment($('#comment-${todo_comment.comment_id}'), 0, ${h.json.dumps(outdated_at_ver)})">
<i class="icon-flag-filled"></i> ${todo_comment.comment_id}</a>
% endif
</td>
<td class="td-todo-gravatar">
${base.gravatar(todo_comment.author.email, 16, user=todo_comment.author, tooltip=True, extra_class=['no-margin'])}
</td>
<td class="todo-comment-text-wrapper">
<div class="todo-comment-text">
<code>${h.chop_at_smart(todo_comment.text, '\n', suffix_if_chopped='...')}</code>
</div>
</td>
</tr>
% endfor
% if len(c.unresolved_comments) == 0:
<tr>
<td class="unresolved-todo-text">${_('No unresolved TODOs')}.</td>
</tr>
% endif
% endif
</table>
</div>
</div>
## COMMENTS
<div class="sidebar-element clear-both">
<div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="${_('Comments')}">
<i class="icon-comment" style="color: #949494"></i>
<br/> ${len(c.inline_comments_flat+c.comments)}
</div>
<div class="right-sidebar-expanded-state pr-details-title">
<span class="sidebar-heading">
<i class="icon-comment" style="color: #949494"></i>
${_('Comments')} - ${len(c.inline_comments_flat+c.comments)}
##${_ungettext("{} General", "{} General", len(c.comments)).format(len(c.comments))} /
##${_ungettext("{} Inline", "{} Inline", c.inline_cnt).format(len(c.inline_comments_flat))}
## TODO check why this ins't working
% if pull_request_menu:
<%
outdated_comm_count_ver = pull_request_menu['outdated_comm_count_ver']
%>
% if outdated_comm_count_ver:
<a href="#" onclick="showOutdated(); Rhodecode.comments.nextOutdatedComment(); return false;">
(${_("{} Outdated").format(outdated_comm_count_ver)})
</a>
<a href="#" class="showOutdatedComments" onclick="showOutdated(this); return false;"> | ${_('show outdated')}</a>
<a href="#" class="hideOutdatedComments" style="display: none" onclick="hideOutdated(this); return false;"> | ${_('hide outdated')}</a>
% else:
(${_("{} Outdated").format(outdated_comm_count_ver)})
% endif
% endif
</span>
<span class="block-right action_button last-item noselect" onclick="return versionController.toggleElement(this, '.hidden-comment');" data-toggle-on="Show all" data-toggle-off="Hide all">Show all</span>
</div>
<div class="right-sidebar-expanded-state pr-details-content">
<table class="todo-table">
<%
def sorter(entry):
user_id = entry.author.user_id
return '{}'.format(str(entry.comment_id).zfill(100))
%>
% for comment_obj in reversed(sorted(c.inline_comments_flat + c.comments, key=sorter)):
<%
display = ''
_cls = ''
%>
## SKIP TODOs we display them above
% if comment_obj.is_todo:
<% display = 'none' %>
% endif
## Skip outdated comments
% if comment_obj.outdated:
<% display = 'none' %>
<% _cls = 'hidden-comment' %>
% endif
<tr class="${_cls}" style="display: ${display}">
<td class="td-todo-number">
<a class="permalink" href="#comment-${comment_obj.comment_id}" onclick="return Rhodecode.comments.scrollToComment($('#comment-${comment_obj.comment_id}'), 0, ${comment_obj.outdated_js})">
% if comment_obj.outdated:
<i class="tooltip icon-comment-toggle" title="Outdated"></i>
% elif comment_obj.is_inline:
<i class="tooltip icon-code" title="Inline"></i>
% else:
<i class="tooltip icon-comment" title="General"></i>
% endif
${comment_obj.comment_id}
</a>
</td>
<td class="td-todo-gravatar">
${base.gravatar(comment_obj.author.email, 16, user=comment_obj.author, tooltip=True, extra_class=['no-margin'])}
</td>
<td class="todo-comment-text-wrapper">
<div class="todo-comment-text">
<code>${h.chop_at_smart(comment_obj.text, '\n', suffix_if_chopped='...')}</code>
</div>
</td>
</tr>
% endfor
</table>
</div>
</div>
</div>
</div>
</aside>
<script>
var $sideBar = $('.right-sidebar');
var marginExpanded = {'margin': '0 320px 0 0'};
var marginCollapsed = {'margin': '0 50px 0 0'};
if($sideBar.hasClass('right-sidebar-expanded')) {
$('.outerwrapper').css(marginExpanded);
$('.sidebar-toggle a').html('<i class="icon-right" style="margin-right: -10px"></i><i class="icon-right"></i>');
$('.right-sidebar-collapsed-state').hide();
$('.right-sidebar-expanded-state').show();
updateSticky()
} else {
$('.outerwrapper').css(marginCollapsed);
$('.sidebar-toggle a').html('<i class="icon-left" style="margin-right: -10px"></i><i class="icon-left"></i>');
$('.right-sidebar-collapsed-state').hide();
$('.right-sidebar-expanded-state').show();
updateSticky()
}
var toggleSidebar = function(){
var $sideBar = $('.right-sidebar');
if($sideBar.hasClass('right-sidebar-expanded')) {
// collapse now
$sideBar.removeClass('right-sidebar-expanded')
$sideBar.addClass('right-sidebar-collapsed')
$('.outerwrapper').css(marginCollapsed);
$('.sidebar-toggle a').html('<i class="icon-left" style="margin-right: -10px"></i><i class="icon-left"></i>');
$('.right-sidebar-collapsed-state').show();
$('.right-sidebar-expanded-state').hide();
} else {
// expand now
$('.outerwrapper').css(marginExpanded);
$sideBar.addClass('right-sidebar-expanded')
$sideBar.removeClass('right-sidebar-collapsed')
$('.sidebar-toggle a').html('<i class="icon-right" style="margin-right: -10px"></i><i class="icon-right"></i>');
$('.right-sidebar-collapsed-state').hide();
$('.right-sidebar-expanded-state').show();
}
// update our other sticky header in same context
updateSticky()
}
var sidebarElement = document.getElementById('pr-nav-sticky');
## sidebar = new StickySidebar(sidebarElement, {
## containerSelector: '.main',
## minWidth: 62,
## innerWrapperSelector: '.navbar__inner',
## stickyClass: 'is-sticky',
## });
</script>
</%def>

View file

@ -948,8 +948,8 @@ def assert_inline_comments(pull_request, visible=None, outdated=None):
if visible is not None:
inline_comments = CommentsModel().get_inline_comments(
pull_request.target_repo.repo_id, pull_request=pull_request)
inline_cnt = CommentsModel().get_inline_comments_count(
inline_comments)
inline_cnt = len(CommentsModel().get_inline_comments_as_list(
inline_comments))
assert inline_cnt == visible
if outdated is not None:
outdated_comments = CommentsModel().get_outdated_comments(